Runtime Identity Is a Data Boundary
Runtime identity is a data boundary
If a local daemon owns durable state, its runtime identity is not a display label. It is a boundary. Cross it accidentally and one environment can read another environment's database, sockets, token cache, search index, or keychain entries.
This showed up in Mxr because the same person is both developer and user. The installed mxr daemon is production mail. cargo run is development mail. If both processes share one config directory, the same socket, the same SQLite database, the same Tantivy index, and the same credential refs, "just testing a branch" can mutate real mail.
The fix is to make identity resolve first, then derive everything else from it.
The rule
Pick a runtime identity before opening any durable handle:
- config directory
- data directory
- SQLite database
- search index
- model/cache directories
- IPC socket
- PID files
- bridge token and bridge port files
- OAuth token roots
- OS-keychain service names or refs
In mxr, release builds default to mxr. Debug builds default to mxr-dev. MXR_INSTANCE overrides both. Demo mode uses mxr-demo.
That gives a boring but valuable invariant:
installed mxr -> instance mxr -> production local state
cargo run -> instance mxr-dev -> development local state
mxr demo -> instance mxr-demo -> demo local state
What the July 2026 incident proved
A production mxr daemon once failed during startup because an IMAP password
read triggered interactive macOS Keychain approval. Running the same source with
cargo run did not reproduce the account state: the debug build correctly
opened the empty mxr-dev profile. Only an explicit MXR_INSTANCE=mxr crossed
into production state and reproduced the credential problem.
That was not configuration drift. It was the boundary working. A source-build
fallback is not equivalent to the installed program unless it deliberately
keeps the same runtime identity. The incident also exposed a separate bug:
credential access was happening too early in daemon startup.
Current mxr keeps the identity boundary and changes the credential path.
IMAP/SMTP passwords resolve from the active profile's secrets.toml first,
with the keychain as an optional fallback. Resolution happens when the account
connects or syncs, so one unreadable credential no longer prevents the daemon
or other accounts from starting.
The subtle part is credentials
Files are obvious. Secret stores are easier to forget.
A copied dev config may still contain password_ref = "mxr/work-imap". If the daemon passes that string directly to the OS keychain, the dev daemon reads the production password. The config looks isolated while the secret store is shared.
So non-production instances need credential scoping too. In mxr, the
disk-first password store lives inside the instance's config directory.
Production preserves legacy keychain names for compatibility, while
non-production prefixes credential refs and Gmail OAuth keychain service names
with the active instance.
Why this generalises
Any local-first tool with a daemon and multiple clients needs this once it has both "real user state" and "developer/test/demo state." The boundary has to include every client:
- CLI autostart must spawn the daemon for the same identity.
- TUI autostart must do the same.
- Web dev proxy must discover the bridge files for the same identity.
- Status output should print the resolved identity and paths so humans can verify what they are touching.
If one client escapes the identity, the boundary is fake.
Code grounding
In mxr, the code truth is:
crates/config/src/resolve.rsownsapp_instance_name(),config_dir(),data_dir(),socket_path(),token_dir(),secrets_file_path(), bridge paths, and credential scoping.crates/daemon/src/provider_credentials.rsscopes IMAP/SMTP refs, resolves passwords lazily from disk before the keychain, and injects profile-scoped Gmail/Outlook token storage.crates/daemon/src/server.rsandcrates/tui/src/ipc.rspassdaemon --instance <current-instance>during autostart.apps/web/vite.config.tsreadsMXR_INSTANCEand defaults dev web work tomxr-dev.
See also
- Mxr - the concrete implementation
- The Local Daemon Pattern - where instance keys already mattered for sockets
- How Processes Talk to Each Other - the IPC path depends on runtime identity