diff --git a/TODO.md b/TODO.md index 7ae56597..b234e612 100644 --- a/TODO.md +++ b/TODO.md @@ -698,6 +698,20 @@ outside the example's new README: unreachable from the LAN while localhost and Tailscale work. Fix: `sudo ufw allow from 192.168.47.0/24 to any port 9010 proto tcp` +- [ ] **Move the APNs `.p8` into the secret store — and teach the store to hold a foreign secret.** + Agreed 2026-08-19 when the key went into `.env` as an interim. The store's own argument applies + almost verbatim: `.env` is auto-loaded by bun into every PM2 process, so a credential there is + readable from `/proc//environ` of twenty processes — `officer-music` and `officer-pty` + would both be able to read a key that can push to your apps forever and does not expire. + + **The blocker is not a purpose string.** `getKey(purpose)` GENERATES a key and returns it; there + is no setter, because everything in the store so far is a key the platform minted itself. The + `.p8`, the APNs Key ID, the Team ID and the FCM service-account JSON are all supplied from + outside, so this needs a `putSecret(purpose, value)` and a decision about who may call it. + + Same applies to `DISCORD_WEBHOOK_URL` and every plugin credential still read from env. + See `docs/secret-store.md` and `docs/push-notifications.md`. + ## Backburner - [ ] **Music tagging feature** (`/music`, Mp3tag-inspired). v1 was scoped as a two-panel workspace — diff --git a/docs/push-notifications.md b/docs/push-notifications.md index 2f6b696e..76d1de49 100644 --- a/docs/push-notifications.md +++ b/docs/push-notifications.md @@ -205,6 +205,17 @@ Env only, on the sidecar, never in the platform process and never in the databas The `.p8` does not expire and can push to your apps forever. Treat it as spending-grade. +**Env is the interim, not the destination.** These belong in the secret store +(`src/databases/officer_db/src/secret-store.ts`, built 2026-08-13), and for the reason that store exists: +bun auto-loads `.env` into every PM2 process, so a key there is readable from `/proc//environ` of +twenty processes — `officer-music` has no business being able to read a credential that can push to your +apps forever. + +It cannot move there yet, and the obstacle is not a missing purpose string. `getKey(purpose)` **generates** +a key; there is no setter, because every secret in the store so far is one the platform minted itself. The +`.p8` and the FCM service account come from outside, so holding them needs a `putSecret` first. Tracked in +`TODO.md`. + `APNS_ENV` appeared in the first design's table and is deliberately **not** implemented: the host is chosen per device from the `environment` column, not per process, so one server pushes to a TestFlight build and a release build at the same time.