Summary
relayfile login writes to exactly one credentials file depending on which sub-flow runs, never both. Operators who use the cloud browser flow get a fresh cloud-credentials.json but a stale credentials.json; operators who pass --token / --api-key get the opposite. Any consumer reading the "wrong" file after a re-login holds stale auth, which is the same failure mode that motivated #178 / #179.
Field shape on disk today:
~/.relayfile/credentials.json: server, token, updatedAt (single bearer)
~/.relayfile/cloud-credentials.json: apiUrl, accessToken, accessTokenExpiresAt, refreshToken, refreshTokenExpiresAt, updatedAt (full OAuth session, separate updatedAt)
Observed on one machine after a single relayfile login:
credentials.json mtime: May 21 13:32:06 2026
cloud-credentials.json mtime: May 21 13:31:25 2026
The two files drift on every login, confirming they are not co-refreshed.
Code references
cmd/relayfile-cli/main.go:1782 — runLogin entry point
cmd/relayfile-cli/main.go:1816 — --token path calls loginWithAPIKey only
cmd/relayfile-cli/main.go:1829 — --api-key interactive path calls loginWithAPIKey only
cmd/relayfile-cli/main.go:1837 — default cloud-browser path calls ensureCloudCredentials only
cmd/relayfile-cli/main.go:895 — ensureCloudCredentials → saveCloudCredentials (writes cloud-credentials.json only)
cmd/relayfile-cli/main.go:1870 — loginWithAPIKey → saveCredentials (writes credentials.json only)
cmd/relayfile-cli/main_test.go:2672 references a prior bug "where relayfile login only refreshed cloud-credentials.json even when a workspace was already registered locally" — that test guards the workspace token but not this credentials.json ↔ cloud-credentials.json split.
Expected vs Actual
- Expected: After any successful
relayfile login, both credential files reflect the new session (or one file becomes the canonical source and the other is removed / treated as cache).
- Actual: Exactly one file is rewritten; the other is left at its prior
updatedAt. Daemons/tools reading the stale file behave as if no re-login occurred.
Suggested fix direction
- Co-write: in the cloud-browser path, after
ensureCloudCredentials, also derive and persist a credentials.json entry pointing at the cloud-issued bearer (or invalidate credentials.json so callers fall through to the cloud session).
- Symmetric in
loginWithAPIKey: if a cloud session exists, refuse silently overwriting it, or clear it.
- Add a test that asserts both file mtimes advance after a single
relayfile login invocation in each mode.
Related
Summary
relayfile loginwrites to exactly one credentials file depending on which sub-flow runs, never both. Operators who use the cloud browser flow get a freshcloud-credentials.jsonbut a stalecredentials.json; operators who pass--token/--api-keyget the opposite. Any consumer reading the "wrong" file after a re-login holds stale auth, which is the same failure mode that motivated #178 / #179.Field shape on disk today:
~/.relayfile/credentials.json:server,token,updatedAt(single bearer)~/.relayfile/cloud-credentials.json:apiUrl,accessToken,accessTokenExpiresAt,refreshToken,refreshTokenExpiresAt,updatedAt(full OAuth session, separateupdatedAt)Observed on one machine after a single
relayfile login:credentials.jsonmtime:May 21 13:32:06 2026cloud-credentials.jsonmtime:May 21 13:31:25 2026The two files drift on every login, confirming they are not co-refreshed.
Code references
cmd/relayfile-cli/main.go:1782—runLoginentry pointcmd/relayfile-cli/main.go:1816—--tokenpath callsloginWithAPIKeyonlycmd/relayfile-cli/main.go:1829—--api-keyinteractive path callsloginWithAPIKeyonlycmd/relayfile-cli/main.go:1837— default cloud-browser path callsensureCloudCredentialsonlycmd/relayfile-cli/main.go:895—ensureCloudCredentials→saveCloudCredentials(writescloud-credentials.jsononly)cmd/relayfile-cli/main.go:1870—loginWithAPIKey→saveCredentials(writescredentials.jsononly)cmd/relayfile-cli/main_test.go:2672references a prior bug "whererelayfile loginonly refreshed cloud-credentials.json even when a workspace was already registered locally" — that test guards the workspace token but not thiscredentials.json↔cloud-credentials.jsonsplit.Expected vs Actual
relayfile login, both credential files reflect the new session (or one file becomes the canonical source and the other is removed / treated as cache).updatedAt. Daemons/tools reading the stale file behave as if no re-login occurred.Suggested fix direction
ensureCloudCredentials, also derive and persist acredentials.jsonentry pointing at the cloud-issued bearer (or invalidatecredentials.jsonso callers fall through to the cloud session).loginWithAPIKey: if a cloud session exists, refuse silently overwriting it, or clear it.relayfile logininvocation in each mode.Related