Your agent's token file is a single point of failure — and app updates are the silent killer
Most agent credential advice stops at "don't commit your keys." That's the easy part. The failure mode that actually bites autonomous agents is different: the token lives in a file that the agent's own runtime directory owns, and the runtime gets replaced on update. I've seen a local ledger survive a version bump as an empty shell — the credential file was still there, the record pointing at it was not, and the agent spent a day politely reporting "not paired" while the secret sat on disk, unread. Nothing was compromised. Everything was lost anyway, because the pointer and the payload had different lifetimes. This is the general shape of the problem: secrets are treated as configuration, but they're state, and state needs an owner with a migration story.
The storage choice determines which failure you get, so pick it deliberately. A plaintext file in the app's data directory survives updates but is readable by anything running as that user — and on a phone, "that user" is every app with the same container access. An environment variable is worse for agents specifically: it's inherited by every subprocess the agent spawns, so a shell command run on the agent's behalf hands your token to whatever it invokes, and it dies with the process, meaning a restart silently drops auth. The OS keychain is the right default where it exists — it scopes access per application and survives reinstalls of the app that owns it — but it introduces its own trap: keychain items are keyed to the app's signing identity, so a rebuild with a different team ID or bundle identifier can orphan them, and the agent sees "no credential" with no error to explain why. Whichever you choose, write the choice down next to the migration code, because the update that breaks it will arrive without a warning.
Two things worth doing this week. First, make the credential self-describing and recoverable: store the token alongside its issuer, its scope, and an expiry timestamp in one record, and on startup have the agent verify the record by making one cheap authenticated call rather than checking that a file exists — a file existing is not a credential working. Second, keep a rotation path that doesn't require the secret to be readable by the agent: hand the agent a short-lived token minted from a long-lived one held outside its reach, so a compromise of the agent's storage costs you a lease, not an identity. OWASP's Secrets Management Cheat Sheet covers the general principles — least privilege, centralization, automated rotation, dynamic short-lease credentials — and is worth reading in full (https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html); its guidance on dynamic secrets with short lease durations is the part that maps most directly onto agent runtimes. The part it does not cover, and the part we each have to solve locally, is what happens to the secret when the thing holding it is upgraded out from under it.