Deployment Workflows
Wavemap separates validation, application delivery, operator work, documentation publication, and infrastructure mutation. The separation is a safety property: each lane answers a different question and receives only the authority needed to answer it.
| Lane | Trigger | Question | Mutation Boundary |
|---|---|---|---|
| Continuous integration | Pull request, develop push, or manual dispatch. | Is this source revision healthy? | Repository and disposable test services only. |
| Application-only delivery | Manual dispatch with an exact merged SHA. | Can these immutable app images safely replace the Development runtime? | App images, modeled runtime deploy, release receipt, and bounded rollback. |
| Deployed-dev operator | Manual dispatch with one reviewed profile. | Which explicit Development operation or recovery proof should run? | Profile-dependent; destructive and lifecycle actions remain visibly selected. |
| Docs publication | Manual dispatch with a selected ref. | Can the curated static site publish and smoke? | Docs bucket and CDN invalidation only. |
| Infrastructure change | Guarded local operator command. | Is a reviewed resource-graph transition safe to apply? | Exact saved full-stack plan only. |
No push or CI-completion event dispatches either application or docs CD. A green merged-state CI run makes an exact commit eligible for manual application delivery; it does not deploy that commit.
Continuous Integration Admission
Section titled “Continuous Integration Admission”.github/workflows/ci.yml runs on pull requests targeting develop or main, pushes to develop, and manual dispatch.
Pull-request CI tests the proposed merge. Push CI records the health of the exact merged develop SHA that downstream
application delivery must find before it can obtain package or cloud authority.
The workflow keeps separate jobs for workflow contracts, change classification, tooling/code, docs, source i18n, rendered pseudolocalization, ordinary hermetic tests, database-backed tests, browser smoke, Docker builds, and pull-request base freshness. Repository-owned classification skips only the expensive docs, rendered-pseudolocalization, and Docker lanes when the changed paths cannot affect them; uncertain paths receive full assurance.
See Testing Runtime And CI for the exact job graph, local commands, and source trail.
Manual Application-Only Delivery
Section titled “Manual Application-Only Delivery”.github/workflows/deploy-application-dev.yml has one trigger: workflow_dispatch. The operator supplies a full
lowercase 40-character Git SHA and confirms that the run is application-only.
Source Admission Before Delivery Authority
Section titled “Source Admission Before Delivery Authority”The first eligibility job validates the full SHA, proves it is contained by develop, and requires successful push-event ci.yml for that exact merged SHA before checking out source or obtaining package/cloud credentials. PR checks and cancelled merged-state runs do not satisfy that gate.
A second job uses the separate dev-application-admission environment and metadata-only OIDC role. It reads the current sanitized deployment contract, canonical release receipt and exact SERVER_VERSION parameter. The receipt must identify the modeled target, repositories, activation posture and full-SHA version. Missing, inconsistent, unavailable or divergent baselines require reviewed operator bootstrap; admission never falls back to the candidate’s last commit.
The typed classifier examines the complete deployed-to-candidate Git range. Migrations, schema/database configuration, public-reader policy and privileged maintenance changes require operator delivery, including when a later app-only commit follows them. After acquiring host ownership, delivery rereads the contract/receipt/version and checks the admitted baseline hash before mutation. A concurrent baseline change therefore requires fresh admission.
Private npm Supply Chain owns the package token’s exact CI and Docker boundary; it grants no cloud or deployment authority.
Delivery Boundary
Section titled “Delivery Boundary”After admission, the dev-application-cd environment grants the dedicated delivery role. The workflow:
- Acquires host mutation ownership and rechecks the admitted baseline.
- Reads the three exact restricted-public frontend build parameters, masking their values.
- Builds or reuses backend, frontend and, when modeled, public API images tagged
sha-<full-git-sha>, then resolves every selected image to a registry digest. - Sends the complete immutable bundle to the modeled runtime document. The host writes the selected
SERVER_VERSIONbefore rendering environments, waits for container health, and checks running image/version identity. - Runs endpoint and non-destructive browser-routing smoke, then records the verified release on the host and publishes the same receipt to the canonical history/current objects.
- On a post-deploy failure, redeploys the preserved previous receipt, republishes that restored receipt and reruns endpoint smoke. Rollback restores the old version before environment rendering.
- Reopens and releases ownership only after a recorded successful release or a verified rollback. Cancellation, failed verification or an uncertain command retains ownership for operator recovery.
The delivery role can publish only to the modeled application repositories, read the sanitized contract and release metadata, read the three frontend build parameters and exact version parameter, write release receipts, invoke the modeled document on the modeled instance, and invoke the exact private lifecycle maintenance alias. It cannot read reader credentials, write SSM parameters, run arbitrary shell commands, perform database maintenance, mutate media, update Pulumi or directly control EC2. The host owns the exact SERVER_VERSION write.
Application rollback changes images and their release identity. It does not reverse schema, infrastructure, configuration, reader grants or credential rotation. A release with database-affecting changes belongs to the reviewed operator lane.
Whole-Operation Host Ownership
Section titled “Whole-Operation Host Ownership”Both workflows use wavemap-dev-host-mutation with cancellation disabled. A persistent owner token on the host spans deploy, migration/reset, smoke, recording and rollback; a Linux flock serializes each host command and prevents release while a command is active. This protects repository entrypoints across jobs and manual calls; it does not constrain an administrator’s arbitrary root or AWS access.
Manual operations use deploy dev runtime ownership and retain the same WAVEMAP_DEPLOY_MUTATION_TOKEN through the entire operation. Tokens start with a letter or digit and contain 16–128 letters, digits, underscores or hyphens. These commands default to a local plan; add --execute only for the separately approved live operation:
export WAVEMAP_DEPLOY_MUTATION_TOKEN="reviewed-operation-unique-id"pnpm wavemap -- deploy dev runtime ownership --action acquire --pulumi-outputs-json /private/path/deployment-contract.json# Execute the approved acquire, then the approved deploy/maintenance/record or rollback commands with this same token.pnpm wavemap -- deploy dev runtime ownership --action release --pulumi-outputs-json /private/path/deployment-contract.jsonAcquisition first closes automatic wake/stop in the private lifecycle coordinator, resumes the host through that same private capability, then acquires host ownership and removes Caddy’s persistent open marker. The API receives SIGTERM and drains its existing pool before database maintenance. A deployment may start the candidate reader behind the closed proxy; migrate/reset stops it again before the retained-role preflight. When the API is enabled, database commands require --public-reader-role or WAVEMAP_PUBLIC_READER_ROLE; the operator workflow reads that non-secret role name from its protected environment variable. Regrant and --check run while readers remain stopped. Ordinary application delivery gains no database administrator authority.
Release verifies frontend and backend readiness, starts the retained public service when present, performs a real rich Event read and zero-row private-data denial checks using its reader credentials, and verifies that no table-level mutation grants are present. It then reopens the cloud lifecycle, repeats host verification and publishes the open marker under the original host lock. A crash between cloud reopening and marker publication can leave automatic wake enabled while public reads remain closed; recovery restores cloud closure before proceeding. These two stores are not one transaction. Already-fresh CloudFront responses may remain visible for their existing maximum 60 seconds; urgent removal requires separately approved invalidation.
There is no lease timeout or automatic takeover. After interruption, confirm that the old workflow and all SSM commands have ended, inspect running images/version and release receipts, then use --action status and --action recover with the original token. Recovery resumes only that owner or finishes acquisition that never reached the host; it cannot replace another owner. --action retry-transition explicitly repeats only a recorded uncertain start/stop, without deleting its intent; --action reconcile-dns publishes the running host’s freshly observed address. These actions retain the same dry-plan/--execute boundary. Do not delete the owner, lock, lifecycle record or admission marker to bypass recovery.
The operator workflow completes its selected database, browser and media checks before verified release. Wake and cold-start smoke follow release, because testing public wake during maintenance would test an intentional refusal. Refresh the sanitized contract after provisioning the private lifecycle handle. Establish a compatible public-image baseline containing the maintenance verifier before activation; an older rollback image without it remains closed and needs operator recovery.
Canonical Receipts And Recovery
Section titled “Canonical Receipts And Recovery”The recorder writes /opt/wavemap/deployment/last-successful-runtime-release.json only after the modeled host action validates the running release. The wrapper then copies that identical metadata to application-releases/aws-dev/history/<full-sha>.json and application-releases/aws-dev/current.json in the deployment-contract store. Both workflows and manual rollback use this owner. Recording and rollback require confirmed completion; live --no-wait is refused.
Receipt validation and its inferred type live in infra/operations/src/deployed-dev/runtime-release-receipt.ts, shared by admission, rollback and host recording. The modeled SSM document embeds that same portable TypeScript source and runs it through the existing backend container’s Node 24 runtime. This does not require Python or Node installed on the host, nor a newly packaged command in an older rollback image. The validator receives explicit metadata inputs and emits validated JSON; Bash writes a private temporary sibling and renames it to the fixed host receipt only after successful validation. Failed validation or interrupted output preserves the previous receipt. The container has no mount of the host receipt directory.
Host and S3 writes are not one transaction. A failed or uncertain copy can leave the host/history ahead of current, or report failure after current was already updated. Preserve the selected previous receipt before deployment. Under the same ownership, either retry recording the verified running release or explicitly roll back to that preserved receipt and republish it. Verify host/current/version agreement before releasing a manual recovery. Do not fabricate a receipt or edit only SERVER_VERSION to bypass admission. History keys are per source SHA, with store object versioning retaining repeated publications.
Bootstrap And Permission Prerequisites
Section titled “Bootstrap And Permission Prerequisites”The source model is not evidence that live grants exist. Before activating these workflows:
- Apply the separately reviewed IaC transition: modeled mutation/record document, public repository where selected, exact repository publish/pull policies, admission role and host-only
PutParameterfor/wavemap/dev/runtime/backend/SERVER_VERSION. - Configure the protected
dev-application-admissionenvironment withAWS_ADMISSION_ROLE_ARN,DEPLOYMENT_CONTRACT_STORE_BUCKETand the private package read token. Its role trusts only the repository/environment OIDC subject and reads only the two current metadata objects plus the version parameter. - Review the externally managed operator role’s trust and exact grants: selected ECR repositories, modeled document/instance and command readback, contract reads, and
s3:PutObjectfor the canonical current/history release keys. Its separate database-maintenance authority is not inherited by application delivery. - Ensure the host has Linux
flockand Docker Compose supportingup --wait, and the backend image retains the pinned Node 24.15.0 runtime or a verified compatible successor for embedded TypeScript execution. Complete a reviewed operator deployment and publish a verified compatible baseline before ordinary application delivery can be admitted.
Public capability bootstrap and the remaining activation gates are documented in Public API Runtime.
Reviewed Deployed-Dev Operations
Section titled “Reviewed Deployed-Dev Operations”.github/workflows/deploy-dev.yml is also manual-only. It accepts a reviewed ref and one deterministic run_profile, then pins every downstream checkout to the SHA resolved by preflight. It does not accept arbitrary combinations of stage switches.
| Profile | Intended use | Important boundary |
|---|---|---|
preflight | Repository and deployment-contract checks. | No cloud authentication or mutation. |
preflight-docker | Preflight plus local image-build proof. | Slow; no push or cloud mutation. |
cloud-plan | OIDC, live metadata, and deploy dry runs. | Cloud-aware but non-mutating. |
deploy-endpoint | Migration-aware app/API operator deploy. | May apply pending migrations; no reset. |
deploy-endpoint-recovery | Endpoint deploy plus wake and browser-routing recovery. | Non-destructive data posture. |
deploy-seeded-browser | Known-data and browser proof. | Includes destructive database reset. |
deploy-media | Media API, storage, CDN, browser, and discrepancy proof. | Includes reset and temporary media mutation. |
deploy-lifecycle | Stop, cold-start page, wake, and browser recovery. | Includes reset and deliberately stops the host. |
deploy-full-validation | Deliberate combination of all expensive proofs. | Broadest and most disruptive recipe. |
Use Runbooks for selection and evidence expectations. The database remains disposable at this project stage, but destructive behavior is still explicit so a human knows what was destroyed and why.
The reviewed operator role used by deploy-dev.yml must separately receive ssm:GetParameters for the same three exact
frontend-build parameter ARNs before a live frontend image job can use this lane. That external role permission remains a
human-controlled activation step rather than an inferred repository mutation.
Responsibility And Source Flow
Section titled “Responsibility And Source Flow”The normal code path is intentionally layered:
human selects exact source and operation -> GitHub workflow owns trigger, environment, permissions, and job order -> public wavemap CLI owns stable command names -> shell wrapper owns live process control and --execute gates -> @wavemap/operations owns typed plans, validation, and summaries -> Pulumi-owned cloud/runtime contracts constrain provider mutation| Owner | Read here first | Owns |
|---|---|---|
| Workflow orchestration | .github/workflows/deploy-application-dev.yml, deploy-dev.yml | Admission, environment, permissions, concurrency, DAG, summaries. |
| Typed GitHub/cloud handoff | .github/actions/prepare-application-delivery-cloud-job, prepare-cloud-deploy-context | Allowlisted environment inputs and credential setup. |
| Public commands | packages/cli/src/wavemap-cli | Stable operator routes and help. |
| Live execution | bin/dev-deploy | Provider CLIs, waits, dry-run/--execute gates, GitHub outputs. |
| Typed deployment logic | infra/operations/src/deployed-dev | Profiles, eligibility, plans, bundles, receipts, validation. |
| Cloud/runtime contract | infra/pulumi/src/providers/aws | Roles, repositories, SSM documents, host, storage, control plane. |
| Regression contracts | packages/cli/src/wavemap-cli/__tests__, infra/operations/src/**/__tests__ | Trigger, ordering, profile, validation, and permission invariants. |
When behavior changes, edit the lowest owning layer and keep the workflow declarative. Do not duplicate business logic in YAML or make docs the only place where a safety rule exists.
Deployment Contract And Secret Boundary
Section titled “Deployment Contract And Secret Boundary”Application and docs workflows read wavemap.deployment-contract version 1 from the private deployment-contract store.
That projection contains deploy-safe facts, not raw Pulumi state or decrypted runtime secrets.
The contract also carries a separate frontendBuildConfiguration projection containing only the prefix, exact parameter
paths, and restricted-public kinds. It never carries provider values. Frontend image jobs fetch those values directly
from SSM after AWS authentication, mask them, and keep them out of workflow summaries and artifacts.
GitHub Actions -> reads deployment targets, parameter references, image identity, and deploy metadata -> sends a modeled SSM command that references parameter pathsruntime host -> reads /wavemap/dev/runtime/* directly from SSM Parameter Store -> renders private environment files and Compose locally -> pulls the selected images and starts containersRendered environment files and decrypted SecureString values must never enter workflow logs, summaries, artifacts, or
the deployment contract. See Configuration And Secrets for value
ownership and Infrastructure Change Policy
for the complete store/actor map.
Manual Docs Publication
Section titled “Manual Docs Publication”.github/workflows/deploy-docs.yml supports only workflow_dispatch. It checks out the selected ref, obtains the docs
role through the dev environment, reads the sanitized contract, builds apps/wavemap-docs/dist, publishes that static
directory, invalidates CloudFront, and performs read-only public smoke.
It does not access the application host, API, database, media store, wake path, app release receipt, runtime secrets, or Pulumi state. Working notes are not public input unless their conclusions are deliberately promoted into curated pages. See Docs Hosting for source ownership and permission details.
Infrastructure Is Not A Delivery Stage
Section titled “Infrastructure Is Not A Delivery Stage”Application and docs workflows never run pulumi up. Wavemap infrastructure changes use
bin/infra/pulumi-stack.sh, a clean exact source checkout, and a refresh-enabled saved full-stack plan. Update authority
requires the exact reviewed plan SHA-256 and source Git SHA. Tendril owns the backend foundation; Wavemap owns only its
/wavemap product state and workload graph.
See Guarded Pulumi Operations And Recovery before any preview, update, or state recovery.
Smoke Lanes
Section titled “Smoke Lanes”| Lane | Default posture | What it proves |
|---|---|---|
| Endpoint | Application delivery and operator deploy. | Public entrypoint, backend health/readiness, i18n manifest. |
| Browser routing | Application delivery and recovery recipes. | Non-destructive public rendering and unauthenticated routing. |
| Database status/migrate | Reviewed operator path. | Live Drizzle ledger compatibility and bounded forward migration. |
| Database reset/seeded/browser | Destructive selected recipes. | Known disposable data and seeded journeys. |
| Media/browser-media/discrepancy | Media recipe. | Real object storage/CDN behavior and DB/object consistency. |
| Wake/cold-start | Recovery or lifecycle recipe. | Cost-control stop/wake behavior and browser recovery. |
| Docs | Every intentional docs publication. | Public static routes and assets without waking the app. |
Higher-cost smoke belongs in the default gate only after it is stable, bounded, diagnosable, and worth its runner and operator cost.
Command And Documentation Homes
Section titled “Command And Documentation Homes”Use pnpm wavemap -- ... for the public command surface and pnpm wm -- ... as the local shortcut. Root scripts remain
compatibility entry points for workflows and older notes; the authoritative arguments and mutation posture live in the
Wavemap CLI Command Reference.
| Change | Authoritative home |
|---|---|
| Public command or flag | CLI Command Reference and packages/cli/src/wavemap-cli. |
| Workflow order, trust, or handoff | This page and .github/workflows. |
| Manual procedure or recovery | Runbooks and the owning wrapper. |
| IAM or resource lifecycle | Infrastructure Change Policy and Pulumi source. |
| Environment cost/data posture | Deployed Dev and environment configuration. |
| Static publication | Docs Hosting and docs deploy source. |