Secrets and env never reach hand-built unit routes (the Woodstar shape): nothing is rendered, and the only path left is SSM Issue open
Filed from the Woodstar community-relays release (woodstar #121, shipped 2026-09-06 as f342862). The release itself went fine. The feature it carries is dark on prod because a secret set through the door never reached the process, and the only way to get it there today is an SSM script against the box. Paul's ruling for this thread: we do not want to be using SSM for any of this as much as possible. That is the ask, more than the one secret.
What happened
carlos secrets set -app woodstar-relay WOODSTAR_COMMUNITY_PROVISION_TOKEN=… was run before the release; carlos secrets list -app woodstar-relay shows the key. The relay reads it with os.Getenv at startup and enables community creation when it is present. After the ship and the box's adoption restart (ExecMainStartTimestamp 21:17:18Z, new binary confirmed), the live relay answers GET /api/communities with "creation_enabled": false.
On the box (read-only SSM, ls /etc/carlos/apps/):
home.kass.training.env # the only file — a carlos-app@ routeand systemctl cat woodstar-relay.service:
Environment=WOODSTAR_APPLE_APP_ID=J5NDF5PCD8.app.woodstar.ios
ExecStart=/opt/carlos/live/woodstar-relay.bdm.oncarlos.com -addr 127.0.0.1:8451 …No EnvironmentFile, no rendered woodstar-relay.bdm.oncarlos.com.env. carlos routes -app woodstar-relay shows the route as KIND instance, UPSTREAM 127.0.0.1:8451, ENVIRONMENT —, STATUS ready, HEALTH Running.
Why
This is the cutover runbook's CATCH 2 (platform docs/superpowers/plans/2026-08-16-woodstar-cutover-runbook.md), filed there as "follow-up gap to file" and never filed. appconfig.Sync renders /etc/carlos/apps/<host>.env for carlos-app@<host> units. The seven Woodstar units are hand-built (ops/woodstar/d24-setup.sh), named woodstar-<svc>.service, and the interim fix at cutover was a unit-level Environment= line for the one var they needed then. So for this route shape:
env set/secrets setsucceed and list, but render nothing the unit reads (this is one step earlier than #16, where a file IS rendered and the process is not bounced);- the
-config-onlyroot pass andcarlos env synchave nothing to deliver to; - the only remaining path is a root drop-in written over SSM, which means a human handling the plaintext secret on the box, which is exactly the shape the platform exists to remove.
The platform's own advice already knows about this route shape (adopt_advice.go: "stamp backing='unit' … the unit reads /etc/carlos/apps instead", issue #14), so the pieces are there; they do not meet for a unit that is not carlos-app@.
Asks
- Config delivery for unit-backed routes that are not
carlos-app@. The simplest version I can see: Sync renders<host>.envfor every route the box serves from a unit, whatever the unit is called, and the adoption pass'stry-restartalready knows which unit it stamped. The Woodstar units would then need one line each (EnvironmentFile=-/etc/carlos/apps/%N.envor the host name) added once, by the same setup script that made them — after which no secret ever needs a person on the box again. - Say so at set time.
secrets set/env setnow warn when a file is delivered that nothing will bounce (#16,b8fd86c). The same note is needed when NOTHING is delivered: "no route of this app on this box reads a rendered env file; the value is stored, not delivered." Today the door reports success and the app silently runs without the value. That is how this shipped believing the credential was in place: the PR text said "sealed into World config", and by every signal the console gives, it was. - A way to ask what a process is running. Same as #16's third point.
carlos routessaysRunning; nothing says "with which config generation". A column, orcarlos env list --delivered, would have made the gap visible before the ship rather than after.
What I did not do
I did not write a drop-in over SSM. The sealed value cannot be read back, so that path also means minting a fresh instance-scoped credential and pasting it into a root file by hand. Holding for the platform answer instead; the feature stays dark on prod until then, which is fine — nothing existing is affected, the Communities screen just shows no create form.
Related: #16 (delivered but not bounced), #14 (backing='unit' stamp), the cutover runbook CATCH 2.