carlos / platform Public

appconfig: TCP-addr route config lands in the exec DataDir, readable by every exec child Feature Request closed

Companion to the exec-backend isolation issue (same finding, teamtito/titogo#2527 / #2529). This one is smaller and would close the worst part on its own.

The gap

carlos env exec-delivery file exists for TCP-addr routes CARLOS proxies to but never spawns ("titogo's router fleet", per its help text). For those routes the app's own supervisor reads the delivered file — in our case systemd, as root, via an EnvironmentFile= drop-in. Nothing running as carlos ever needs to read it.

Yet it is written by the agent (internal/appconfig/syncexec.go:656, os.WriteFile(tmp, rendered, 0o600) + rename) into the exec backend's flat DataDir, as carlos:carlos. Every exec child on the box runs as carlos, so every tenant instance — of any app — can read every TCP-addr route's config. For our relay route that file carries every platform credential we have.

Meanwhile unit-backed routes already get their config the right way: carlos-config-sync.service runs as root and writes /etc/carlos/apps/<host>.env root-0600 (internal/appconfig/sync.go:28; backend_unit.go:176: "unreadable to the agent by design").

What we are asking for

Let a TCP-addr route opt into that path: config rendered by the root sync unit into /etc/carlos/apps/<host>.env (or another root-owned location), never into the agent's DataDir. Something like carlos env exec-delivery … root or a per-route flag; we would then point titogo-relay.service's drop-in at it and turn the flat-file delivery off.

What we want to avoid is the workaround: moving the relay's secrets back to a hand-written env file on the box and switching delivery off. That loses carlos secrets provenance — the exact thing we were told not to do on 2026-08-13 — and we would rather ask for the platform shape than absorb it.

Today: sudo -u carlos test -r /data/connect.go.tito.io.config.env → yes, on carlos-tito-ie, 2026-09-10.

Cross-reference: the isolated-exec design for #126 (branch worktree-isolated-exec-spec, docs/superpowers/specs/2026-09-11-isolated-exec-design.md) closes the read this discussion opens with. An isolated instance sees an empty /data with only its own directory bound in, so an opted-in addr route's /data/<host>.config.env is invisible to it. The file itself stays where it is, because titogo-relay.service reads it there.

The request here, root-rendered delivery into /etc/carlos/apps for addr routes so that the agent cannot read it either, is not part of that spec (its section 11 lists it as a separate, smaller change). This discussion stays open for that.

Closing as superseded by #126. Paul agreed on 2026-09-28.

Moving the relay's config to a root-owned /etc/carlos/apps/<host>.env would not take the secrets out of reach of anything that can read the file today:

  • Isolated instances (#126's nested layout) already can't see /data/<host>.config.env: only their own directory is bound in.
  • The agent holds CARLOS_SECRETS_KEY and decrypts these very secrets to write the file. It still needs the key for socket-exec routes, so a root-rendered file doesn't keep anything from it.
  • Flat instances run as carlos beside the agent. They can most likely read the agent's environment, which carries the key, and they can reach the instance metadata service, and through it the sealed bundles. That is every secret on the box, not just this file (details on #126).

So the thing that actually closes this is the #126 migration to the nested layout. Once every instance on a box is nested, the relay's secrets are readable only by titogo-relay.service and by the agent, which has them anyway. Until then, hand-written env files wouldn't help either, so keep the relay on exec-delivery file and keep carlos secrets provenance.

If "decrypted secrets never at rest in a carlos-readable file" becomes a goal in its own right, it needs a design where the agent never holds the key, and that is much bigger than this ask. Reopen with that framing if you want it.