Skip to content

feat(cloudflare): save a D1 restore point before remote migrations and add void db restore - #440

Merged
Brooooooklyn merged 8 commits into
voidzero-dev:mainfrom
bayraak:d1-restore-point-before-migrations
Oct 17, 2026
Merged

Brooooooklyn merged 8 commits into
voidzero-dev:mainfrom
bayraak:d1-restore-point-before-migrations

Conversation

@bayraak

@bayraak bayraak commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Problem

void deploy --platform cloudflare applies pending D1 migrations to the production database before it uploads the new Worker version. A Worker rollback puts the old code back, but it does not undo the migration. void project rollback already says so: "Rolling back will NOT revert these migrations." Nothing gave the operator a way back for the data.

D1 Time Travel can restore a database to any minute in the last 30 days (7 on Workers Free). An exact restore point for "just before this deploy's migrations" has to be taken at that moment, because writes keep landing while the deploy runs.

What changes for users

Deploying with pending D1 migrations

◇ Applying D1 migrations...
● Saved a D1 restore point before applying 2 migrations.
  To undo these migrations, restore this point (discards every later write):
    void db restore --bookmark=00000085-0000024c-00004ef2-0a83ea2f…
Case Result
No D1, or nothing pending Unchanged: no restore point, no new output
Pending migrations Restore point saved, then the apply runs
The pending list or the restore point cannot be read Deploy stops before migrating and shows Cloudflare's reason. The database is unchanged
Anything fails after the migrations were applied: the apply, the check after it, the artifact check, the upload, the readiness check or the activation The final error repeats the void db restore line, after saying to fix the error and deploy again first
Another deploy applied the migrations before the restore point Only the migrations still pending after the restore point are named; none left means no restore line
Void Cloud, PostgreSQL and MySQL deploys Unchanged

Recovering

void project rollback          # if the new code is live
void db restore --bookmark=<b> # or --timestamp 2026-10-16T09:30:00Z
# fix or remove the migration, then deploy again

After a one-off void deploy --platform cloudflare from a project saved on Void, or with deployment disabled, add --platform cloudflare to each command. void project rollback gains that flag, and every follow-up command Void prints carries it.

void db restore:

  • restores the D1 database of a direct Cloudflare deploy, including from a fresh checkout or CI where .void/ is absent;
  • asks first (No by default) and needs --yes without a terminal;
  • accepts --timestamp only with Z or an offset, and shows the exact UTC time it will restore;
  • prints void db restore --bookmark=<previous> to undo the restore;
  • lists the migration files the database no longer has applied, which the next deploy applies;
  • explains, when Cloudflare rejects a restore, that restore points expire after 7 days (Workers Free) or 30 days (Paid) and that older migrations are undone with a new migration. The direct Cloudflare rollback prompt and the docs say the same.

It refuses PostgreSQL, MySQL and Void platform projects.

Implementation

  • Typed operations d1.time-travel.bookmark (cf d1 time-travel get-bookmark) and d1.time-travel.restore (cf d1 time-travel restore … --force; Void asks first) run through the existing pinned cf transport. No new token permission: deploy already needs D1 Edit.
  • Bookmarks must match Cloudflare's hex segments before they are printed or passed on, and restore targets reach cf as one argument each.
  • Cloudflare reasons go through cloudflareCommandDiagnostic, which strips stdout and redacts credentials.
  • CLI help, the void db interactive list, docs/reference/cli.md, the database reference, the deployment guide, the Cloudflare and project rollback docs, and the rollback prompt point to the new command.

Tests

  • deploy-migrations-restore-point.test.ts covers:
    • the call order;
    • each refusal before the apply, with Cloudflare's reason;
    • the uploader check after the restore point;
    • the restore line in every failure after the apply.
  • cloudflare-operation-process-d1.test.ts checks, against a stubbed cf:
    • the bookmark and restore argv;
    • the decoded results;
    • malformed bookmarks;
    • a failed restore.
  • db-args, db-migrate, cloudflare-operations and cli-help tests cover:
    • parsing;
    • the prompt;
    • the fresh-checkout fallback;
    • refusals;
    • the help page.
  • cloudflare-deploy.test.ts asserts the pre-apply list target again. It also keeps the "no recognizable marker" test reaching the check after the apply.
  • Mutation spot checks fail the tests: dropping the uploader check, --force, the regex anchors, the restore line, the prompt default, or the UTC normalisation.

Locally:

  • vp lint and the package typechecks pass.
  • The affected and full suites pass, except browser tests: Playwright's Chromium could not be downloaded on this network.

🤖 Generated with Claude Code

@Brooooooklyn
Brooooooklyn force-pushed the d1-restore-point-before-migrations branch from 53f2468 to 3f96ae6 Compare October 16, 2026 09:15
@Brooooooklyn Brooooooklyn changed the title feat(cloudflare): take a D1 Time Travel bookmark before applying remote migrations feat(cloudflare): save a D1 restore point before remote migrations and add void db restore Oct 16, 2026
Brooooooklyn
Brooooooklyn previously approved these changes Oct 16, 2026
@Brooooooklyn

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-17T03:42:56.194637Z caa2a75 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9509cd25df

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/void/src/cli/db.ts
Comment thread packages/void/src/cli/db-args.ts Outdated
@Brooooooklyn

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 27f1a46b62

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/void/src/cli/db.ts Outdated
Comment thread docs/reference/cli/database.md Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3d27796843

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/void/src/cli/deploy-migrations.ts
Comment thread packages/void/src/cli/project-args.ts Outdated
@bayraak
bayraak force-pushed the d1-restore-point-before-migrations branch from 753b8d1 to 2c0d493 Compare October 16, 2026 20:09
@bayraak
bayraak requested a review from Brooooooklyn October 17, 2026 00:03
@Brooooooklyn
Brooooooklyn force-pushed the d1-restore-point-before-migrations branch from 2c0d493 to 4168de6 Compare October 17, 2026 03:19
bayraak and others added 7 commits October 17, 2026 11:20
…te migrations

A Worker rollback does not undo a migration. Before applying pending D1 migrations, deploy now takes a Time Travel bookmark through cf, prints the cf d1 time-travel restore command for it, and stops if the bookmark cannot be taken.
…migrations

Deploy now prints `void db restore --bookmark=<b>` for the restore point
it saves before migrating, instead of a raw `npx cf` command. The new
command restores the D1 database of a direct Cloudflare deploy to that
bookmark or to a time with a zone. It asks first (No by default), needs
--yes without a terminal, prints the bookmark that undoes the restore,
and lists the migration files the database no longer has applied. It
also works from a fresh checkout whose Cloudflare config names the D1
database.

Deploy refusals now include Cloudflare's redacted reason, and every
failure after the restore point repeats the restore command. Bookmarks
are checked against Cloudflare's hex format before they are printed or
passed to cf, and restore targets reach cf as single arguments.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Restore points last 7 days on Workers Free and 30 days on Workers Paid.
The direct Cloudflare rollback hint, the restore failure and the docs
now say so, and point to a new migration for older changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…r dates

A one-off `void deploy --platform cloudflare` keeps the saved platform,
so `void db restore` refused right after it. Restore now accepts
`--platform cloudflare`, and deploy prints the flag when the saved
platform is not Cloudflare.

`new Date` turns 2026-02-30 into March 2 and 24:00 into the next day.
`--timestamp` now compares the fields as written, so it never restores
to a moment the user did not type.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ollow-ups

After a one-off `void deploy --platform cloudflare` from a project saved
on Void or with deployment disabled, the recovery commands Void printed
did not reach Cloudflare: `void project rollback` followed the saved
platform and had no way to name Cloudflare, and the restore's undo and
redeploy instructions dropped the flag.

`void project rollback` now accepts `--platform cloudflare`. The
rollback and restore follow-up commands carry the flag whenever
Cloudflare is not the saved platform, and the restore no longer points
to `void db status`, which reads only a saved Cloudflare target.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Two overlapping deploys could both see a migration pending. If the
other deploy applied it before this one took its bookmark, this deploy
still printed that bookmark as the way to undo the migration, though
restoring it would leave the migration in place.

Deploy now reads the pending set again after it takes the bookmark and
names only the migrations still pending then. When none remain, it
prints no restore command. It refuses to migrate if that second read
fails.

The interactive `void project` list also offers `--platform cloudflare`
as a rollback flag, instead of reading it as part of the deployment ID.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ter migrating

Deploy printed the restore command once, before the build and upload
output. If the artifact check, the upload, the readiness check or the
activation failed after the migrations were applied, the final error
did not name it, although the live version now ran against the new
schema.

Each of those failures now ends with the restore command, and says to
fix the error and deploy again first: restoring discards every later
write. The command is printed before the process exits, and once even
when publishing throws.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Brooooooklyn
Brooooooklyn force-pushed the d1-restore-point-before-migrations branch from 4168de6 to e7eea9c Compare October 17, 2026 03:20
Brooooooklyn
Brooooooklyn previously approved these changes Oct 17, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e7eea9c8d8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/void/src/cli/db-args.ts
`new Date` keeps only milliseconds, so `--timestamp
2026-10-16T09:30:00.123456Z` restored to .123, an earlier instant than
the one written, which could cross a write. `void db restore` now
accepts at most three fractional digits and says so in its error.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Brooooooklyn
Brooooooklyn merged commit a38b1f8 into voidzero-dev:main Oct 17, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants