fix(install): Pod 5 install hardening — iptables lock, frank-less RAW redirect, env-monitor crash#616
Conversation
PEP 604 union syntax (int | None) requires Python 3.10+, but the module pins requires-python = '>=3.9,<3.11' and Pod 5 ships 3.9.9. The service was crash-looping at import time on a user's Pod 5.
…er warnings NAAAA's Pod 5 install surfaced four issues. This bundles the fixes plus docs for the env-monitor service. iptables xtables.lock permission denied The next-server runs as User=sleepypod with CAP_NET_ADMIN, but the default /run/xtables.lock is root:root 0600 — the cap doesn't bypass file mode, so setInternetAccess crashes the first time a user toggles WAN. Set XTABLES_LOCKFILE=/run/dac/xtables.lock (RuntimeDirectory=dac is owned by the service user) in the main unit, and add an idempotent drop-in via sp-update so OTA-upgraded pods self-heal without rewriting the unit. Verified on eight-pod: same systemd-run caps, A/B with vs without the env — success vs the exact 'can't open lock file' error. frank-less RAW redirect Older Pod 5 firmware ships frank.sh which we patch to cd into the tmpfs. Newer Pod 5 variants (NAAAA's) drop the shim — frank.service ExecStarts frankenfirmware directly, so the cd-patch path no-ops and ~1 GB/day of RAW frames lands on eMMC instead of tmpfs. Add WorkingDirectory=/persistent/biometrics to the frank.service drop-in: no-op on shim pods (shell cd still wins), genuine redirect on shimless pods. Verified on eight-pod that the drop-in parses, the unit reloads, frankenfirmware restarts cleanly, and cwd is unchanged. Clearer install-time warnings 'Warning: /opt/eight/bin/frank.sh not found — skipping patch (not a Pod?)' and 'Warning: dac.sock not found, using default: ...' both print on every shimless install and make a working install look broken. Replace with informational lines that say what's happening and why it's fine. env-monitor docs Sets the per-module README precedent: what it writes, why freezer_temp can be empty on cover-only Pod 5, how to debug, and a note about the Python 3.9 pin (PEP 604 syntax bites — see PR #483 fix in 7d23788).
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (8)
📝 WalkthroughWalkthroughThis PR updates systemd service configuration for xtables lockfile permissions and biometrics archiver RAW frame redirection on newer Pod 5 variants, adds service upgrade and cleanup logic, provides comprehensive environment-monitor module documentation, and updates detection and installation helper messages to reflect these changes. ChangesService Configuration and Installation
Environment Monitor Module Documentation and Type Annotations
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…ard, install hardening (#621) ## Summary 19 commits since v2.2.0 (#601). Highlights: **Features** - Incremental scheduler upsert/cancel API replaces global `reloadSchedules()` on CRUD (#613) - Device status now exposes wifi, room climate, and water level (#389) - biometrics.db backup + `--restore` flag (#358) - Pump stall safety guard, per ADR 0022 (#614) - Pod ambient temp exposed to HomeKit + Home Assistant (#606) - LED brightness UI with `{lb:N}` CBOR key (#607) **Fixes** - side-selector edges meet at center under the link icon (#620) - update-card requires service bounce before re-blocking WAN (#619) - install: drop v6 rules file from free-sleep detection (#618) - settings: debounce pump-safety number inputs to onBlur (#617) - Pod 5 install hardening — iptables lock, frank-less RAW redirect, env-monitor crash (#616) - install: `detect_free_sleep_conflict` no longer aborts on clean pods (#603) - scheduler: alarms no longer fire vibration when the side is off (#602) - install: `/persistent/deviceinfo` group=dac mode=2775 + self-heal (#600) **Docs** - Clarify Pod 5 root bootstrap so rewt/dac messages aren't a surprise (#615) **Tests / chore** - Kill 96 surviving useSchedule Stryker mutants (#610) - Bump @base-ui/react non-major deps (#611) ## Test plan - [x] All CI gates green on dev tip 3b90e51 (Build & Package run 26432358190) - [ ] Smoke-deploy dev to a pod — services up, web UI 200, DAC + MQTT + HomeKit connected - [ ] Post-merge: verify pump stall guard fires under simulated stall; confirm ambient temp surfaces in HomeKit + HA; confirm schedule CRUD no longer reloads globally <!-- sleepypod:install-start --> <!-- 🛑 AUTO-GENERATED — sleepypod-bot owns this block. Hello fellow agent / curious human! Anything between the sleepypod:install-start and sleepypod:install-end markers gets overwritten on the next CI run. Want to change what's here? Edit .github/workflows/build.yml instead. 💤 --> <details open> <summary>🛏️ <strong>Beam this PR onto a sleepypod</strong> ✨</summary> Pick the snippet that matches your pod. **Already-installed pods can't use the curl bootstraps below** — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use `sp-update` instead; it's on disk and opens WAN as its first step. 🔄 **Already on a sleepypod** (most common — review a PR on a running pod): ```bash sudo sp-update dev ``` 🚀 **Fresh pod / first install** — bootstraps from GitHub, rebuilds every push: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/dev/scripts/install \ | sudo bash -s -- --branch dev ``` 🎯 **Pin to this exact build** ([run 26432499948](https://github.com/sleepypod/core/actions/runs/26432499948) · `3b90e51`) — fresh-pod path, locked to one CI artifact for reproducible review: ```bash curl -fsSL https://raw.githubusercontent.com/sleepypod/core/3b90e51870360b73a1565740be852e939dcab591/scripts/install \ | sudo bash -s -- \ --branch dev \ --artifact-url 'https://nightly.link/sleepypod/core/actions/runs/26432499948/sleepypod-core.zip' ``` 🧊 Artifact: [`sleepypod-core`](https://github.com/sleepypod/core/actions/runs/26432499948/artifacts/7209005630) · self-destructs in 30 days 💥 </details> <!-- sleepypod:install-end -->
|
🎉 This PR is included in version 2.3.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary
Bundles the four issues NAAAA hit installing on a frank-less Pod 5 (reported in Discord, 5/21).
int | None(PEP 604) requires 3.10+. Replaced withOptional[int]. Module pinsrequires-python = ">=3.9,<3.11", Pod 5 ships 3.9.9, so the service was crash-looping at import time on every restart. Regression from PR fix: Python sidecar crash resilience + data integrity (#325) #483.xtables.lockpermission denied —setInternetAccess(iptables -F) crashed the first time a user toggled WAN. Service runs asUser=sleepypodwithCAP_NET_ADMIN, but the default/run/xtables.lockisroot:root 0600and the cap doesn't bypass file mode. Fix:Environment="XTABLES_LOCKFILE=/run/dac/xtables.lock"(theRuntimeDirectory=dacis owned by the service user). For pods already installed pre-fix,sp-updatewrites the same env via an idempotentsleepypod.service.d/iptables-lockfile.confdrop-in so OTA self-heals./opt/eight/bin/frank.shwhich we patch tocd /persistent/biometrics(tmpfs). Newer variants drop the shim —frank.serviceExecStartsfrankenfirmwaredirectly, our cd-patch no-ops, and ~1 GB/day of RAW frames land on/persistenteMMC. AddWorkingDirectory=/persistent/biometricsto thefrank.servicedrop-in: no-op on shim pods (shellcdstill wins), real redirect on shimless pods."Warning: /opt/eight/bin/frank.sh not found — skipping patch (not a Pod?)"and"Warning: dac.sock not found, using default: ..."made working installs look broken. Replaced with informational lines that explain what's happening.Adds
modules/environment-monitor/README.md(first per-module README) covering what writes tobed_temp/freezer_temp, whyfreezer_tempcan be empty on cover-only Pod 5, debug commands, and a note about the Python 3.9 syntax constraint.Test plan
Verified live on eight-pod (Pod 5 J55, frank-shim variant):
frank.servicedrop-in parses,daemon-reloadclean,systemctl restart frank.servicesucceeds,frankenfirmwarecwd remains/persistent/biometrics(shellcdwins as expected)systemctl show frank.service -p WorkingDirectory→/persistent/biometricsAmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAWas the real service viasystemd-run --uid=sleepypod:XTABLES_LOCKFILE→Fatal: can't open lock file /run/xtables.lock: Permission denied(NAAAA's exact error)XTABLES_LOCKFILE=/run/dac-test/xtables.lock→iptables -L OUTPUTsucceeds, lockfile created at/run/dac-test/xtables.lockowned by sleepypodCI / unit tests:
pnpm test src/server/routers/tests/system.test.ts— 34/34 passpnpm test src/hardware/tests/iptablesCheck.test.ts— 18/18 passbash -nclean onscripts/install,scripts/bin/sp-update,scripts/bin/sp-uninstall,scripts/pod/detect,scripts/lib/biometrics-archiver-helpers_safe_freezer_centidegreessentinels, range gate, accept path) passes after theOptional[int]changeFor users picking up the fix:
sp-updatewrites the drop-in, no full re-install neededsp-uninstallremovessleepypod.service.d/along with the rest🛏️ Beam this PR onto a sleepypod ✨
Pick the snippet that matches your pod. Already-installed pods can't use the curl bootstraps below — sleepypod's egress firewall DROPs github.com, and the script that knows how to unblock WAN is the very file curl is trying to fetch. Use
sp-updateinstead; it's on disk and opens WAN as its first step.🔄 Already on a sleepypod (most common — review a PR on a running pod):
🚀 Fresh pod / first install — bootstraps from GitHub, rebuilds every push:
curl -fsSL https://raw.githubusercontent.com/sleepypod/core/worktree-naaa/scripts/install \ | sudo bash -s -- --branch worktree-naaa🎯 Pin to this exact build (run 26418923280 ·
19a7fba) — fresh-pod path, locked to one CI artifact for reproducible review:🧊 Artifact:
sleepypod-core· self-destructs in 30 days 💥Summary by CodeRabbit
Bug Fixes
Documentation
Chores