Export, Retire, and Delete Completely
End the customer lifecycle with a verifiable export and an ordered deletion across every owning plane.
System map · Day 15
Whole-system design
Five stable layers. Today's work is expanded and linked; the rest stays in context.
Product and authority
Covered — People and product entry points · Identity and policy
HelixWorks control plane
Source-backed today
Enforces export, retire, and delete as ordered owner-only lifecycle transitions.
Generated application plane
Design target · not proved
Stopping app-owned sessions and domain processing is required before erasure, but the current generated HTML has no app data plane.
Delivery and desired state
Covered — Git desired state · CI and immutable artifacts · Argo CD reconciliation
Cloud and orchestration
Covered — Terraform and AWS APIs · Accounts, VPC, DNS, and private paths · Kubernetes or EKS control plane
Compute and traffic
Covered — Worker compute · Platform service workloads · Generated app workloadsAhead — Ambient mesh data plane
Storage and evidence
Ahead — Infrastructure state · Cluster desired and live state
Product data and artifacts
Design target · not proved
The provider seam exports and tombstones control/runtime records; production app-data export and erasure remain unimplemented.
Evidence and observability
Source-backed today
Produces a reviewable deletion plan and terminal evidence without relying on an unsafe broad recursive command.
The enterprise problem and today’s slice
Enterprise problem: A customer can stop using a HelixWorks application while artifacts, grants, runtime secrets, backups, connector authority, and generated-app records continue to exist, creating cost, privacy, and security risk. Whole-course context: The incoming evidence is a locally released project with provider collaboration and audit records; today closes the lifecycle that is implemented. Today’s slice: We export provider project metadata, retire and tombstone its hosted-runtime row, require retirement before project deletion, and preserve audit evidence; connector revoke, artifact erasure, backups, and generated-app data remain future owner jobs. End-of-day evidence: The domain test proves wrong-order denial, tenant-scoped project absence, runtime retirement, and retained project.deleted evidence; a production erasure receipt does not yet exist. Still unsolved: Multi-owner erasure and the internal design principles and infrastructure implementation used to reproduce it are taught next.
Customer outcome and implementation focus
The customer outcome is a reviewable export, retire, and delete completely change, not a collection of requirements. This day starts with the implementation boundary, then uses the command or manifest below to produce positive, denied, and recovery evidence.
Components in focus
Export coordinator, deletion worker, policy evaluator, and audit service; worker pods; application database, export object storage, tombstone/evidence store; cache: not involved because lifecycle transitions are durable.
This map names the implementation boundary for this day. The service or controller changes only the state it owns; runtime and audit evidence let the operator distinguish a declared change from an effective one.
Export then retire data through a guarded deletion workflow
Execute deletion as a guarded state machine
Broad recursive deletion is unsafe because an empty or wrong identifier can expand the blast radius. The current implementation enforces a smaller state machine: owner-only export, retire before delete, tenant-scoped runtime tombstone, and retained deletion evidence. It does not yet implement a signed export manifest, dry-run plan, per-owner erasure jobs, or AWS deletion. Read the exact lifecycle in services/control_plane/domain.py.
def retire(self, project: Project, actor: str) -> None:
self._owner(project, actor)
project.status = "retired"
self.repository.save(project)
self.runtime.delete(project.organization_id, project.project_id)
self._audit(project, actor, "project.retired")
def delete(self, project: Project, actor: str) -> None:
self._owner(project, actor)
if project.status != "retired":
raise ValueError("retire project before deletion")
Key takeaways
Retirement is a multi-owner lifecycle with prerequisites, idempotency, and falsifiable completion evidence.
- Export must be usable and accepted before destructive steps begin.
- Revoke authority and freeze writes before erasing data.
- Retention exceptions need an owner, basis, expiry, and blocked restore policy.
Checklist
An application is retired only when active access and unjustified retained data are both gone.
- [ ] Export manifest includes schema, counts, checksum, exclusions, and expiry
- [ ] Target resolution rejects empty, ambiguous, or wrong-owner identifiers
- [ ] Every plane acknowledges its own deletion or justified retention exception
- [ ] Post-delete denial and unaffected positive control are recorded