apns credentials belong in the secret store, and cannot go there yet
env is the interim. the secret store's own reasoning applies almost verbatim: bun auto-loads .env into every pm2 process, so the .p8 is readable from /proc/<pid>/environ of twenty of them. officer-music has no business being able to read a key that can push to the owner's apps forever and never expires. the obstacle is not a missing purpose string, which is what it looks like from the outside. getKey(purpose) GENERATES a key and returns it — there is no setter, because everything in the store so far is something the platform minted itself. the .p8, the key id, the team id and the fcm service account all come from outside, so this needs putSecret and a decision about who may call it. same for DISCORD_WEBHOOK_URL and every plugin credential still in env. recorded in TODO.md and in the design doc rather than left as an intention.
This commit is contained in:
@@ -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/<pid>/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 —
|
||||
|
||||
@@ -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/<pid>/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.
|
||||
|
||||
Reference in New Issue
Block a user