> ## Documentation Index
> Fetch the complete documentation index at: https://docs.veristar.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Handover readiness checklist

> Every item that changes hands so the new operator can run the service on its own, with the evidence for each.

Every item that has to change hands for the new operator to run the service on its own. Tick an
item only with the evidence named next to it. The procedures are in [plan.md](https://docs.veristar.app/handover/plan.md) and
[rebuild.md](https://docs.veristar.app/handover/rebuild.md); the order of sections follows the cutover.

Who signs: the new operator for each item, SCF for section 9.

## 1. Before the shadow period

* [ ] SCF has named the community operator and the handover start date (plan section 2.1).
* [ ] The new operator has its own GitHub owner (user or organisation), cloud project, DNS
  account, probe account, alert receiver, and age identities for at least two people.
* [ ] The new operator's people have read [../architecture.md](https://docs.veristar.app/architecture.md),
  [../deploy/](https://docs.veristar.app/deploy/overview.md), [../runbooks/](https://docs.veristar.app/runbooks/overview.md),
  [../sla.md](https://docs.veristar.app/sla.md), [../support.md](https://docs.veristar.app/support.md) and this directory.
* [ ] The new operator's age public keys are recipients of **staging** only
  (`infra/secrets/recipients/staging.txt`, `sync-recipients.sh`).

## 2. Independent rebuild (shadow period)

* [ ] A new environment built from the repository alone in the new operator's accounts, following
  [../deploy/overview.md](https://docs.veristar.app/deploy/overview.md); every gap in the docs found on the way is fixed
  in the repository. Evidence: the issue or pull request per gap.
* [ ] It runs the same release as production (`SA_IMAGE_TAG`).
* [ ] `make tofu-check`, `make ansible-lint`, the second `site.yml` run reports `changed=0`.
* [ ] The end-to-end checks of [../deploy/verification.md](https://docs.veristar.app/deploy/verification.md) pass.
* [ ] The runbooks that need no production access were run on it: edge node loss, database
  failover, point-in-time restore, Umami upgrade.
* [ ] Its backups run: `pgbackrest info` lists a full backup, and the backup alerts are quiet.
* [ ] Its probe and status page measure it; one monthly SLO report exists.

## 3. Domain

* [ ] The registrar account that holds `<input I4>` is known, and so is its transfer lock state
  (many registrars refuse a transfer within 60 days of a registration or a previous transfer,
  and the ICANN 60-day lock after a registrant change applies unless it was waived).
* [ ] The new DNS zone exists in the new account (`make <env>-apply`, `infra/tofu/modules/dns`)
  and `cutover-transfer.yml -e cutover_stage=precheck` passes against its nameservers.
* [ ] The auth code is issued, the transfer is approved by both sides, and the registrar shows the
  new owner.
* [ ] The delegation points at the new nameservers and `cutover-transfer.yml
      -e cutover_stage=verify` passes: public resolvers answer with the new nodes, every pinned
  tracker release is served byte for byte with a valid certificate, console, web dashboards
  and OTLP answer.
* [ ] An example page that still carries a snippet from before the cutover delivers a pageview in
  Chromium, Firefox and WebKit (the `@cutover` check of `tests/handover`, pointed at the real
  names, or by hand with the browsers' network tools).
* [ ] Not used: the redirect path. If the domain cannot be transferred, every tenant was told to
  replace its snippet and OTLP endpoint before `cutover-redirect.yml` ran
  ([redirect-research.md](redirect-research.md)).

## 4. Data

* [ ] Ingestion was drained and the source stopped (rebuild.md section 2).
* [ ] The export ran by the pgBackRest path (or the pg\_dump path for another provider), and the
  directory's `SHA256SUMS` was checked on import.
* [ ] `make migrate-import` passed: schema versions equal, row-level security coverage check,
  `jobs run reconcile`.
* [ ] `platformctl umami rotate-admin` and `jobs run umami-rotate --force` ran on the new leader.
* [ ] `make verify-migration` printed `result: source and target are equal`. Evidence: its output.
* [ ] Two tenants confirmed they see their history in the new console and web dashboards.
* [ ] The dump directories were deleted after verification.

## 5. Secrets and credentials

* [ ] The original operator's age keys are removed from every
  `infra/secrets/recipients/<env>.txt`, `sync-recipients.sh` ran, and the change is merged.
* [ ] Every value they could read is rotated: `infra/secrets/rotate-secrets.py`, then
  `rotate-all.yml` with `rotate_previous_age_identity` when that identity is available. Its
  last play passed, which proves every previous PostgreSQL, NATS, NATS route, Umami admin,
  Umami user, Patroni REST and Grafana credential is refused and every new one accepted.
* [ ] Owner inputs replaced at their providers and the old ones revoked there: GitHub OAuth client
  secrets (I2; `rotate-all.yml` asks GitHub), Cloudflare token (I6), Azure role assignments (I5),
  Discord webhooks (I8), the OpenTofu state passphrase and the
  provider tokens in `tofu.enc.env`.
* [ ] `secrets.previous.enc.yaml` deleted after `rotate-all.yml` passed.
* [ ] Operator API keys the original operator created are revoked
  ([../runbooks/operator-key-revocation.md](https://docs.veristar.app/runbooks/operator-key-revocation.md)).
* [ ] The original operator's SSH keys are removed from the `ops` user of every server (input
  I15, the inventory's admin keys), and their source addresses from the admin CIDRs (I13).

## 6. Repository and packages

* [ ] The repository is transferred to the new owner on GitHub; issues, pull requests, releases
  and the old URL redirect are checked after the move.
* [ ] Set up again under the new owner, because a transfer does not move them: Actions secrets
  and variables, environments and their protection rules, branch protection, Dependabot
  settings and alerts, the container registry path (images under `ghcr.io/<owner>/...`), the
  npm trusted publisher (input I9) bound to the new repository.
* [ ] The npm scope is transferred and one tracker release is published from the new repository
  ([../runbooks/tracker-release.md](https://docs.veristar.app/runbooks/tracker-release.md)).
* [ ] Release images are built and signed from the new repository; `deploy.yml` verifies the new
  signing identity.

## 7. Accounts and access

* [ ] Cloud, DNS, probe and registrar accounts of the original operator hold nothing the new
  environment needs, and the original operator has no access to the new ones.
* [ ] GitHub OAuth apps of every environment belong to the new owner (input I2), with the
  callback URLs of the new environment and wildcard redirect matching off.
* [ ] `OPERATOR_GITHUB_IDS` (input I3) lists exactly the people SCF names; the control plane was
  restarted after the change ([../operators/overview.md](https://docs.veristar.app/operators/overview.md)).
* [ ] Alerts reach the new operator's receiver (I8); a test alert was delivered before the old
  receiver was removed.

## 8. Tenants

* [ ] Every project was told the cutover date, the new processor and that nothing changes in its
  snippet or exporter (plan section 2.3, step 1), with the notice period the legal review set
  (input I10).
* [ ] The public issue tracker and [../support.md](https://docs.veristar.app/support.md) name the new operator.

## 9. Done

* [ ] SCF confirms the new operator runs the system on its own, and the closing issue on the
  repository links the evidence above (plan section 7).
* [ ] The old environment's teardown date is announced; its backups expire with their retention.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.