Repository navigation
fix: keep courier iMessage pickup running across reboots - #18
Merged
Merged
Conversation
Nothing started bin/fm-courier-pickup.py: on the VM it ran as a transient systemd-run --user unit and had to be restarted by hand after every reboot. bin/fm-courier-pickup-service.sh renders fm-courier-pickup.service from this checkout's resolved paths and an explicit FM_HOME, and install writes it atomically into the account's systemd user directory. The unit runs `fm-courier-pickup.py run` with the FM_NOTIFY_COURIER=1 opt-in, is wanted by default.target (boot start for a lingering account), restarts 5 s after any failure, and runs as the installing account with NoNewPrivileges and no identity, capability or path grant of its own. Install never runs systemctl, because enabling the unit is the courier activation step. docs/voice-relay.md gains the install, enable, update and rollback runbook. The pickup regression now installs the unit into a temporary config directory, checks it with systemd-analyze verify where available, and runs the unit's own ExecStart with its own environment to prove it reaches this checkout's pickup for the named home; unsafe FM_HOME paths are refused. Boot start under linger and restart-on-failure under a live user manager are deferred to the VM pass and unverified here.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Part of the overnight hardening build: the smallest durable best-practice change, the way Kun builds Firstmate, never a today-only patch and never over-engineered. VM re-test finding P1: nothing starts Firstmate's courier iMessage pickup (bin/fm-courier-pickup.py run, merged in #17). On the VM it ran as a transient systemd-run --user unit and had to be restarted by hand after every reboot. Ship how the pickup runs so it survives reboots, before courier activation.
Referenced substance of #17: bin/fm-courier-pickup.py is Firstmate's side of the courier iMessage wire. Running as Firstmate's own account with an explicit FM_HOME and the FM_NOTIFY_COURIER=1 opt-in, its
runmode continuously reads the courier's inbound spool (/srv/courier/inbound) read-only, files each captain message into Firstmate's conversation transport, and answers through Firstmate-owned courier outbox requests; it is off unless FM_NOTIFY_COURIER=1, and nothing launched it.What Changed
Risk Assessment
✅ Low: The change is bounded, preserves existing account and transport authorization, and documents reboot and activation prerequisites.
Testing
The targeted courier suite and disposable CLI checks passed for installation, effective settings, enablement and rollback, path refusals, pickup execution, recovery, and test selection. CLI transcripts and persisted artifacts were retained; isolated systemd startup was blocked by cgroup permissions, leaving crash restart and reboot checks untested. Temporary fixtures and downloaded packages were removed.
Evidence: Launcher installation, enablement, refusals, and pickup recovery
Source: Launcher installation, enablement, refusals, and pickup recovery
Evidence: Targeted courier suite transcript
Source: Targeted courier suite transcript
Evidence: Installed systemd unit
Source: Installed systemd unit
Evidence: Pickup state after explicit relaunch
Source: Pickup state after explicit relaunch
Evidence: Launcher-only test selection
Source: Launcher-only test selection
Evidence: Isolated systemd manager permission blocker
Source: Isolated systemd manager permission blocker
Evidence: Manual CLI validation driver
Source: Manual CLI validation driver
Pipeline
Updates from git push no-mistakes
✅ **Intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 1 issue found → auto-fixed ✅
tests/fm-courier-pickup.test.sh:34- The generated configuration checks use raw string matching instead of asserting effective systemd semantics, contrary to the test-quality rule. For example, appending Restart=no after Restart=on-failure would still pass the presence check and syntax verification while disabling crash recovery. Replace the checks at tests/fm-courier-pickup.test.sh:34 (required directives) and tests/fm-courier-pickup.test.sh:36 (forbidden directives) with normalized, section-aware assertions or real-consumer property checks. Cover ExecStart, both Environment assignments, WorkingDirectory, Restart, NoNewPrivileges, WantedBy, User, Group, AmbientCapabilities, CapabilityBoundingSet and ReadWritePaths. Use the documented launcher contract as the oracle and account for repeated assignments and equivalent values described by systemd syntax. The direct startup check does not exercise restart behavior.🔧 Fix applied.
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
TMPDIR="$PWD/.test-phase-tmp" bin/fm-test-run.sh tests/fm-courier-pickup.test.sh.test-phase-tmp/codex -c 'PYTHONDONTWRITEBYTECODE=1 python3 .test-phase-tmp/manual.py "$$"; exit "$?"'systemd-analyze verify <installed-unit>against the generated disposable unitSYSTEMD_OFFLINE=1 systemctl --user --no-reload enable <installed-unit>,is-enabled fm-courier-pickup.service, anddisable fm-courier-pickup.servicewith isolated XDG configurationbin/fm-test-run.sh --list --changed --base <fixture-base>in a launcher-only git fixtureunshare --user --map-root-user --pid --fork trueapt download systemdanddpkg-deb --extractinside the disposable worktreeWorkspace-localsystemd --user --unit=default.targetunder bwrap, diagnosed with strace✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.