Tear down a Namespace or a Server only under the person's approval#4155

open
main
t3/sec04-f5-teardown
opened1h agobyNNick
View on GitHubGitHub import enabledPR last imported 14m ago

Summary

F5 of the 0.4 security program. A Server that enforces Device approval now takes things off only under a signed teardown: Containers of a Namespace, Volumes (RemoveVolume), and a Server leaving or being forgotten. The person approves it with a Device key in the CLI or a passkey in the dashboard.

mermaid
1sequenceDiagram
2 participant P as Person
3 participant C as CLI / dashboard
4 participant Cloud
5 participant S as Each Server
6 C->>C: draft body in ployz-core (cluster, scope, signer, 15 min window)
7 C->>P: every signed line, read back from the body bytes
8 P->>C: yes, then Device key or passkey signs the body
9 C->>Cloud: delete with teardown (kept on the row / Deployment / removal mark)
10 Cloud->>S: Out / Remove / RemoveVolume / Leave / Forget + teardown
11 S->>S: verify signer, own Cluster, own Machine ID, Namespace or Volume named, window
12 S-->>Cloud: admitted, or "This server takes this off only under your approval: …"

Scopes the body can carry (ployz-core/src/authority/teardown.rs):

text
1namespace { namespaces, machines, volumes } # Environment, Project, stray Namespace deletion
2volumes { machines, volumes } # Deploy / Retry with --accept-volume-loss
3server { leaving, forgetting, volumes } # Remove or Forget a Server
4preview_rule # a preview's own pv-<rule>-* Namespace; no Leave/Forget

Where a teardown is checked on the Server (ployzd):

diff
1 ensure_container (Operator)
2- deploy::verify
3+ carries only a teardown → admit_teardown_step # Out, Remove; never Start
4+ else → deploy::verify # unchanged
5 RemoveVolume
6+ admit_operator_volume_removal # Docker Volume and this Machine named
7 Leave / Forget
8+ admit_operator_departure # leaving == self, or self in forgetting

The approval is checked again under the lock. A removal mark (Store migration 0046) keeps the teardown, so the sweep's later admissions carry it until it expires. The verify-removal-marks model is unchanged, because the new mark field is data the sweep passes on and not a new state.

Bets touched (core/DESIGN.md)

  • 6 Machine-local authority. Each Server admits each step against its own facts: its own Machine ID in the scope, its own Cluster key, a Namespace or Docker Volume the body names. No Server acts on another Server's records.
  • 7 Cloud drives, never owns. Cloud stores the teardown on machine_remove_attempt, namespace_cleanup and the Deployment only as relay bookkeeping. A Cloud that rewrites it gets refused by the Servers.
  • 8 Evidence over claims. The person sees every signed field, rendered from the exact signed bytes, and the loss they confirm is the loss signed. Refusals name details.capability.
  • 5 Two-tier identity. The scope names Machines by ID and Volumes by Docker name, never by a display name. Names show only beside the IDs.
  • Stable promise. The new capability is ployz.teardown.v1. All new request fields are optional, and an older daemon ignores them. The dashboard refuses to delete from an enforcing Server that lacks the capability, so it never sends a teardown that would be ignored.

No red flag is crossed.

Residuals (a compromised Cloud)

Cloud can delay, drop or reorder a teardown a person approved, or replay it within its 15-minute window to the Servers it names. It can also show different names beside the IDs in the dashboard. It cannot widen a teardown: a Namespace, Volume or Server outside the signed scope is refused on the Server.

Widened files (beyond f-plan's F5 "Files")

  • ployz-store: migration 0046, deployment/{admit,lifecycle,mod}.rs, removal/{mod,due,mark}.rs, preview_rule.rs. These carry the teardown on Remove, Deploy, Retry and AdmitRemoval, and on the removal mark, for the runner and the sweep.
  • CLI: deploy.rs, env, project, server/clean.rs, shared/confirm.rs. They sign and show the teardown beside the existing question.
  • ployz-config-wasm and the SDK, so the dashboard drafts the body with the same ployz-core code the Servers run.
  • Dashboard deletion paths: Environment and Project teardown, Server Remove and Forget, stray Namespaces, Deploy and Retry that accept volume loss. The machine_remove_attempt.teardown and namespace_cleanup.teardown columns are added in a drizzle migration.

Also fixed (found in the lab, outside F5)

ployzd peer pull kept redis:7-alpine@sha256:… as redis:7-alpine:ployz-sha256-…, which Docker refuses. Every Deploy that moved a tagged image between Servers failed (F 6 × S 5). It now keeps the image under redis:ployz-sha256-….

Limits

  • Under enforcement, Environment shutdown, branch idle-close and anonymous teardown are refused. They have no one to sign.
  • CLI server rm stays a direct path with Full access, signed by the Device that runs it.

Gaps 3, 4 and 5 of f-plan Appendix A, and gap 8 of the e-plan, are settled and recorded there. f-inventory row 47 now reads Signed(Teardown).

Evidence

Live, on a 3-Server lab of this checkout through Cloud (SERVERS=3 DAEMON=checkout). Screenshots and video land under media/f5-* in a later push.

LaneResult
1. Enforcement off, dashboard Environment deleteno prompt, removed, no teardown lines
2. Enforcement on, dashboard Environment delete with a Volumereview shows every signed line; each Server logs teardown approved by=passkey … then admits Out, Remove, and the Volume removal; done in 8 s
4. Teardown for shop-other replayed on shop-stagingremove_volume and ensure_container gone refused; the same teardown on its own Namespace admitted. No teardown: refused. Body rewritten under the passkey's answer: refused (challenge mismatch)
5. Remove of machine-3 captured and replayed on machine-2Leave and Forget of machine-1 refused; all Servers stay up
7. ployzd killed mid-deletionrestarted daemon admits the resent Out and Remove under the same teardown; deletion finishes
3. CLI server rm from the Devicegoes direct as the Device (the stated limit)

Still to run: lane 6 (preview close), lane 8 (docs read against lanes 2 and 3), the perf comparison.

  • Focused nextest on the touched crates: 166/166. Store binaries: 354/354. call_audit passes. Clippy with CI flags is clean.
  • Dashboard: typecheck and lint are clean. Vitest in the touched directories: 75/75. The postgres tests pass for namespace cleanup, machine removal and the CLI handler.

Merge Danger

Door: two-way for code. Store migration 0046 and the drizzle migration only add nullable columns.

Blast Radius: deletion paths on enforcing Clusters

With enforcement off, nothing changes: the dashboard asks for nothing, and a request without a teardown goes as before. With enforcement on, deletions that used to go through now need a passkey or Device signature, and enforcing Servers without ployz.teardown.v1 are refused in the dashboard with an upgrade message.

Checks

16 successful · 6 skipped
All checks passed
4296c4a
Synced from GitHub

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

External image Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 1981939c-47f3-4b02-8913-52b15bebab5a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share