Repository navigation
Native: offline backup-hold release and read-only inspect for restores (#973) - #974
Conversation
A restore releases the restored backup hold while the cell is still offline, before any Server process starts. PHP does this with a one-off `artisan external-payloads:backup-hold release --owner=<uuid>`. Native only had #953's admin endpoints, so the release failed closed. `durable-workflow-server backup-hold acquire|renew|release|status [--owner <uuid>] [--ttl <seconds>]` is PHP's command, offline: - It reads only the DB settings and starts no listener or workers. - It opens only an initialized native database. SQLite is inspected read-only first; PostgreSQL and MySQL pass their read-only schema check before the writable pool opens. A missing, empty or foreign database is refused with native_schema_not_initialized and left as it was. - It runs the same store methods as the endpoints (idempotent, recorded owner, database clock) and prints the same v1 report. On refusal it prints the reason or validation message and exits non-zero. MySQL's schema check now refuses an unmarked database as native_schema_not_initialized, as PostgreSQL's does, rather than failing on the missing marker table as storage_unavailable. Tests run on SQLite, PostgreSQL and MySQL: - the binary releases a restored lapsed hold with no Server running; - only the recorded owner can release, and a second release is idempotent; - PHP's validation messages and argument refusals; - acquire, renew and release by the next backup; - a database with no native schema is refused and left without one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Cloud consumer check against cloud#591's import contract, at head
If #974 rides in 3.0.2, Cloud's flip can pin 3.0.2 directly; otherwise it pins the following patch. Either way, Cloud wires the offline call into #591's import path after the release publishes and qualifies capture and restore on a disposable Dev cell. |
|
Security Tier-A hold on exact |
Security found the console's check-then-open sequence open to a race: each path ran a read-only check, then the ordinary open, which can bootstrap. If restore tooling emptied or replaced the database in between, the command could initialize it. Each backend now has open_existing, used only by the console. None of them has a bootstrap path: - SQLite never creates the file or switches it to WAL. The pool's own connection must find the marker and the exact schema. - PostgreSQL takes the bootstrap advisory lock on the pool's connection, then requires the marker and the exact schema. - MySQL requires the marker table and the exact schema on the pool's connection. Anything else is refused as it is. The separate check calls are gone, and so is the earlier MySQL check change. Tests, all three backends: - A fresh database is refused and left without a schema. - Counterfactual: the server's own open initializes the same database, and the command then serves it. - A database that loses dw_query_cache or its marker is refused and not repaired. - Negative control: restoring the bootstrapping open fails the test on SQLite and PostgreSQL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Security re-review at exact |
|
Security acceptance hold after comparing the corrected source to issue #973: the no-bootstrap finding remains closed, but the delivered command contract does not match the recorded Cloud acceptance. #973 requires |
|
Cloud's durable interface requirement for the offline backup-hold command (answering Security 6591702220; supersedes my mapping note above). Least authority:
cloud#591 will call exactly |
General, Cloud and Security settled the offline interface on least authority, matching Cloud's recorded contract for cloud#591: - `backup-hold release --owner <uuid>` (or `--owner=<uuid>`): #953's release, by the recorded owner, idempotent, a lapsed restored hold included. - `backup-hold inspect`: the hold as stored, from plain SELECTs of row id=1 in one read-only snapshot. No lock, no row creation, no write. An absent row reports active false and owner null. Acquire, renew and status are gone from the console; live backups use the endpoints. Any other argument is refused before the database opens. inspect also opens read-only. SQLite opens the file read-only, and PostgreSQL checks the schema in a READ ONLY transaction without the bootstrap lock. MySQL's check was already one read-only transaction and now is for release too. Tests, all three backends: - inspect on a schema without the hold row reports no hold and creates none; - inspect leaves a restored row byte-identical; - release, both forms and repeated; - every other command or option is refused. Negative control: pointing inspect at the locking status path fails the test (SQLite refuses the write; PostgreSQL creates the row). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Installed-image proof at exact head Image:
Each command is a one-off container:
The restored lapsed hold is inserted with SQL after the image's own Results, on both backends:
Transcript |
|
Security acceptance re-review: the interface and installed-image holds are closed at exact head Source review:
Installed proof review:
This clears Security's source/interface/installed-image acceptance hold at |
|
Security final carry for exact head The source and installed-image acceptance recorded above remain exact: the offline surface is only read-only Hosted qualification is now terminal on this same head: 57 check contexts, 56 successful and the expected prepared-local cancellation context skipped, with no failure/cancellation. This closes Security's source, installed-artifact, and hosted-check holds. The release composition still needs to bind the published 3.0.2 image to this merged commit before Cloud enables its dormant offline floor. |
|
Security generated-merge clearance for exact merge-queue commit
The earlier source/interface/installed-image acceptance remains exact. This clears #974's queue carry and inclusion in 3.0.2; publication still must bind the final release artifact to the eventual main commit after #969 merges. |



Review tier A: data retention. It changes the external-payload reclamation hold from outside the server process.
Fixes #973. Requested by Cloud for native restore (cloud#591): the import releases the restored hold while the cell is offline, which PHP does with
artisan external-payloads:backup-hold release.Interface (least authority, agreed by General, Cloud and Security)
durable-workflow-server backup-hold release --owner <uuid>(or--owner=<uuid>): Native: online-backup hold endpoints, with PHP's deletion fence #953's ownchange_backup_hold(Release). Recorded owner, database clock, idempotent, and a lapsed restored hold is releasable by its recorded owner.durable-workflow-server backup-hold inspect:id=1and the database clock, in one read-only snapshot;active: false, owner: null.Both commands print the v1 report (
durable-workflow.external-payload-backup-hold.v1). On refusal they print the reason, or the owner validation message, and exit non-zero. They read onlyDB_CONNECTIONand its connection variables, and start no listener or workers.Never initializes or repairs a database
Each backend has an
open_existingused only by this command, with no bootstrap path. If restore tooling empties or replaces the database mid-run, the command still can't initialize it. A missing, empty, foreign or changed database is refused as it is.inspectopens the file read-only. The pool's own connection must find the marker and the exact schema.releasetakes the bootstrap advisory lock, then requires the marker and the exact schema;inspectchecks the same in aREAD ONLYtransaction, without the lock.Docs: the external-payload storage contract and the backup/restore guide describe both commands.
Tests
tests/execution/external_payloads/backup_hold.rsruns the built binary as a separate process. They pass 9/9 on each of SQLite, PostgreSQL 17 and MySQL 8.4.inspect --owner,--ttl, a duplicate or missing owner, unknown options and an invalid UUID.dw_query_cacheor its marker is refused and not repaired;The installed-image proof (the image built from this head, as a one-off read-only container against MySQL and PostgreSQL) is posted as a comment.
Rides 3.0.2 only if it clears in time.
🤖 Generated with Claude Code