Public: anyone can read this; the crew can change it.

Platform app move gap: Custom domain for an app, with certificates and a one-step revert #11712

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/11700-platform-app-move-gap-custom-domain-for → main 3 commits fork 2 threads · 2 unresolved

  • Merged. scout/11700-platform-app-move-gap-custom-domain-for was merged into main as 1a9a4cf.

Review

Sign in to review this change.
3 files changed +47 −3
docs/README.md +1
@@ -34,6 +34,7 @@ platform for organizations.
3434 - [Operating](OPERATING.md): configuration, health, releases, switches and limits.
3535 - [Metrics](METRICS.md): the Prometheus endpoint.
3636 - [Failure domains](DOMAINS.md): the ways the forge stops and what it reports.
37 - [An app's own domain](features/app-own-domain.md): cut-over, certificate, and the way back.
3738 - [Snapshots](SNAPSHOT.md): nightly copies and what they hold.
3839 - [Recovery](RECOVERY.md): a printable guide for a forge in trouble.
3940 - [Restore](RESTORE.md): bring a forge back on a fresh machine.
docs/features/app-own-domain.md added +43
@@ -0,0 +1,43 @@
1 # An app's own domain: cut-over and the way back
2
3 The step of a move that can strand a
4 shop (`platform-app-move.md`). The forge serves a verified name
5 and its proxy fetches the certificate; it never writes DNS, so the revert is
6 the owner's records, put back by hand in one edit. In order:
7
8 1. **Write the old records down first.** Every record at the name (A, AAAA,
9 CNAME, ALIAS, and the TTL of each) as the old host serves them today,
10 in the migration issue. This list is the way back; the forge cannot read
11 it for you after the cut.
12 2. **Add the name** (Settings → App → Domains, `app_domains` `add`, or
13 `POST …/app/domains`). The answer lists the TXT proof and the CNAME or
14 ALIAS to publish. Publish the **TXT only**; the old A/CNAME stays.
15 3. **Check** (`check`). The sentence says verified, or what is missing. The
16 old site still answers: nothing has moved. `GET /__forge/domains/ask?domain=<host>`
17 answering 200 is what lets the proxy issue the certificate on the first
18 request (docs/APPS.md, _Your own domain_).
19 4. **Cut:** replace the old A/CNAME with the CNAME/ALIAS the forge gave. The
20 first request to the new name pays for issuing the certificate (a second
21 or two); request it once yourself, over https, before telling anyone.
22 5. **Revert, one step:** put the step-1 records back. Traffic returns to the old
23 host as each resolver's cache of the new record expires, so keep the TTL
24 low (`platform-app-move.md`, §6) until the shop is settled.
25 The forge keeps answering at the name until the domain is removed
26 (`remove`); removing it is only tidiness once DNS no longer points here,
27 and the old host's certificate was never touched.
28
29 Left out, on purpose: the forge does not edit DNS at a registrar (#11717),
30 show the certificate's expiry (#11718), or store the old records for you
31 (#11719). This closes #11700 as a runbook over a feature that already ships,
32 not as new code.
33
34 ## What keeps these claims true
35
36 All in `test/app-domains.test.ts`:
37 - the ask endpoint answers 200 only for a verified host (also with case and a
38 trailing dot) and 404 for an unverified, unknown or missing name: "verified, the forge serves the
39 app at the host and every URL follows";
40 - before verification the forge answers for itself and the ask says 404:
41 "before verification the forge answers for itself at the host";
42 - removal makes the host unknown again and the ask 404: "removal makes the
43 host unknown again".
docs/features/platform-app-move.md +3 −3
@@ -43,16 +43,16 @@ counts. The old copy stays until that has passed.
4343 ## 6. Cut-over (person, quiet hour, the owner says "go")
4444 1. Lower the DNS TTL a day ahead.
4545 2. Put the old app read-only; take a last dump; load; compare counts.
46 3. Point the domain at the forge; switch payment webhooks and live keys.
46 3. Point the domain at the forge (`app-own-domain.md` has the steps, the
outdated

Repoint to the new features doc once the section moves; this line is also very long, split it.

47 certificate and the revert); switch payment webhooks and live keys.
4748 4. Place one live order.
48 5. **Way back:** the old DNS records, written down before step 3; restoring
49 5. **Way back:** the old DNS records, written down before step 3 (`app-own-domain.md`); restoring
4950 them is the one step. The old app stays up read-only for a week.
5051
5152 ## What the forge lacks (each is an issue)
5253 - A database import for an app (`migrate` has no data path): upload a dump, load it, report counts and checksums. (#11697)
5354 - A Postgres app has no home here: no managed Postgres, only per-app `bun:sqlite`. (#11698)
5455 - A file and upload copy with a manifest compared on both ends. (#11699)
55 - A custom domain for an app with certificate issuance, and a one-step revert of it. (#11700)
5656 - Per-app encrypted backup with a restore proof (`docs/features/backup-truth.md` covers the forge's own, not an app's). (#11701)
5757 - A rehearsal mode: a preview seeded from a copy of production data, with payment sandbox keys only. (#11702)
5858 - Inbound payment webhooks that survive a preview's changing address. (#11703)

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs