# ADR-0100: The launch gate has profiles and produces evidence, and the rehearsal is a command
## Metadata
| Field | Value |
| -------------- | ------------------------------------------------------------------ |
| **Status** | Accepted |
| **Date** | 2026-11-02 |
| **Authors** | Alessio Brillo |
| **Deciders** | Alessio Brillo |
| **Supersedes** | — |
| **Relates to** | ADR-0090, ADR-0091, ADR-0094, ADR-0095, ADR-0097, ADR-0099 |
| **Project** | DOMINUS |
## Context
At v1.3.4 the code is complete and the remaining launch blockers are
execution: the infrastructure has never been provisioned, the operator
identity and the fiscal position are human tasks, and the third-party audit
has not started. The launch gate (`npm run launch:check`) reflects the
Cloud's promises and nothing else:
1. It demands `LEGAL_*`, `METRICS_TOKEN` and `BACKUP_DIR` even from a
self-hosted community deployment, which publishes the AGPL and the
self-hosted data flow and has no operator identity to declare. A
community deployment therefore cannot be declared launchable without
lying about who operates it.
2. It prints a verdict and forgets it. The launch checklist asks for
evidence, and a terminal scrollback is not evidence: a release cannot
attach what the gate saw, and a reviewer cannot compare two runs.
3. It judges the environment and, with `--url`, a handful of endpoints. It
never checks the things that break silently in public: the canonical
origin, TLS, HSTS/CSP, the parity between the HTML and the JSON of the
same public page, the sitemap the robots.txt advertises, the live
trademark canary.
4. It cannot be exercised before the infrastructure exists. The Cloud
acceptance suite (`npm run acceptance:cloud`) builds the production
topology, but it explicitly names what it does not prove — the real
providers, the point-in-time recovery, Stripe Tax — and it never runs the
launch gate against the stack it just built.
The result is the failure mode ADR-0090 and ADR-0097 named: a gate that
checks the repository against itself cannot see the world, and a claim that
no command reproduces is a document.
## Decision Drivers
1. **The community edition must have its own launch bar.** Its promises are
different from the Cloud's, and a gate that cannot state them
accurately cannot judge them.
2. **Evidence must be an artifact.** A release should carry the output of
the gate that approved it, not a link to a CI log that expires.
3. **The launch sequence must be executable before the infrastructure
exists.** The first `terraform apply` is not the place to discover a
canonical URL bug or a missing CSP header.
4. **The paid launch is stricter, not different.** `--paid` must turn the
operator's contracts (Stripe, backups, proxy depth, external probe,
audit, alerting) from warnings into failures.
5. **No secrets in evidence.** The gate records "is set", never the value.
## Considered Options
### Option A: keep one Cloud-only gate and document the community case
**Advantages:** no code; the Cloud gate stays exactly as reviewed.
**Disadvantages:** a community deployment is forever "not launchable" by
the only command that says what launchable means, and the operator is
pushed to invent an identity or ignore the gate. Both are worse than a
profile.
### Option B: a second, separate community script
**Advantages:** each script is simple and reads like its edition.
**Disadvantages:** two gates drift. The checks that matter (public surface,
legal footer, canonical origin, trademark) are identical, and a fix applied
to one would have to be remembered in the other. ADR-0097's lesson is that
duplicated truth rots.
### Option C: one gate, two profiles, evidence, and a rehearsal (chosen)
1. **Profiles.** `--cloud` and `--community`, plus auto-detection from the
same rule the server uses (`DATABASE_URL` or a non-`env` auth provider).
`--paid` implies cloud and raises the operator contracts to failures.
2. **Evidence.** `--evidence
` writes
`launch-evidence--.json` and `.md`, on pass and on fail.
The JSON carries the mode, the target, every check, its detail and its
duration; the Markdown is the human-readable table a release attaches.
3. **New checks.** The canonical origin (configured and rendered), TLS and
HSTS/CSP on HTTPS, HTTP→HTTPS, HTML/JSON parity for the same public
page, robots → sitemap, public input validation, data-rights endpoints,
and the live trademark canary (`nike` blocked, `vintagecoffee` clear,
never unverified).
4. **The rehearsal.** `npm run launch:rehearse` builds the production-parity
Cloud stack with the acceptance suite, runs the gate against it with a
complete fixture environment while the stack is up, writes the evidence
and tears the stack down. With `--url` it judges a real deployment and
starts nothing. PITR is opt-in (`--with-pitr --pitr-base … --pitr-wal …
--pitr-expect-rows :`), because it needs a real base
backup, a WAL archive and a row count worth comparing; a missing
prerequisite is a SKIP with the command to run, never a pass.
5. **The paid bar.** `--paid` fails on: a missing Stripe configuration for
any reason, a missing backup directory or zero retention, an unset
`TRUST_PROXY_DEPTH`, a placeholder Alertmanager webhook, a missing
external probe, and a missing third-party audit on file.
**Advantages:** one gate, two honest profiles; the evidence is the release
artifact; the rehearsal can run today, without credentials or spend, and
the same command judges staging tomorrow.
**Disadvantages:** the gate grows and must itself be tested; the rehearsal
is minutes, not seconds, so it is a separate CI job rather than part of
`npm run gate`; a live canary makes it network-dependent (deliberately:
the trademark source is the one dependency the product cannot do without).
### Option D: skip the rehearsal and provision staging first
**Advantages:** the fastest path to a real URL.
**Disadvantages:** the first deploy carries every unknown at once, and
ADRs 0095 and 0099 both record that the defects which matter are the ones
only the real path finds. A rehearsal narrows the first staging run to the
things that genuinely need the infrastructure.
## Decision
**Option C.**
The gate keeps a single implementation, with:
`--cloud` (default when the environment says so), `--community`,
`--paid`, `--url`, `--evidence`, `--json`, `--skip-trademark` (debug only,
recorded in the evidence).
The rehearsal is `npm run launch:rehearse`. It is not part of `npm run gate`:
`gate` is seconds-to-minutes and must stay fast enough to run on every push.
The rehearsal runs as its own CI job, like `acceptance:cloud`.
## Consequences
### Positive
- A community deployment can be declared launchable by the same command
that judges the Cloud, with the checks that actually apply to it.
- Every release can attach the evidence the gate produced, and two runs can
be compared line by line.
- The canonical origin, TLS, CSP, HTML/JSON parity and the trademark canary
stop being "looked at once by a human" and become checks.
- The first staging deployment starts with a stack that has already run the
gate, so the remaining unknowns are the infrastructure's, not the code's.
- The `--paid` profile is the checklist's "before the paid tiers" made
executable.
### Negative
- The gate is larger and needs its own tests.
- The rehearsal adds a CI job measured in minutes, and its trademark canary
depends on a live third-party endpoint.
- Evidence files contain the deployment URL and the names of failing
checks; they must be reviewed before publication, like any release
artifact.
### Neutral
- No product feature changes. The scoring engine, the pipeline and the
Cloud surface are untouched.
- The community edition still publishes the AGPL and the self-hosted data
flow; the gate simply stops pretending it must publish an operator
identity.
## Links
- [ADR-0090](0090-legal-surface-and-launch-gate.md) — the launch state is a
command, not a document.
- [ADR-0091](0091-verified-published-artifact.md) — the artifact is verified,
not assumed.
- [ADR-0094](0094-launch-posture-single-replica-external-probe.md) — the
external probe and the paid posture.
- [ADR-0095](0095-cloud-acceptance-suite.md) — the acceptance suite and its
stated boundaries.
- [ADR-0097](0097-cli-front-door-and-release-state.md) — the release state
and the front door.
- [ADR-0099](0099-release-verified-against-the-world.md) — the release
verified against the world.