Repository navigation
feat(api): port server-side rollback (#553) onto the ADR 0040 wire - #895
Open
elliotBraem wants to merge 1 commit into
Open
elliotBraem wants to merge 1 commit into
elliotBraem wants to merge 1 commit into
Conversation
The console rollback config-half (fly-hosting ticket 06), re-implemented from 87d37e4 on feat/reconciler-v1 against the post-cutover registry wire rather than merged (slot→remote, digest cutover, wire cutover all postdate it): - everything-dev gains two user-facing subpaths: ./deploy/rollback (verifyRollbackSnapshot pinned-remote SRI gate + buildRollbackPayload rolledBackFrom stamp — the CLI's own machinery, unchanged) and ./auth/near-signer (submitRegistryWrite + signing strategies). - api/src/lib/rollback-service.ts: fetch the target snapshot from the runtimeConfig history (public FastKV reads through registryClientShape), compare against live, verify every pinned remote, stamp rolledBackFrom, then one atomic __fastdata_kv write carrying the config + a DeployRecordSchema audit record (deploys/<ts>.json — the rollback shows up as a /deploys row). Keys derive through keyPathOf; the pre-cutover audit-manifest read is gone (the record's registryUrl derives locally). - Refuse-not-half-execute: unknown block height, identical-to-live, SRI failure (per-remote checks returned), pre-cutover snapshot (deliberately unrestorable per ADR 0040 §4 — no server-side force, the CLI keeps --force), wrong namespace, unconfigured, verification read failure. - Own namespace only: hosting.rollback { account, gateway } variables + optional ROLLBACK_PRIVATE_KEY secret; absent → the route refuses with status: refused, not an error. Registry-wired ownership is ticket 09. - POST /hosting/rollback behind session/api-key auth, Effect-native handler; /deploys own-namespace rows gain a rollback button (deploys-rollback-<i>) with ConfirmDialog, refusal/success toasts, list invalidation; the rolledBackFrom badge moves to deploys-rolled-back-<i>. - api (+ template) tsconfig gains the DOM lib (near-signer's near-cli source needs JsonWebKey); api gains near-kit (catalog). - Tests: service 11/11 (every refusal path incl. verification-read failure, stamping, atomic-write shape, signing failure, unreachable-live tolerance), route 2/2 (401 + not-configured); browser spec extended with the own-namespace outcome leg plus the owner-gated publish-v2→rollback→rolledBackFrom leg (traces). The instance half (restart on the rolled-back pin + drift marker) is ticket 08's reconciler work, not here.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fly-hosting ticket 06 — port the console rollback config-half from
87d37e45onfeat/reconciler-v1(#553).What was ported vs re-implemented
Re-implemented (forced by the wire cutovers): the old
rollback-service.tsread history through the pre-cutover helpers (historyUrlForKey,auditManifestKeyPrefix,parseAuditManifest,registryNamespaceForAccount) that no longer exist. The service now derives every key throughkeyPathOf(runtimeConfig+deployRecord), reads throughregistryClientShapefromeverything-dev/registry, and stamps the audit trail as aDeployRecordSchemarecord atdeploys/<ts>.json— so a rollback appears as a/deploysrow with therolledBackFrombadge (the old branch'smanifests/audit-manifest payload would have been invisible on the new wire). The per-check vocabulary isremoteeverywhere (ADR 0040 §5), notslot.Ported as-was (decision structure and refusals): unconfigured → refuse; wrong namespace → refuse (own-namespace-only; registry-wired ownership is ticket 09); unknown block height → refuse; identical-to-live → refuse (unreachable live tolerated — the snapshot publish is idempotent); SRI failure → refuse with per-remote checks; pre-cutover snapshot → refuse as deliberately unrestorable (ADR 0040 §4, wording updated; no server-side force — the CLI keeps
--force); signing failure → refuse with the cause. Refusals are results (status: "refused"), not thrown errors.Reused unchanged (shared machinery, new exports only):
verifyRollbackSnapshot/buildRollbackPayloadfromeverything-dev/deploy/rollbackandsubmitRegistryWrite/SigningStrategyfromeverything-dev/auth/near-signeralready existed inpackages/everything-dev— this PR only adds the subpath exports (+ tsdown entries) so the api consumes exactly what the CLI's rollback flow uses. No verification is re-implemented in the api shell. The atomic write carries the config + deploy record in one__fastdata_kvcall.Also carried over:
hosting.rollback { account, gateway }variables + optionalROLLBACK_PRIVATE_KEYsecret,POST /hosting/rollbackbehind session/api-key auth (Effect-native handler, tagged reads), the/deploysrollback button (deploys-rollback-<i>) + ConfirmDialog + toasts + list invalidation, DOM lib for api (+.github/templates/api— near-signer's near-cli source needsJsonWebKey),near-kitin api deps (catalog).Changed from the source branch: the changeset is
patchoneverything-devonly — the old one named api/ui (minor), which the current release rules reject (private workspaces). The rolledBackFrom badge test id moved todeploys-rolled-back-<i>so the ticket'sdeploys-rollback-<i>belongs to the button (the badge took that name in #892).Signing recommendation
ROLLBACK_PRIVATE_KEYshould be replaced by custody publish (publish.auth = "custody", OutLayer) as the default path, but not in this PR:submitRegistryWriteaccepts a strategy, and the custody path in the deploy train composesmode: "custody"writes with an idempotency key — wiring rollback through OutLayer's/wallet/v1/callis a contained change tobuildFastKvRollbackDepsplus a config leg.Test evidence
pnpm --filter api test: 7 files / 57 tests green, including newrollback-service.test.ts(11: every refusal path incl. verification-read failure, stamping, atomic-write shape via key assertions, signing failure, unreachable-live tolerance) androllback-route.test.ts(2: 401, not-configured) — in-memory pglite via the committed.env.test.pnpm run typecheckgreen (all 13 workspaces);pnpm run lintgreen (no errors; pre-existing warnings untouched).tests/regression/browser/specs/deploys.spec.ts: a CI-runnable own-namespace leg (button → ConfirmDialog → outcome toast, trace) and the fullpublish v2 → roll back → config pointer shows rolledBackFromleg, gated behindROLLBACK_SPEC_ACCOUNT/ROLLBACK_SPEC_GATEWAY(needs a disposable namespace + the stack configured withhosting.rollback+ROLLBACK_PRIVATE_KEY); not booted locally — owner/CI step per the ticket. Both legs end with a Playwright trace artifact.Post-merge cleanup for the owner (recorded in the ticket):
origin/feat/reconciler-v1, localrb-553, andfeat/rollback-config-halfcan be deleted once this merges.