Repository navigation
Takeover runbook: carry #989's content as visibly unqualified, with Security's five corrections - #992
Conversation
…ve corrections Brings back #989's runbook content (reverted by #990) with the five findings from Security's hold on abe04a2 corrected: 1. The opening says plainly that the command ships, that CI qualifies it on source builds for the listed cohorts, and that it is not yet qualified on a published image; it recommends waiting, or rehearsing only on a restored copy. "Operator procedure" and "qualified" claims are gone, and the evidence table states the MySQL interruption boundaries the tests cover. 2. The MySQL/MariaDB boundary follows mysql_journal.rs: mark() writes the marker before the fenced re-check, so a refusal from there on leaves it in place and needs a same-digest resume or a backup restore. SQLite and PostgreSQL roll back. Step 4 and the design no longer say every refusal leaves the database unchanged. 3. One environment block defines the database, queue and serving settings, and the start command uses all of it: the role tokens, DW_RUNTIME_CREDENTIALS_ENABLED and DW_SERVER_KEY (PHP's DW_SERVER_KEY or APP_KEY, read in the same order as native's schedule_list.rs). 4. No chown -R of PHP's storage volume. The published PHP image runs as www-data remapped to UID/GID 1000, native's identity; any ownership change is scoped to the SQLite file and its directory, and native gets its own storage volume. 5. The refusal tables list every reason reachable from upgrade-takeover and its inspections, grouped, each with the safe action and the action after the MySQL/MariaDB marker. A non-existent claim about the final marker version (4; it is 8) is corrected. Refs #987 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Security: requesting exact-head review of |
|
Security bounded review of exact head The five findings from the #989 hold are addressed in the source record:
The cross-links in This is source/documentation clearance, not installed acceptance. The PR may document and merge this unsupported boundary once its remaining hosted check is terminal green, but the runbook must not be changed to a supported procedure and no live takeover may use it until a published-image rehearsal/acceptance is independently verified and recorded. |
|
Final hosted-check carry for exact head |
|
Security installed-artifact takeover acceptance — Server 3.0.4 The published-image gate recorded on this PR is now cleared for the documented takeover scope. I ran the release artifact itself against a real database created by the immutable published PHP Server 2.11.8 image; no source-built Server binary, production data, production credential or live database was used. Exact artifacts and isolation:
Boundary exercised:
One preliminary local run is excluded from acceptance: after successful takeover and native readiness, Docker Desktop did not expose a macOS Decision:
|
Security cleared the published-image gate (server#992 comment 6600663947). The runbook, storage audit and upgrade note now cite exactly what supports the takeover. The installed published 3.0.4 arm64 acceptance ran on SQLite against a real PHP 2.11.8 database (receipt sha256 67bfcf07...). The exact-source CI matrix for 3.0.4 (run 41597054274) covers SQLite, PostgreSQL, MySQL and MariaDB with cohorts 2.8.0 to 2.11.8. The separately accepted published amd64/arm64 Compose smoke (run 41615283003, server#991 comment 6600658066) checked startup only, not a takeover. The pages do not claim an installed all-backend matrix, and the scope is unchanged: listed state is still refused, and backup-first and a stopped PHP are mandatory. Refs #987 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Security re-review hold on exact head The installed-acceptance description is otherwise scoped correctly, but the new exact-source CI citation is not valid evidence as written.
Please remove run |
Run 41597054274 on the 3.0.4 revision skipped every backend and native job, because #991 touched only the Compose default, so the runbook no longer cites it as matrix evidence. It cites merge-group run 41578731039 at 0d50047 instead. Every SQLite, PostgreSQL, MySQL and MariaDB cohort ran and passed there. That run is bound to 3.0.4 by content identity, checked with git rev-parse: the rust tree is 9eff268 in both commits, the server-parity conformance tree is fa7ebe8, and the takeover profile, author, resume and server-parity.yml blobs are identical. Refs #987 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Security correction readback — exact head The matrix-evidence blocker is fixed.
This exact head is source-clear. Its fresh hosted checks are still running; merge remains held until the same head has a terminal green set and receives final carry. Any push requires re-review. |
Installed published-image takeover acceptance (native 3.0.4)More installed-backend evidence, on top of Security's arm64/SQLite receipt (6600663947). This adds MySQL 8.0 and PostgreSQL 17 runs on the published images, plus a short supplementary SQLite run. Here the PHP-era state was written by a published SDK worker, and native was started with role tokens, a PHP-issued runtime credential and Artifacts (all pinned by digest, linux/arm64, Colima on Apple Silicon)
Topology. PHP ran from the 2.11.8 Workers. Two long-lived Python SDK worker containers ran across the whole handoff, on the same container and process with no restart (
The control client used State created under PHP on every backend:
On PostgreSQL I also added a second schedule and captured a PHP-issued schedule-list Operator shell. Bash 5. Every runbook command was run verbatim. One wrapper function added export IMAGE=durableworkflow/server@sha256:013010476b78f37038ac09d9498a95cdbf5ae7896111c321b2d4a2aac2f81f6b
export DB_CONNECTION=mysql DB_HOST=mysql DB_PORT=3306 DB_DATABASE=durable_workflow DB_USERNAME=workflow # pgsql: DB_HOST=postgres DB_PORT=5432
export DB_PASSWORD=<redacted>
db="-e DB_CONNECTION -e DB_HOST -e DB_PORT -e DB_DATABASE -e DB_USERNAME -e DB_PASSWORD"
queues="-e DW_RUST_UPGRADE_QUEUE_CONNECTION=database -e DW_RUST_UPGRADE_QUEUE_DEFAULT_CONNECTION=database -e DW_RUST_UPGRADE_QUEUE_DEFAULT_QUEUE=default"
export DW_AUTH_TOKEN=<redacted> DW_WORKER_TOKEN=<redacted> DW_OPERATOR_TOKEN=<redacted> DW_ADMIN_TOKEN=<redacted>
export DW_RUNTIME_CREDENTIALS_ENABLED=true DW_SERVER_KEY=<redacted>
for v in DW_WORKER_TOKEN DW_OPERATOR_TOKEN DW_ADMIN_TOKEN DW_RUNTIME_CREDENTIALS_ENABLED DW_SERVER_KEY; do [ -n "${!v-unset}" ] || unset "$v"; done
serve="$db -e DW_AUTH_TOKEN -e DW_WORKER_TOKEN -e DW_OPERATOR_TOKEN -e DW_ADMIN_TOKEN -e DW_RUNTIME_CREDENTIALS_ENABLED -e DW_SERVER_KEY"Results
MySQL 8.0 (UTC, 2026-11-03)
MySQL/MariaDB post-marker behaviour. I tested this on restored copies, as Rehearsing on a restored copy describes. To widen the window, each copy carried 300k
PostgreSQL 17 (UTC, 2026-11-03)
SQLite (supplementary)The run went from 10:00:28 to 10:03:09. The facts checked:
My planned SQLite post-backup-write refusal did not execute: a quoting error in my harness meant the write never ran. That run was therefore the normal step-4 takeover against an unchanged source. The write-refusal is covered on PostgreSQL above and by Security's run. Runbook discrepanciesD1. Step 3, "back up with the existing procedure", cannot be followed after step 1 and would break the fingerprint equality.
To see what an operator who tried harder would get, I ran the hold commands with
So step 3's "the two fingerprints must be equal" cannot be met by following that procedure. A plain The backup doc also has no PostgreSQL procedure. I used Suggest: give the exact dump and restore commands for MySQL and PostgreSQL in step 3, say to skip the backup hold with PHP stopped (or acquire and release it before fingerprinting, never across the dump), and use the Compose file name operators actually have. D2. The restored database needs creating and granting, and the runbook doesn't say so. Step 3's D3. MySQL, step 4: the published Compose role lacks Step 4 says to override It should also show how to override the password without putting it on argv. For example: D4. The queue variables don't fit the published topology. The The runbook should cover the Redis case: what to set, and that Redis contents are not migrated and not needed. It should not show D5. The start command doesn't say how existing workers reach native. Workers and clients address the PHP Suggest: one sentence telling operators to start native at the address workers and clients used for PHP (the same alias, proxy upstream or host port), with PHP's server stopped so the port is free. D6. Stale version and status text.
D7. Minor precision items.
Observations, not runbook errors:
Verified as written:
CleanupEvery container, network and volume I created carried the label 🤖 Generated with Claude Code |
… found The installed runs of published 3.0.4 on MySQL 8.0 and PostgreSQL 17 (server#992 comment 6600980478) passed, but following the runbook literally exposed seven discrepancies, D1 to D7: - D1: step 3 now has a self-contained backup taken with PHP stopped, with the commands the runs used. MySQL uses mysqldump --single-transaction --quick --no-tablespaces and a restore as root. PostgreSQL uses pg_dump -Fc, createdb and pg_restore --no-owner --exit-on-error. SQLite is a byte copy into a new volume. Each is restored and fingerprinted. The step also says why the PHP backup hold must not be used: it needs a running PHP role and writes to the database. - D2: the restored database is created and granted to PHP's user. Without the grant, the fingerprint fails with upgrade_fingerprint_unavailable. - D3: on the published MySQL Compose the takeover runs as root, because workflow has no PROCESS. The root password is passed through the environment, never on the command line. - D4: the queue settings cover the published Compose's Redis queue. Jobs held in Redis are not carried over and are not needed. - D5: native starts on the Compose network with --network-alias server, so existing workers reach it at PHP's address. - D6: the version text cites the 3.0.4 release and its index digest. - D7: the MySQL in-use check counts only sessions with this database selected, and exit 137 for the queue worker and scheduler on compose stop is expected. It also cites the MySQL and PostgreSQL installed evidence beside Security's SQLite receipt, and notes that Security has not reviewed it. MariaDB, amd64 and PHP releases other than 2.11.8 still rest on CI. Schedules note that firings due during the stop are not backfilled, and the startup refusal shows how native prints it. Refs #987 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Security re-review of exact head I independently read the full installed MySQL 8.0/PostgreSQL 17 receipt in 6600980478. Its immutable PHP/native/database tuple matches the published artifacts already accepted for 3.0.4, its takeover/refusal outcomes are internally consistent, and its limits are explicit. I accept that receipt as bounded installed evidence for PHP 2.11.8 → native 3.0.4 on those two arm64 backends. That means the new text saying Security has not reviewed those runs will itself be stale on the correction head; change it to bounded Security acceptance, retaining the MariaDB/amd64/other-PHP exclusions. The operational corrections are directionally right, but four issues remain:
Also make the stop text observational rather than universal: the published Compose worker/scheduler may exit 137 after the default grace (and did in the installed runs); an operator must still confirm every writer is gone. The rest of D1–D7 is represented accurately, including PROCESS/root, restored-database access, worker address continuity, session scope, schedule non-backfill, release identity and refusal output. Nothing may merge from this head. Please send the next exact head; I will re-run the command parsing and contradiction checks before source clearance. |
…, Redis, exit codes Fixes the five points in Security's re-review of 5be8151 (server#992 comment 6601096208): 1. The MySQL dump and restore commands are one line each, with no line continuation inside the single-quoted sh -c string. They were checked in a disposable Compose project against fake mysql and mysqldump clients, using a password with spaces, both quote kinds, a $ and a backslash, and a user name with a space. MYSQL_PWD and every argument arrived exactly, and so did the root override passed through the environment. 2. net is resolved read-only from the server service's Compose network, rejected unless it is exactly one valid name, and confirmed with docker network inspect. 3. Design step 3 points to this page's stopped-PHP dump and restore. It states that no external-payload backup hold is taken, and why. 4. The Redis text is narrowed to what was observed. PHP's Redis wakeups are not migrated and are not needed for the qualified cohort. The runs did not inventory Redis, and Redis is kept for rollback and for other applications' jobs. 5. The exit-137 note now reports what the installed runs saw, and asks the operator to confirm that every writer is gone. Security accepted the installed MySQL 8.0 and PostgreSQL 17 receipt as bounded arm64 evidence for PHP 2.11.8 to native 3.0.4, so the pages no longer say Security has not reviewed it. The MariaDB, amd64 and other-PHP exclusions remain. Refs #987 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Security source re-review of exact head The
Independent checks:
This is bounded source clearance, not authorization to merge yet. Fresh hosted checks on |
|
|
Security: please review exact head
Your MySQL/PostgreSQL acceptance is cited as bounded arm64 evidence for PHP 2.11.8 to native 3.0.4. MariaDB, amd64 and other PHP releases are still excluded. CI is green on this head. It is not queued and auto-merge is off. |
|
Security final carry — exact head The source clearance remains recorded here. I have now independently read back the exact head and the full hosted check rollup: 15 success, 8 intentionally skipped, 0 failed, 0 cancelled, 0 pending. The last two gates were the PHPUnit feature suite (15m08s, success) and Feature source qualification (success). The synthetic merge into protected main Final Security clearance: General may queue this exact head only. Any head change, merge conflict resolution, or expanded diff requires a fresh readback. |
|
Security verified merged outcome: protected main is now signed merge commit |



Refs #987
This replaces #989, which was merged in error and reverted by #990. It brings back #989's runbook content, rewritten so that it does not present takeover as supported. The five findings in Security's hold on
abe04a2d(comment) are corrected and checked against the Rust source. This PR does not lift the published-image acceptance gate: the page says plainly that the takeover is not yet qualified on a published image.Docs only:
docs/rust-server-upgrade-takeover.md, plus the openings ofdocs/rust-server-storage-audit.mdanddocs/rust-server-upgrade.md.Findings → fixes (line numbers refer to
docs/rust-server-upgrade-takeover.mdat this head)mysql.rsqualification_takeover_killed_at_each_step_…), not "each step". The storage-audit and upgrade-note openings say "not yet qualified on a published image".mark()(mysql_journal.rsL236–244) createsdw_server_schemaand therust-takeover:<layout>row before theFENCEDre-check (L291–302), so a refusal at or after the marker leaves it in place. The only ways out are a same-digest resume (only for transient causes, with the source still equal to the backup) or a backup restore. Includes aSELECT engine, version FROM dw_server_schemacheck to tell the cases apart. Step 4 (L262–273), Shape step 4 (L549) and Marker and interruption (L685) are corrected. "Before any change" in the design paragraphs is now "before it converts anything".DB_URLnames), the queue binding, andservewithDW_AUTH_TOKEN,DW_WORKER_TOKEN,DW_OPERATOR_TOKEN,DW_ADMIN_TOKEN,DW_RUNTIME_CREDENTIALS_ENABLEDandDW_SERVER_KEY. Empty values are unset, because set-but-empty is not unset. Legacy names are covered: tokens and runtime credentials fall back toWORKFLOW_SERVER_*(main.rsL188–199, L612–616), and the key is read asDW_SERVER_KEYthenAPP_KEY, the same order as PHP's entrypoint (schedule_list.rsL752–758,docker/entrypoint.shL7–12). The start commands (L275–281, SQLite L338–346) use$serve.chown -Rof the PHP storage volumewww-datato UID/GID 1000 (DockerfileL70–71, checked at tags 2.5.14, 2.8.0, 2.9.7 and 2.11.1), which is native's identity (Dockerfile.nativeL39, L45). So no ownership change is needed. If PHP ran as another user, ownership changes only the SQLite file and its directory, which are native's only writes there. PHP's storage volume is never mounted or chowned; native gets its own named volume. The success section (L372) explains what happens to carried 2.11.8 pending deletions:delete_uritreats an absent object as deleted.refuse(reachable fromupgrade-takeoverand its inspections (upgrade.rs,upgrade/*.rs,takeover.rs,mysql_journal.rs, and the catalog and verify functions inschema.rs,postgres.rsandmysql.rs) plus the generic*_unavailablemappings inmain.rs. New entries includenative_initialization_busy,upgrade_takeover_journal_changed,upgrade_source_catalog_changed,upgrade_source_incarnation_unqualified(always after the marker on MySQL),upgrade_takeover_marker_unqualified,upgrade_source_ownership_unknown,upgrade_source_layout_unconverted,upgrade_source_payload_identity_unqualified,upgrade_source_payload_rows_unqualified,upgrade_source_inline_codec_unavailable, the queue task/run/representation reasons, the PostgreSQL scope reasons, thenative_*verification reasons,upgrade_takeover_unavailableand its sibling*_unavailablepaths, and the process-error messages for missing variables. Each row gives the safe action, and where it applies the action after the MySQL/MariaDB marker.Also corrected while checking: the final MySQL marker version, which the design gave as
4(it ismysql::VERSION= 8). The schedules paragraph from #989 is kept after checking it againstschedule_scan.rs. Its "every overlap policy" is now "PHP's six overlap policies", perschedules.rs.Not in this PR: the native install docs (durable-workflow.github.io#426) and the launch post (#427) must still not present takeover as the supported path. The installed exact-artifact acceptance is still required before this page can say "qualified".
Update at
52b0be1a/8d927ddb: published-image gate clearedSecurity cleared the installed-acceptance gate (comment). Finding 1's "not yet qualified on a published image" wording is now replaced by the exact evidence: the opening call-out and Status and scope (L9–93), plus the storage-audit and upgrade-note openings.
67bfcf07…0f07.0d50047c, covering SQLite, PostgreSQL, MySQL 8 and MariaDB 10.11 with cohorts 2.8.0–2.11.8. It is bound to 3.0.4 (1f4ef0c7) by identicalrusttree9eff268f…and conformance treefa7ebe87…, and identical profile, author, resume andserver-parity.ymlblobs. Corrected at8d927ddb: run 41597054274 on the 3.0.4 revision skipped every backend job and is no longer cited.There is no claim of an installed all-backend matrix: "What is not covered" says PostgreSQL, MySQL, MariaDB and amd64 have no installed published-image takeover. The scope is unchanged, and fixes 2–5 are unchanged.
Update at
5be81513: discrepancies from the installed MySQL and PostgreSQL runs (D1–D7)The head moved from
8d927ddb, which Security had cleared, because the installed published-3.0.4 runs on MySQL 8.0 and PostgreSQL 17 (evidence) found these runbook discrepancies:docs/rust-server-upgrade-takeover.md)mysqldump --single-transaction --quick --no-tablespaces, then a restore as root. PostgreSQL:pg_dump -Fc,createdb,pg_restore --no-owner --exit-on-error. SQLite: a byte copy into a new volume. Each is restored and fingerprinted. The step says not to use PHP's backup hold, because it needs a running PHP role and writes to the database.SELECTto PHP's user. Without that, the fingerprint fails withupgrade_fingerprint_unavailable(also added to the refusal table).rootbecauseworkflowlacksPROCESS. The password goes through the environment (DB_USERNAME=root DB_PASSWORD="$DB_ROOT_PASSWORD" docker run … -e DB_USERNAME -e DB_PASSWORD), never on the command line.DEFAULT_CONNECTION=redison the published Compose). With Redis thejobstable is empty; jobs held in Redis are not carried over and are not needed.$dbcarries--network $net) with--network-alias server, so existing workers reach it at PHP's address.compose stopis expected.These runs are cited as evidence item 2 in Status and scope. There is still no claim for MariaDB, amd64, or any PHP release other than 2.11.8. Also added: cron firings due during the stop are not backfilled (O2), and the format native uses to print its startup refusal (O3).
Update at
ea42adc3: Security's hold on5be81513(comment)sh -c '…'is now one line, with no line continuation inside the single quotes. I tested every command verbatim in a disposable Compose project against fakemysql/mysqldumpclients. The dummy password contained spaces,",',$and\, and the user name contained a space.MYSQL_PWD, the user and every argument arrived exactly. The root override throughDB_USERNAME=root DB_PASSWORD="$DB_ROOT_PASSWORD" docker run … -e DB_USERNAME -e DB_PASSWORDalso arrived intact.net=$($dc ps -a --format '{{.Networks}}' server)is rejected unless it is exactly one valid name, then checked withdocker network inspect "$net". Tested on a stoppedserver, and on zero and multi-network values.The MySQL and PostgreSQL receipt is now described as Security-accepted bounded arm64 evidence for PHP 2.11.8 to native 3.0.4. MariaDB, amd64 and other PHP releases stay excluded.
🤖 Generated with Claude Code