Skip to content

fix(cli): make fresh setup and logout use canonical cloud auth - #441

Open
miyaontherelay wants to merge 4 commits into
mainfrom
fix/440-credential-store-setup
Open

fix(cli): make fresh setup and logout use canonical cloud auth#441
miyaontherelay wants to merge 4 commits into
mainfrom
fix/440-credential-store-setup

Conversation

@miyaontherelay

Copy link
Copy Markdown
Contributor

Closes #440.

What changed

  • make hosted setup perform POST /api/v1/workspacesGET /api/v1/workspaces/{cloudId}/resolve → delegated-token mint against the returned relayfileWorkspaceId
  • add actionable create/provision guidance for workspace_not_found instead of surfacing a raw 404
  • make workspace create, workspace list, and workspace current use the canonical hosted session when configured
  • preserve explicit precedence: command flag, environment token, canonical hosted session, self-hosted ~/.relayfile/credentials.json
  • stop hosted login/delegated bootstrap from deleting the legitimate self-hosted credentials file
  • make logout revoke the canonical hosted refresh token, clear cloud-auth.json and cloud-identity.json, then clear Relayfile-local credentials
  • retain the hosted credential and return an error without a success message when revocation cannot be verified

The two credential stores remain independent. This change does not migrate, move, or rewrite credentials.json into the hosted store.

Red before green

The new regressions failed on origin/main with the observed behavior:

  • fresh setup called create, skipped resolve, and received 404 workspace_not_found from delegated-token mint
  • hosted workspace list --names-only printed nothing
  • hosted workspace create made zero Cloud requests when a self-hosted file also existed
  • logout made no revoke call and left the canonical session readable
  • the raw setup error did not name the create/provision step

The self-hosted-only guard passed on main and byte-compares credentials.json before/after workspace create; it remains green after this change.

Verification

  • go test ./cmd/relayfile-cli -count=1
  • go test ./... -count=1
  • go vet ./...
  • git diff --check

The focused logout coverage reads the stored auth state after the command, asserts the refresh token was revoked, and verifies both canonical auth and identity state are gone. A separate negative test verifies an unconfirmed revoke leaves the session available for retry and prints no false success result.

Session-Id: 01a03068-91e3-7a21-9de9-8fed7d680d3e
@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Review Change Stack

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 39684048-d181-431f-b52a-425961aa3c82

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/440-credential-store-setup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@miyaontherelay

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Head commit changed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Session-Id: 01a03068-91e3-7a21-9de9-8fed7d680d3e
Session-Id: 01a03068-91e3-7a21-9de9-8fed7d680d3e
@miyaontherelay

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions

github-actions Bot commented Aug 23, 2026

Copy link
Copy Markdown

Relayfile Eval Review

Run: .relayfile/evals/runs/2026-08-23T22-14-25-883Z-HEAD-provider
Mode: provider
Git SHA: cee4508

Passed: 4 | Needs human: 0 | Reviewable: 0 | Missing output: 0 | Failed: 0 | Skipped: 0

Human Review Cases

No reviewable human-review cases captured Relayfile output.

@miyaontherelay

Copy link
Copy Markdown
Contributor Author

Independent review of exact head 6db46adf01c2e9d89cd13e88536f3aa9eb002860.

Finding

[P1] Do not treat a self-hosted catalog row as the hosted Cloud workspacecmd/relayfile-cli/main.go:2354

ensureWorkspaceForSetup consults the shared local catalog by display name before making any Cloud request. That catalog also contains the legitimate self-hosted workspaces this PR promises to preserve. If an existing self-hosted user has, for example, {Name: "demo", ID: "self-hosted-demo"}, then logs into Agent Relay Cloud and runs relayfile setup --workspace demo or relayfile workspace create demo, this branch reuses the self-hosted row. Because it has no RelayWorkspaceID, the CLI calls GET /api/v1/workspaces/self-hosted-demo/resolve; it never sends POST /api/v1/workspaces, and Cloud returns the same workspace-not-found failure this PR is intended to eliminate. A row with stale identifiers from another hosted session has the same provenance problem.

This violates the documented hosted-over-self-hosted precedence and makes coexistence fail for a plausible upgrade path (the same project/workspace name in both modes). Hosted setup/create needs to establish identity from the current Cloud session—matching Cloud IDs/bindings, not a name-only local row—and create when no current-Cloud match exists, while leaving the self-hosted row/file intact.

The new coexistence test at main_test.go:1262 writes self-hosted credentials but no colliding catalog row, so it cannot fire on this defect. The new collision test at main_test.go:8647 covers only active-marker selection, not setup/create. A must-fire regression should seed the existing self-hosted row and assert the Cloud request order remains create → resolve → mint and that the legacy credentials bytes are unchanged.

Other audited constraints

No further findings in the requested areas:

  • Auth precedence is explicitly documented as flag → RELAYFILE_TOKEN → canonical hosted session → self-hosted credentials, and malformed on-disk hosted auth fails visibly.
  • For a non-colliding fresh user, setup now performs create → resolve → mint with the Relayfile workspace ID and gives an actionable create/provisioning error instead of a raw mint 404.
  • Logout revokes the exact stored refresh token, retains the canonical session on network/non-2xx failure, removes auth/identity only after verified 2xx revocation, and does not claim Cloud revocation for self-host-only logout.
  • Credential writes remain atomic at 0600 under 0700-created credential/lock directories; the existing refresh persistence test asserts 0600.
  • The self-host-only compatibility test byte-compares credentials.json; the fresh setup and logout tests assert specific requests/state transitions and are must-fire for their covered regressions.

Validation

  • Serial go test ./... -count=1: pass.
  • The ten new auth/workspace regression tests run directly: pass.
  • go vet ./...: pass.
  • git diff --check origin/main...HEAD: pass.
  • Exact-head workflow runs all completed successfully: CI, Contract, Relayfile Evals.

I did not approve, merge, or modify the branch.

@miyaontherelay

Copy link
Copy Markdown
Contributor Author

Accepted as blocking: the current hosted setup path can reuse a name-only row from the shared local catalog, including a legitimate self-hosted row, before establishing identity from the current Cloud session. That violates hosted-over-self-hosted precedence and can reproduce the original workspace-not-found failure.

I am adding a must-fire regression that seeds the colliding self-hosted catalog row, asserts the hosted request sequence is create → resolve → mint, and byte-compares the legacy credentials.json. I will demonstrate it failing on current head before changing setup/create so only current-Cloud identity/bindings can be reused; otherwise Cloud creation will occur without modifying the legacy row or credentials file.

No merge action will be taken.

@miyaontherelay

Copy link
Copy Markdown
Contributor Author

Red regression captured on exact pre-fix head 6db46ad:

go test ./cmd/relayfile-cli -run '^TestHostedSetupIgnoresCollidingSelfHostedCatalogRow$' -count=1

fails with:

resolve the workspace after the create step: Cloud did not provision a Relayfile workspace for self-hosted-demo: http 404 workspace_not_found: Workspace not found
requests: [GET /api/v1/workspaces/self-hosted-demo/resolve]

The test seeds both the legacy credentials.json and {Name:"demo", ID:"self-hosted-demo"} catalog row. It byte-compares the credentials immediately after setup returns, then requires create → resolve → mint and verifies the original self-hosted catalog row remains unchanged alongside the hosted row.

Session-Id: 01a03068-91e3-7a21-9de9-8fed7d680d3e
@miyaontherelay

Copy link
Copy Markdown
Contributor Author

P1 fixed in 86cf0b7 (fix(cli): verify hosted workspace catalog bindings).

The setup/create identity decision no longer calls Cloud with a name-only local catalog row:

  • A self-hosted/name-only row is ignored for hosted identity, so setup goes directly through create → resolve → mint.
  • A stored hosted candidate is reusable only after GET /api/v1/workspaces from the current Cloud session confirms its exact Cloud workspace ID; then the ID is resolved again before minting.
  • A stale hosted binding absent from the current session is not reused; setup creates a new hosted workspace.
  • Cloud-sourced catalog persistence matches exact Cloud/Relayfile IDs before names and does not overwrite a colliding self-hosted or stale row. Active hosted selection scans every same-name row for an exact current Cloud ID.
  • The canonical self-hosted credentials.json remains byte-for-byte unchanged.

Must-fire regression:

  • Seeds credentials.json and {Name:"demo", ID:"self-hosted-demo"}.
  • Asserts exact Cloud request order POST createGET resolvePOST mint.
  • Byte-compares credentials.json after setup.
  • Deep-compares the original self-hosted catalog row and requires a separate hosted row.
  • Failed on pre-fix head with only GET /api/v1/workspaces/self-hosted-demo/resolve and workspace_not_found; passes on 86cf0b7.

Additional coverage verifies current-session exact-ID reuse, stale-binding replacement, repeat messaging-only provisioning through the authoritative list, and active hosted selection across duplicate display names.

Local validation on the new head:

  • go test -p 1 ./... -count=1 — pass
  • go vet ./... — pass
  • git diff --check — pass

Exact-head CI is now pending. No merge action was taken.

@miyaontherelay

Copy link
Copy Markdown
Contributor Author

Final exact-head validation for the P1 fix (86cf0b7dc1a93dafbbab0357016dfb89364391ce):

  • CI run 32669850026 — success. Go Test, SDK Typecheck/Test, Workers check, Client Typecheck/Test, Go Build, and E2E all completed successfully.
  • Contract run 32669850027 — success on attempt 1. Go tests, core/SDK builds, and contract validation all passed.
  • Relayfile Evals run 32669850017 — success on attempt 1. Eval summary: 4 passed, 0 needs human, 0 reviewable, 0 missing output, 0 failed, 0 skipped.

Review-body audit after the final push:

  • The independent P1 body was read in full and is addressed by 86cf0b7 plus the red/green regression recorded above.
  • GitHub reports 0 formal reviews, 0 review threads (therefore 0 unresolved threads), and 0 inline review comments.
  • CodeRabbit's only review responses remain “Head commit changed” and “Review rate limited”; it produced no review content. No Devin or other bot review body/thread is present.

The PR is open, GitHub reports merge state CLEAN, and the local branch is clean and tracks origin. No merge, publish, release, or tag was performed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fresh-user setup cannot complete: login and workspace commands use different credential stores

1 participant