15

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