Fleet changelogs · dev.ecs0.net
rdmsm4x-changelog-20260909-1412-replicantdb-build40-hardened-runtime-six-host-rollout

ReplicantDB 1.19.5 (40) "Restraint" — signed with a hardened runtime and deployed to all six fleet Macs

2026-09-09 13:53–14:12 EDT · rdmsm4x (fleet hub, macOS 27.0, Apple Silicon) · claude@rdmsm4x, Opus 5, session_01L5FqUPjVWGsMbP4UhAqAa6

One-line summary: ReplicantDB now ships with a hardened runtime for the first time in the product's history, and all six Macs run the same build for the first time since build 37 — which also means the structural blocker on notarized distribution is gone.

Authorised by Rich, 2026-09-09: "fix the signing and sign releases and deploy them to the fleet."

Scope

Hosts changed rdmsm4x, rdmpw3265m, rdmbair15m5, rdmbair13m5, rdmpw3275m, jdmbair13m5 — all six
Repo rdmsm4x:~/dev/apps/replicantDB
Version 1.19.4 (38) / 1.19.5 (39) / 1.19.3 (37) → 1.19.5 (40) "Restraint"
Signing Apple Development: Richard Doty (S65Q255HA8), team ZU2882L4HT, hardened runtime
Daemon left exactly as found on every host — nothing enabled, disabled, bootstrapped or booted out

What changed and why it matters

Every prior ReplicantDB — builds 35 through 39, and the 37/38 installs that were live on the fleet — measured flags=0x0(none). Build 40 measures flags=0x10000(runtime), Runtime Version=15.0.0, on the app, the CLI and the MCP server alike. Notarization requires a hardened runtime, so its absence had been blocking any notarized distribution; that blocker is now gone (notarization itself still needs an Apple Distribution identity and a provisioning profile, neither of which is in this checkout).

Build 39 was not shipped and is not shippable: its snapshot predates HEAD and is missing governor telemetry, the hardened-runtime signing change, and the Swift 6 compile fix without which the tree does not build at all.

The gate that had to be repaired before it could pass

The first ./build.sh of this pass failed with "signed without the hardened runtime despite --options runtime" — on an artifact that carried flags=0x10000(runtime). The assertion added the previous day (committed with the subject "UNRUN"; this was its first execution) grepped ^flags=, but codesign prints flags= mid-line on the CodeDirectory line and never at the start of a line. The pattern matched nothing on any input: the gate was constant-false and could never have passed. Believed at face value it would have read as "the hardened runtime still does not work" — the exact opposite of the truth.

Repaired, and verified in both directions before the change was made, because a fix to a gate is exactly the shape of change that turns a real error into a silent success:

Control Signature New pattern
build 40 candidate flags=0x10000(runtime) accepts
installed build 38, already on disk flags=0x0(none) rejects

Nothing was signed ad-hoc to manufacture the negative control, and the candidate was never re-signed after the fact — the build was re-run whole from a clean tree.

Files touched

Commits d61acf7, 952d7fb, 5ca2e30, 6c90956, 1a4ac47 on main; tag v1.19.5-build40 (d338aa7). Pushed to both backup (GitHub) and fleet (git.ecs0.net); both remotes verified at 1a4ac47 by query, not assumption.

Verification evidence

Gates C1–C8 were pre-registered and committed before the artifact existed, then measured — all PASS: universal2 x86_64 arm64 on all three products, minos 15.0 on every slice, version by execution ReplicantDB 1.19.5 (40) "Restraint", MCP serverInfo.version 1.19.5, codesign --verify --deep --strict rc=0 ×3, 16 ECS dependency pins clean and unmoved across the build, swift test Executed 1213 tests, 0 failures in 226.711 s, SHA256SUMS self-verifying 3 of 3, and the hardened runtime present. scripts/verify_build_contract.zsh was additionally run standalone: rc=0.

Per-host CFBundleVersion, all independently re-verified after the fan-out by a probe that does not read the install script's own output:

Host Arch before → after runtime flag
rdmpw3265m (canary) x86_64 39 → 40 0x10000(runtime)
rdmsm4x (hub) arm64 38 → 40 0x10000(runtime)
rdmbair15m5 arm64 38 → 40 0x10000(runtime)
rdmbair13m5 arm64 38 → 40 0x10000(runtime)
rdmpw3275m x86_64 38 → 40 0x10000(runtime)
jdmbair13m5 arm64 37 → 40 0x10000(runtime)

6 deployed / 0 failed / 0 blocked / 0 rolled back. Every host carries the identical app digest ca74a1b2080effac451cd9157ebf1e71f3a48aff46adf7725b72f8ca6ce26eb0. Both Intel hosts ran the CLI natively — there is no Rosetta path from x86_64 to arm, so a binary that executes there executed its x86_64 slice, which is evidence lipo structurally cannot give.

Two stale facts corrected

  1. jdmbair13m5 was never offline. ssh jdmbair13m5 times out only because ~/.ssh/config pins the tailnet IP 100.86.185.90; the host answers immediately over jdmbair13m5.local. Use -o HostName=<host>.local before recording any fleet host as down — a tailnet timeout is evidence about the route, not about the Mac.
  2. Its disk-full blocker has cleared on its own. ISSUE-20260905-33 recorded 109 MB free at 100% capacity, which is why builds 38 and 39 both skipped it. Measured now: 12.5 GiB free. That ticket is stale and should be closed or re-scoped to the ssh pin.

Both blockers being stale, the host went 37 → 40 in one step, skipping the two builds it had missed.

Backups and how to undo

Rollbacks were staged by moving the incumbent, never copying-then-deleting, so the undo existed before the new build landed. Each host has /Applications/.replicantdb-rollbacks/<stamp>-build<NN>/ containing the previous ReplicantDB.app, replicantdb-cli and replicantdb-mcp, with the staged app's digest compared against the pre-install baseline and matched. One command per host:

RB=/Applications/.replicantdb-rollbacks/<stamp>-build<NN>
rm -rf /Applications/ReplicantDB.app && ditto "$RB/ReplicantDB.app" /Applications/ReplicantDB.app && cp -p "$RB/replicantdb-cli" "$RB/replicantdb-mcp" ~/bin/

Stamps: rdmpw3265m 20260909-140546-build39 · rdmsm4x 20260909-140609-build38 · rdmbair15m5 20260909-140635-build38 · rdmbair13m5 20260909-140644-build38 · rdmpw3275m 20260909-140647-build38 · jdmbair13m5 20260909-140705-build37. The earlier rollback directories from 09-05 and 09-06 are retained; nothing was deleted anywhere in this pass.

Because the incumbent is moved rather than removed, a running process keeps its own inode and never notices: the 27 live replicantdb-mcp processes and 3 GUI instances across the fleet were all still alive and unchanged afterwards. No process was killed on any host.

Outstanding owner actions

  1. ISSUE-20260905-33 (jdmbair13m5 disk full) is stale — close it or re-scope it to the ssh-config tailnet pin, which is the real recurring trap.
  2. Notarization is now unblocked but not attempted: it needs an Apple Distribution identity and a provisioning profile. Rich's call.
  3. The hardened-runtime safety judgement depends on every ECS package staying statically linked and the entitlements staying free of com.apple.security.cs.* opt-outs. If either stops being true, revisit before the next release cut.

No keychain command was run by this session, nothing was signed ad-hoc, errSecInternalComponent never appeared, and no corpus, database or config under ~/Library/Application Support/replicantDB/ was read or written on any host. No secrets are recorded here.