rdmsm4x-changelog-20260909-1705-replicantdb-build41-notarized-developer-id
2026-09-09 17:05 EDT · rdmsm4x (macOS 27.0, 26A428, Apple Silicon) · claude@rdmsm4x (Opus 5, session_01L5FqUPjVWGsMbP4UhAqAa6)
ReplicantDB 1.19.5 (41) "Restraint" is the first notarized build of this product. Cut, notarized, stapled and deployed to ALL SIX fleet Macs. 6 deployed / 0 failed / 0 blocked / 0 rolled back.
Authority: DEC-20260909-03 (Rich, 2026-09-09) —
every ECS app ships signed with
Developer ID Application: east coast science, llc (ZU2882L4HT),
hardened runtime, --timestamp, notarized via the
ecs-notary notarytool keychain profile, stapled. TCC
re-prompts on the hosts accepted by Rich.
Why this was needed even though Build 40 shipped three hours earlier
Build 40 was the first ReplicantDB with a hardened runtime.
Necessary, and on its own not sufficient: it was signed
Apple Development, which Apple's notary service does
not accept at all. Gatekeeper's verdict comes from the notary
ticket, not from the certificate, so build 40 measured
rejected under spctl -a -t exec on every host.
Build 41 measures
accepted / source=Notarized Developer ID.
Marketing version stays 1.19.5; build 40 → 41. No rule in the repo
bumps the marketing version for a signing-identity change, and
Version.swift's contract is "CFBundleVersion — monotonic,
never reused".
Files
changed (all in /Users/richh/dev/apps/replicantDB,
committed by explicit path)
| File | Change |
|---|---|
Sources/ReplicantDBCore/Version.swift |
build 40 → 41 |
build.sh |
Developer ID constants; --timestamp on codesign;
timestamp read-back assertion; new NOTARIZE step (ditto zips →
notarytool submit → poll → log issues null → stapler staple/validate →
spctl); manifest gains notarization fields |
scripts/verify_build_contract.zsh |
same identity constants; asserts runtime, timestamp, Gatekeeper verdict and the stapled ticket; separate assertions for the bundle and for bare tools |
dist-snapshots/1.19.5-build41/ |
SNAPSHOT.md, SHA256SUMS, build manifest, gate pre-registration, build + test logs |
SESSION-STATE.md |
2026-09-09 17:05 EDT checkpoint (per-host table, submission ids, rollbacks, blocked hosts) |
ISSUES.md |
RESOLVED "current app artifact is development-signed"; correction appended to the build-40 hardened-runtime item |
Commits 378499d, 0ba5c36,
f9b4405, 3b9dae4, 8ae4ede,
2f0e1ac, 30065ad on main. Pushed
to remotes backup and fleet. Tag
v1.19.5-build41 on both, dereferencing to
30065ad.
Commands run (the load-bearing ones)
nice -n 10 ./build.sh # rc=0, 82 s end to end
scripts/verify_build_contract.zsh # rc=0 standalone
nice -n 10 swift test # rc=0, Executed 1213 tests, 0 failures, 254.284 s
xcrun notarytool submit … --keychain-profile ecs-notary
xcrun notarytool info/log … --keychain-profile ecs-notary
xcrun stapler staple / validate dist/ReplicantDB.app
spctl -a -vv -t exec <each artifact>
ditto -c -k --sequesterRsrc --keepParent # transport, preserves signature AND stapled ticket
Verification evidence
Gates C1–C9 were pre-registered at commit
f9b4405, before the artifact existed, then
measured. All PASS.
- C1
lipo -archs=x86_64 arm64on app, cli, mcp · C2minos 15.0on every slice - C3 by execution:
ReplicantDB 1.19.5 (41) "Restraint", rc=0;CFBundleVersion41 - C4
codesign --verify --deep --strictrc=0 ×3;Authority=Developer ID Application: east coast science, llc (ZU2882L4HT);TeamIdentifier=ZU2882L4HT - C5 17 ECS path-dependency checkouts clean and at identical HEADs before and after, matching the pre-registered baseline
- C6
Executed 1213 tests, with 0 failures (0 unexpected) in 254.284 (254.405) seconds, rc=0 — identical to build 40, no drop - C7
shasum -a 256 -crc=0, 3 of 3 · C8flags=0x10000(runtime)×3 - C9 both submissions Accepted,
notarytool logissuesnull,stapler validaterc=0,spctlsource=Notarized Developer ID
Artifact digests:
a3a7a333e15e56c31837943a0cee7a3852eb034b2104ae555ae4a9d6abf8df2d ReplicantDB.app/Contents/MacOS/ReplicantDB
7a541b39eeb7e7906dcb247560850a87e49076e9766e73ef75ae3a40fb972175 replicantdb-cli
b64af744778afbadd8d922f5d22d7ecae364cc3ae88acd24c8d07a3b665a081e replicantdb-mcp
Notarization: app 71c2c29d-ec88-4214-8117-f90748383e6d,
tools bfe0bd12-e10f-4cb3-b4f0-de48204086af. Four superseded
submissions from two aborted runs are listed in SNAPSHOT.md so they are
never resubmitted.
Per-host result
| Host | Arch / OS | CFBundleVersion | Result |
|---|---|---|---|
| rdmpw3265m (canary) | x86_64 / 26.7 | 40 → 41 | OK — Intel, verified by native execution |
| rdmbair15m5 | arm64 / 27.0 | 40 → 41 | OK |
| rdmpw3275m | x86_64 / 26.7 | 40 → 41 | OK — Intel, verified by native execution |
| rdmsm4x (hub) | arm64 / 27.0 | 40 → 41 | OK |
| rdmbair13m5 | arm64 / 27.0 | 40 → 41 | OK — recovered at 17:13 after its macOS 27.0 update |
| jdmbair13m5 | arm64 / 27.0 | 40 → 41 | OK — recovered at 17:26 after its Tailscale node was restarted |
6 deployed / 0 failed / 0 blocked / 0 rolled back. Daemon state left exactly as found on every host.
Independently re-verified at 17:27:09 EDT by a probe that does not
read the install script's output. All six identical:
CFBundleVersion 41, 1.19.5 (41) "Restraint" by
execution, app digest a3a7a333e15e…,
x86_64 arm64,
codesign --verify --deep --strict rc=0,
stapler validate rc=0, spctl -a -t exec rc=0,
daemon not-loaded as found.
How the two blocked hosts were recovered rather than forced
- rdmbair13m5 had taken the macOS 27.0
update (
26A5425a→26A428) — that was the reboot. Back at 17:08:35. Its uptime gate was honoured rather than rounded: at 17:09 it readup 5 minsagainst a gate of greater than 5, with load average 296.75 from the post-boot storm. Waited one cycle; installed at 17:13 atup 9 mins, load 11.27. - jdmbair13m5 was never down — its Tailscale node
was.
ping jdmbair13m5.local0.0% packet loss whiletailscale statusread "offline, last seen 45m ago" andBackendStatewasStoppedwith the app running. One reversibleTailscale up(rc=0, no output, no login URL, no credential prompt, no keychain command) took itStopped → Running. Route repair is not a deploy and the two were kept separate: the repair went over mDNS viassh -o HostName=jdmbair13m5.local— the override is required because~/.ssh/configmaps bothjdmbair13m5andjdmbair13m5.localto the pinned tailnet IP, so plain.localsilently resolves to the dead route — and every deploy step usedjdmbair13m5.ts.dataroo.net.
Three checks that measured the wrong thing
- The handed-over readiness probe greps
softwareupdated— a resident daemon present on all six hosts, including one up 1 day 4 hours. Read literally it blocks every host forever. The clause that varies isInstaller Progress(0 everywhere). Itspgrep -falso self-matched through the ssh command line (22 false hits on the hub), andpgrep -x tailscaledreported STOPPED on five hosts reached over the tailnet. spctl -a -t execnever accepts a bare Mach-O, so the assertion on the two CLIs was constant-false — the mirror of build 40's runtime gate, one commit later. Replaced using three measured controls that all exit 3, so only the text discriminates.open -areturned rc=0 and a pid on all four hosts and actually launched build 41 on one. On three it re-activated a process still executing from the moved-aside rollback directory — including a build 38 binary running since Sep 8. Found withlsofon each pid'stxt; fixed with a graceful quit and relaunch, re-measured. Nothing force-killed.
Backups and how to undo
Rollback directories were staged before each install by moving the incumbent (never copying-then-deleting), so running processes keep their inode:
/Applications/.replicantdb-rollbacks/20260909-165938-build41-from-40 # rdmpw3265m
/Applications/.replicantdb-rollbacks/20260909-170030-build41-from-40 # rdmbair15m5
/Applications/.replicantdb-rollbacks/20260909-170057-build41-from-40 # rdmpw3275m
/Applications/.replicantdb-rollbacks/20260909-170124-build41-from-40 # rdmsm4x
/Applications/.replicantdb-rollbacks/20260909-171352-build41-from-40 # rdmbair13m5
/Applications/.replicantdb-rollbacks/20260909-172629-build41-from-40 # jdmbair13m5
RB=<one of the above>
rm -rf /Applications/ReplicantDB.app && ditto "$RB/ReplicantDB.app" /Applications/ReplicantDB.app \
&& cp -p "$RB/replicantdb-cli" "$RB/replicantdb-mcp" ~/bin/
Every earlier rollback directory (09-05, 09-06, build 40) is retained. Nothing was deleted anywhere.
Outstanding owner actions
- None for the deployment — all six hosts are on build 41 and
independently verified. Worth knowing:
jdmbair13m5's Tailscale node has now stopped after a reboot twice in one day (14:16 and 16:41), each time needing a manualTailscale up. That is a recurring pattern rather than a one-off, and is worth a launch-time fix on that host. replicantdb-cliandreplicantdb-mcpare notarized but cannot be stapled (bundles, dmgs and pkgs only). They need network reachability to Apple for a first-launch ticket lookup where the app does not. Property of the format, not a defect.- The hardened-runtime caveat still stands: if any
ECS dependency ever becomes a dynamic framework, or a
com.apple.security.cs.*entitlement is added, revisit before the next release cut — notarization now fails too, not just launch.
Not done, deliberately
No keychain command of any kind (login keychain remains first in the
search list, fleet-signing second). No daemon enabled, disabled,
bootstrapped or booted out. Nothing deleted. No process force-killed. No
corpus, database or config under
~/Library/Application Support/replicantDB/ read or written.
jdmbair13m5's route was repaired over .local (diagnosis and
one Tailscale up), but it was never installed over
.local — every deploy step used the pinned tailnet name. No
secrets appear in this record: the signing identity is named by common
name and SHA-1 fingerprint (both public), and the notarytool credential
is referenced only by its keychain profile NAME
(ecs-notary).