Documentation
¶
Overview ¶
D1.6b: --list-backups one-shot CLI.
The Maintenance Agent's GET /v1/backups endpoint shells out to the cli container (via `docker compose --profile cli run --rm cli --list-backups --backup-dir /backup`) and parses the JSON array this command emits to stdout. Operators can also invoke it manually for debugging — same output format either way.
Encryption detection uses the D1.4 magic prefix `CVRTBK01` at byte 0 (see backup_encrypt.go: backupEncMagic). We peek 8 bytes; entries whose first 8 bytes match are flagged encrypted=true. Anything else is reported as encrypted=false; we do NOT attempt deeper validation (gzip-magic, tar header, etc.) because the agent only needs an operator-facing "encrypted-or-not" hint here, and partial reads are cheap.
Symlinks, directories, and unreadable entries are skipped silently rather than failing the whole listing — the human (or agent) can act on whatever IS readable. Errors at the directory level (e.g. non-existent --backup-dir) DO fail the command.
Culvert — Enterprise-grade open source HTTP/HTTPS proxy https://github.com/KidCarmi/Claude-Test
Release Management API — P1.6d-0 (foundation; NO GUI in this slice).
Read-only catalog/current endpoints plus the async dispatch + resume control endpoints, all backed by the P1.6c DispatchService. The handlers own only HTTP concerns (auth, decode, the async 202 split, and a bounded per-agent status store); planning/execution/verify-by-digest/audit/alert all live in the service. Agent endpoints are resolved by an injected resolver (NOT a config route — config is a later slice), so this slice adds no /api/releases/config, no GUI panel, no auto-update, no scheduling.
Dispatch is asynchronous: the handler starts DispatchService.Dispatch in the background and returns 202 the instant the op_id + resume context are durably recorded (the service's onApplied observer). A planner/preflight refusal that happens BEFORE apply returns a synchronous 4xx/503 instead. The watch then runs to terminal in the background and updates the status store, which the GET status endpoint surfaces.
Release Catalog Runtime — Slice 1 (Catalog Load + Resolve).
A pure, in-process, UNSIGNED, local-source, read-only release catalog. It parses the release index + referenced manifests, validates them fail-closed, verifies each manifest's content hash against its RAW bytes, and builds the forward (channel → release) and reverse (pinned ref → release) indexes the rest of the release layer depends on.
Scope (roadmap/D1.6d-P1.2-release-catalog-runtime-plan.md): Catalog Load + Resolve only. NO signatures, NO network/refresh, NO GUI, NO agent calls, NO upgrade dispatch, NO rollback-candidate computation, NO CP→DP propagation.
Control-Plane-only: nothing here is wired into the proxy/data-plane path by this slice; it returns a DIGEST (`repo@sha256:…`, the agent's native currency) and has no release/channel awareness on the agent side.
Integrity ≠ authenticity: the manifest_sha256 check (§4.9) is a content hash, NOT a signature. It catches corruption/drift but is worthless against a tampering attacker, and the INDEX itself has no integrity protection until a later slice signs it. Do not treat a loaded catalog as authenticated.
Release Catalog Distribution — P1.5 Slice d (air-gap BundleCatalogProvider).
BundleCatalogProvider is a TRANSPORT that stages the CATALOG portion of a signed offline release bundle (a tar / tar.gz archive carried into an air-gapped site) into a fresh temp dir, which the caller then hands to LoadVerifiedCatalog (the P1.3 trust boundary) exactly like any other source. Verification is identical to online — only the transport differs (plan §8).
Unlike the HTTP provider, the bundle is COMPLETE before any parse, so no two-phase gate is needed (plan §5.1: local/bundle providers stage-then-verify unchanged). The provider therefore does NO trust work at all — it only moves bytes, under a hostile-archive discipline:
- any absolute / traversal ("..") / backslash / NUL entry name rejects the whole bundle (never silently skipped);
- any symlink or hardlink entry rejects the whole bundle;
- catalog entries must be regular files within per-file and total bounds;
- non-catalog entries (the future image blobs, plan §8) are skipped WITHOUT reading their content;
- index.json is required; index.json.sig is staged when present — the enforce/permissive decision for a missing signature belongs to the P1.3 mode at verify time, not to the transport.
Scope (roadmap/D1.6d-P1.5-catalog-distribution-plan.md — Slice d): catalog extraction + staging + cleanup only. NO image docker-load (the bundle slice that loads images MUST bind loaded-image-digest == authenticated list_digest, P1.3 §7 — recorded there, not here), NO outer bundle signature, NO GUI/API, NO dispatch, NO agent changes, NO HTTP changes.
Release Catalog Distribution — P1.5 Slice a (CatalogHolder + atomic publish).
The CatalogHolder owns the live, atomically-swappable verified *Catalog that the rest of the Control Plane reads. It is the swap component P1.2 deferred ("reload = construct a new *Catalog; the refresh slice will own swap semantics; not here"): this slice loads + verifies from a single local directory, publishes atomically, exposes an explicit no-catalog state, and a manual reload that keeps the current catalog on any failure.
Scope (roadmap/D1.6d-P1.5-catalog-distribution-plan.md — Slice a): holder + atomic publish + local-dir reload only. NO goroutine, NO HTTP provider, NO cache persistence, NO staleness, NO GUI/API, NO agent/dispatch/air-gap. The only path to a published catalog is LoadVerifiedCatalog (the P1.3 trust boundary) — there is deliberately no "publish raw *Catalog" entry point.
Release Catalog Distribution — P1.5 Slice c (HTTP CatalogProvider).
HTTPCatalogProvider is a TRANSPORT that stages a catalog candidate (index.json + index.json.sig + referenced manifests) from an HTTP(S) origin into a fresh temp dir, which the caller then hands to LoadVerifiedCatalog (the P1.3 trust boundary) exactly like a local-dir source.
The §5.1 contract is the heart of this slice: because manifests cannot be fetched without first reading their refs out of the index, the provider runs a TWO-PHASE verify — it verifies the index signature over the RAW index bytes BEFORE parsing the index to enumerate manifest fetches. A forged/unsigned index (in enforce mode) therefore triggers ZERO manifest requests. The final LoadVerifiedCatalog over the staged dir re-verifies everything (defense in depth, incl. the manifest_sha256 hash check that authenticates each fetched manifest).
Scope (roadmap/D1.6d-P1.5-catalog-distribution-plan.md — Slice c): the HTTP transport + two-phase verify + staging + cleanup + timeout + minimal retry/backoff + conditional (ETag/If-Modified-Since) fetch. NO GUI, NO dispatch, NO agent changes, NO air-gap bundle, NO CP→DP propagation. The provider is transport-only apart from the §5.1 index-verify gate.
Release Catalog Distribution — P1.5 Slice b (Refresher + last-good cache + staleness).
The Refresher orchestrates stage → verify → publish on top of the P1.5a CatalogHolder, adds a last-good on-disk cache (restart durability), single- flight, refresh metadata, and staleness. It still uses a LOCAL directory as the source — the HTTP provider and air-gap bundle are later slices.
Scope (roadmap/D1.6d-P1.5-catalog-distribution-plan.md — Slice b): refresher + cache + staleness + single-flight + an OPTIONAL (default-off) interval ticker. NO HTTP provider, NO air-gap bundle, NO GUI/API, NO agent/dispatch, NO metrics/alert wiring. Every publish still goes through LoadVerifiedCatalog (the P1.3 trust boundary); the cache is re-verified on load.
Release Catalog Runtime — Slice 1: query surface (Resolve / Lookup / Current / List). All methods are pure and I/O-free after LoadCatalog.
Release Catalog Authenticity — P1.3 Slice 1 (index signature verification).
Adds a Control-Plane-side authenticity gate on TOP of the P1.2 catalog runtime (release_catalog.go): an ed25519 detached signature over the RAW index.json bytes, verified against a baked/operator TrustStore BEFORE the index is parsed or any manifest_sha256 entry is trusted (plan §5/§5.0). Signing the index transitively authenticates every manifest (the index binds each by sha256), so one signature over one document is the whole authenticity kernel.
Scope (roadmap/D1.6d-P1.3-catalog-authenticity-plan.md — Slice 1): TrustStore + envelope parse + LoadVerifiedCatalog + the three enforcement modes. NO GUI, NO agent/dispatch, NO air-gap bundle, NO network/refresh, NO CP→DP propagation, NO metrics wiring. The verifier is release-agnostic and verifies fully offline.
Release Dispatch — P1.6 Slice a (CP-side dispatch kernel, PURE PLANNING).
The Dispatcher turns an operator target (release_id or channel) into a DispatchPlan: it resolves the release from a PINNED immutable catalog snapshot (P1.5 holder), reconciles the catalog repo against the deployment's proxy_repo (repo equality / one explicit air-gap rewrite, design §4), derives Current/already-current from the agent's running_image.repo_digests (P1.1, design §3/§5), and builds the EXISTING upgrades.apply request object (design §6) — WITHOUT sending it.
It is a PURE, deterministic planner: no I/O, no randomness, no agent contact. The idempotency key is an INPUT (higher orchestration owns op identity); the catalog snapshot is read exactly once at plan start. The agent receives only an image_ref + existing apply flags — no release/channel/version/catalog data crosses to it, and it stays release-agnostic.
Scope (roadmap/D1.6d-P1.6-release-dispatch-plan.md — Slice a): planning + the request object + tests. NO agent POST, NO upgrades.check, NO tags, NO tag updater, NO new agent endpoint, NO agent changes, NO GUI, NO auto-update, NO rollback-candidate computation, NO legacy-updater changes.
Release Dispatch — P1.6 Slice b (CP-side execution wrapper).
DispatchExecutor takes a FROZEN DispatchPlan (P1.6a), generates the CP idempotency key, POSTs the EXISTING upgrades.apply, polls the EXISTING agent op to a terminal state, re-reads running_image.repo_digests, and classifies a terminal DispatchTerminal by VERIFYING THE RUNNING DIGEST itself (never the agent's self-report). It is single-flight per agent and emits audit events via a hook.
The agent is untouched and stays release-agnostic: only image_ref + existing apply flags cross the wire (no upgrades.check, no tags, no fallback). The transport is behind the AgentClient seam so the orchestration is fully testable with a fake; httpAgentClient is the concrete adapter over the existing /v1 endpoints.
Scope (roadmap/D1.6d-P1.6-release-dispatch-plan.md — Slice b): execution + verify-by-digest + terminal classification + audit structs/hooks. NO GUI, NO auto-update, NO rollback-candidate computation, NO legacy-updater change, NO new agent endpoint.
Release Dispatch — P1.6 Slice c (CP-side orchestration wiring).
DispatchService is the CP-side wrapper that ties the PURE planner (P1.6a) to the execution wrapper (P1.6b/c-0) and the rest of the Control Plane: the audit ring (release.dispatch + release.dispatch.outcome), the alert webhook hook, and the real HTTP transport to the EXISTING agent /v1 surface.
It owns an AGENT-KEYED single-flight registry: exactly one DispatchExecutor per agent identity, so the executor's per-instance single-flight becomes a per-AGENT guarantee regardless of how many Dispatch calls arrive. A second dispatch to an agent already mid-op is rejected (errDispatchInFlight) and audited, never queued or duplicated.
The agent stays release-agnostic: only image_ref + existing apply flags cross the wire (no upgrades.check, no tags, no fallback). Verify-by-digest remains the only success gate, and the CP idempotency key is generated ONCE per dispatch op and threaded through the plan so the executor honors it (P1.6c-0).
Scope (roadmap/D1.6d-P1.6-release-dispatch-plan.md — Slice c): service wrapper, agent registry, Resume/re-poll, audit + alert wiring, real transport. NO GUI, NO agent change, NO new agent endpoint, NO legacy-updater work, NO auto-update, NO rollback-candidate logic.
Release Management startup wiring (P1.6d-0.1).
Constructs and publishes the Release Management backend (catalog provider + DispatchService + releaseManager) so the /api/releases* routes are actually usable instead of reporting "not configured". It is deliberately MINIMAL and NON-FATAL: any failure leaves globalReleaseMgr nil and the routes report a clear 503 — never a panic.
Scope: empty catalog holder (a later refresh slice populates it), the default proxy_repo, an optional empty repo_rewrite, and the single CP-LOCAL maintenance agent (key "local"), reached over its unix socket by default or an http(s) URL via CULVERT_MAINT_AGENT_URL. NO mutable config route, NO GUI.
Source Files
¶
- admin_settings.go
- alerts.go
- auth.go
- auth_idp.go
- auth_ldap.go
- auth_oidc.go
- auth_oidc_flow.go
- auth_saml.go
- auth_startup.go
- auth_startup_config.go
- authpolicy.go
- backup.go
- backup_encrypt.go
- bandwidth.go
- blocklist_feed.go
- blocklist_startup.go
- blocklist_startup_config.go
- blockpage.go
- bootstrap.go
- ca.go
- ca_metrics.go
- catdb.go
- categorygroup.go
- cdr.go
- cdr_breaker.go
- cdr_client_keyatrest.go
- cdr_health.go
- cdr_metrics.go
- cdr_pool.go
- cdr_proxy.go
- cdr_ui.go
- cdrpolicy.go
- cdrstore.go
- clam.go
- cleanup.go
- client_class.go
- cluster_ca_keyatrest.go
- cluster_metrics.go
- config.go
- configversion.go
- connlimit.go
- connlimit_startup.go
- connlimit_startup_config.go
- controlplane.go
- diagnostics.go
- dp_node_keyatrest.go
- enrollment.go
- events.go
- feedsync.go
- fileblock.go
- fileblock_startup.go
- fileblock_startup_config.go
- filemagic.go
- fileprofile.go
- geoip.go
- geoip_startup.go
- geoip_startup_config.go
- ha.go
- hashcache.go
- identity.go
- inspection_rules.go
- inspection_rules_config.go
- kek.go
- keyatrest_diagnostics.go
- legacy_auth_providers_startup.go
- legacy_auth_providers_startup_config.go
- list_backups.go
- lockout.go
- logger.go
- logguard.go
- logstore.go
- main.go
- metrics.go
- metrics_token_startup.go
- metrics_token_startup_config.go
- mtls_ocsp_startup.go
- mtls_ocsp_startup_config.go
- nodegroup.go
- observability_startup.go
- observability_startup_config.go
- ocsp.go
- otlp.go
- otlp_traces.go
- pac.go
- pac_startup.go
- pac_startup_config.go
- plugin.go
- policy.go
- proxy.go
- release_api.go
- release_catalog.go
- release_catalog_bundle.go
- release_catalog_holder.go
- release_catalog_http.go
- release_catalog_refresher.go
- release_catalog_resolve.go
- release_catalog_verify.go
- release_dispatch.go
- release_dispatch_exec.go
- release_dispatch_service.go
- release_wiring.go
- restore.go
- rewrite.go
- rewrite_default_action_startup.go
- rewrite_default_action_startup_config.go
- runtime_shutdown.go
- saas_feed.go
- scan_remote.go
- scan_svc.go
- scanner.go
- security.go
- security_scan.go
- session.go
- session_startup.go
- session_startup_config.go
- socks5.go
- store.go
- syslog.go
- threatfeed.go
- tls.go
- totp.go
- ui.go
- ui_access_policy_startup.go
- ui_access_policy_startup_config.go
- ui_auth.go
- ui_authpolicy.go
- ui_cluster.go
- ui_config.go
- ui_extras_startup.go
- ui_extras_startup_config.go
- ui_governance.go
- ui_helpers.go
- ui_metadata_divergence.go
- ui_metadata_enforcement.go
- ui_middleware.go
- ui_policy.go
- ui_rbac.go
- ui_routes_meta.go
- ui_security.go
- ui_session.go
- ui_static.go
- update.go
- update_cluster.go
- upstream.go
- upstream_transport.go
- urlcat_metrics.go
- yara_scan.go