Skip to content

Use delegated relayfile token refresh#274

Merged
khaliqgant merged 1 commit into
mainfrom
auth-unify-direct-http-2108
Jun 13, 2026
Merged

Use delegated relayfile token refresh#274
khaliqgant merged 1 commit into
mainfrom
auth-unify-direct-http-2108

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Jun 13, 2026

Copy link
Copy Markdown
Member

Summary

  • consume the delegated relayfile TokenPair bundle from cloud and persist it as relayfile-owned credentials
  • replace cloud /join renewal with direct relayauth /v1/tokens/refresh rotation from the creds file
  • extend relayfile-mount --creds-file to rotate delegated TokenPairs and fail fast on rejected/expired delegation
  • keep cloud control calls on cloud session material while data-plane calls use relay_pa_ access tokens

Validation

  • go test ./cmd/relayfile-cli ./cmd/relayfile-mount ./internal/delegatedauth
  • go test ./...
  • git diff --check
  • legacy /join helper grep clean

Notes

  • Depends on merged cloud#2125 bundle contract: relayfileUrl, relayauthUrl, relayfileWorkspaceId, relayfileToken, relayfileTokenExpiresAt, relayfileRefreshToken, relayfileRefreshTokenExpiresAt, relayfileScopes, delegationNotAfter, relayfileMountPaths.
  • Follow-up operator gate after merge: full E2E artifact proving delegated auth loop + product run with agent-relay poisoned/no CLI fallback.

Review in cubic

@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@khaliqgant, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 48 minutes and 56 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8b35fa37-96f7-4569-9d2f-2f1fbdaaf19a

📥 Commits

Reviewing files that changed from the base of the PR and between 27583cb and e12a638.

📒 Files selected for processing (9)
  • cmd/relayfile-cli/background_test.go
  • cmd/relayfile-cli/main.go
  • cmd/relayfile-cli/main_test.go
  • cmd/relayfile-cli/productized_cloud_mount_e2e_test.go
  • cmd/relayfile-cli/setup_e2e_test.go
  • cmd/relayfile-cli/writeback_daemon_test.go
  • cmd/relayfile-mount/main.go
  • internal/delegatedauth/delegatedauth.go
  • internal/delegatedauth/delegatedauth_test.go
📝 Walkthrough

Walkthrough

This PR replaces Cloud "join" tokens with delegated relayfile credentials minted by Cloud, persisted locally, and refreshed via HTTP token endpoints. Delegated credentials flow through setup, workspace join, integration operations, and mount-loop operations to eliminate join-based token bootstrapping and improve credential lifecycle management.

Changes

Delegated Credentials Implementation

Layer / File(s) Summary
Delegated Auth Package
internal/delegatedauth/delegatedauth.go, internal/delegatedauth/delegatedauth_test.go
New delegatedauth package introduces Bundle and TokenPair types, atomic file persistence, HTTP refresh via /v1/tokens/refresh, and error classification. Tests validate token rotation persistence and refresh rejection without credential mutation.
Setup and Workspace Join Delegated Bootstrapping
cmd/relayfile-cli/main.go (lines 239–243, 842–851, 3620–3627)
cloudRelayfileDelegatedTokenRequest payload and delegatedRelayfileTokenViaCloud mint delegated credentials; persistDelegatedWorkspace stores workspace record fields from delegated bundle; both setup and workspace join replace Cloud join with delegated flows.
Workspace Client and API Operations
cmd/relayfile-cli/main.go (lines 575–1209, 4225–4408)
workspaceCommandClient prefers delegated credentials when direct token absent; refreshFromDelegated updates client state from refreshed bundles; workspace API calls fall back to delegated refresh on auth errors; unified isMountCredentialExpired classifies delegated expiry equivalently to cloud refresh expiry.
Integration Operations Using Delegated Credentials
cmd/relayfile-cli/main.go (lines 2030–2673, 3428)
runIntegrationConnect, runIntegrationList, runIntegrationDisconnect, runIntegrationAdopt, runIntegrationSetMetadata, and runOpsReplay mint/use delegated credentials instead of Cloud join tokens for workspace runtime setup and API operations.
Mount CLI Delegated Credential Support
cmd/relayfile-mount/main.go (lines 21, 489–543)
mountCredsFile schema extends with delegated token fields; token loading picks first non-empty candidate; installCredsFileRefresh prefers delegated renewal via delegatedauth.RenewFile when rotation token present.
Mount Flag Parsing and Credential Resolution
cmd/relayfile-cli/main.go (lines 3833–4153)
Add --creds-file flag; when no --token provided, load/refresh delegated credentials from file, validate workspace match via workspaceRequestMatchesDelegatedCredentials, derive mount server from delegated bundle, and thread delegated path into mount startup.
Mount Loop Delegated Refresh and Degraded Handling
cmd/relayfile-cli/main.go (lines 7856–8070)
runMountLoop accepts delegated creds file path; refresh delegated tokens before sync; use isMountCredentialExpired for all degraded-state transitions; update workspace record and websocket on delegated refresh; apply degraded-specific stall reason and error messages for delegated-credential failures.
Main Test Suite Migration
cmd/relayfile-cli/main_test.go
Workspace join, tree, read, setup, integration connect, mount, ops replay, adopt, and set-metadata tests replace cloud-token minting with delegated-token endpoints and /v1/tokens/refresh handlers; add delegated JWT expiry support; update Authorization expectations and assertion helpers.
E2E Test Infrastructure and Mock Cloud
cmd/relayfile-cli/productized_cloud_mount_e2e_test.go, cmd/relayfile-cli/setup_e2e_test.go, cmd/relayfile-cli/writeback_daemon_test.go
Mock cloud tracks tokenMintCount/refreshCount instead of joinCount; implement /v1/tokens/refresh and /relayfile/delegated-token handlers; update e2e tests to generate/pass delegated credentials file to mount loop; adjust setup_e2e and writeback_daemon for new runMountLoop signature.
Background Test Error Update
cmd/relayfile-cli/background_test.go (line 582–589)
Update TestRestartClearsStaleDaemonPID to assert "delegated relayfile credentials" error substring instead of prior "agent-relay cloud session".

Sequence Diagram(s)

sequenceDiagram
  participant Setup
  participant Cloud API
  participant delegatedauth
  participant LocalDisk
  participant Mount
  Setup->>Cloud API: POST /relayfile/delegated-token
  Cloud API-->>Setup: Bearer + Rotation tokens, refresh URL
  Setup->>delegatedauth: SaveAtomic(delegated_creds_path, Bundle)
  delegatedauth->>LocalDisk: write JSON with atomic temp
  Setup->>Mount: --server <from bundle> --creds-file <path>
  Mount->>delegatedauth: Load(delegated_creds_path)
  delegatedauth->>LocalDisk: read JSON
  delegatedauth-->>Mount: Bundle
  Mount->>delegatedauth: Renew(ctx, client, bundle, timeout)
  delegatedauth->>Cloud API: POST /v1/tokens/refresh
  Cloud API-->>delegatedauth: new Bearer + Rotation tokens
  delegatedauth->>LocalDisk: SaveAtomic with rotated tokens
  Mount->>Mount: sync data-plane with refreshed bearer
Loading

🎯 4 (Complex) | ⏱️ ~60 minutes

A rabbit hops through the forest of credentials anew,
Where delegated tokens refresh the morning dew. 🐰
No join-paths remain—just bundles of trust,
Persisted and rotated, as storage systems must!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.39% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and specifically describes the main change: transitioning from cloud-based /join token renewal to delegated relayfile token refresh mechanisms.
Description check ✅ Passed The description clearly relates to the changeset, detailing the implementation of delegated token consumption, persistence, and refresh rotation across CLI and mount components.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch auth-unify-direct-http-2108

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 and usage tips.

@agent-relay-code

Copy link
Copy Markdown
Contributor

pr-reviewer could not complete review for #274 in AgentWorkforce/relayfile.
The review harness exited with code 1.
No review was posted; this needs operator attention.

@github-actions

github-actions Bot commented Jun 13, 2026

Copy link
Copy Markdown

Relayfile Eval Review

Run: .relayfile/evals/runs/2026-06-13T20-31-29-921Z-HEAD-provider
Mode: provider
Git SHA: d19dad0

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.

@khaliqgant khaliqgant force-pushed the auth-unify-direct-http-2108 branch from 27583cb to e12a638 Compare June 13, 2026 20:29
@agent-relay-code

Copy link
Copy Markdown
Contributor

pr-reviewer could not complete review for #274 in AgentWorkforce/relayfile.
The review harness exited with code 1.
No review was posted; this needs operator attention.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/relayfile-cli/main_test.go`:
- Around line 285-291: Tests mutate shared flags from httptest handlers (e.g.
the boolean seenBootstrap) without synchronization; convert each such
flag/counter used by handlers and later assertions to an atomic-backed type
(e.g. change seenBootstrap bool -> seenBootstrap int32) and use
sync/atomic.StoreInt32 in the handler and sync/atomic.LoadInt32 in the test
assertions (or protect access with a sync.Mutex if you prefer). Apply the same
change to the other handler-set counters/flags referenced in the review so all
writes in the httptest handlers are synchronized with the reads in the test
code.

In `@cmd/relayfile-cli/main.go`:
- Around line 4397-4400: The code incorrectly returns the stale auth error `err`
after calling `c.refreshFromDelegated()` instead of the new delegated-refresh
error; change the error return so that when `refreshFromDelegated()` returns a
non-nil `refreshErr` you return that `refreshErr` (not `err`) (i.e., replace the
current `return body, contentType, err` with `return body, contentType,
refreshErr`), and make the identical fix at the other occurrence (lines
4408-4411) where `c.refreshFromDelegated()` is checked; this ensures callers
like `runStatus` and other workspace verbs receive the delegated-expiry error
from `c.refreshFromDelegated()` rather than the stale 401/403.
- Around line 5433-5436: The current code treats every
delegatedauth.ErrRefreshRejected as a permanent credential expiry; instead, only
map to ErrDelegatedRelayfileCredentialsExpired when the delegatedauth error
indicates a permanent revocation (e.g., error code or sentinel for
delegation_expired or workspace_token_revoked). Modify the error handling around
the existing if errors.Is(err, delegatedauth.ErrRefreshRejected) check: inspect
the concrete delegatedauth error (unwrap err and check its Code/Type or use a
provided helper like delegatedauth.IsPermanentRevocation(err)) and return
ErrDelegatedRelayfileCredentialsExpired only for those permanent-revocation
cases; for all other refresh-rejected cases (transient timeouts, non-2xx, etc.)
return the original err (or wrap it without converting to
ErrDelegatedRelayfileCredentialsExpired) so the mount is not forced into
degraded mode. Ensure you reference the same variables (err, bundle) and the
same sentinels (delegatedauth.ErrRefreshRejected,
ErrDelegatedRelayfileCredentialsExpired) when making this change.
- Around line 1619-1622: In persistDelegatedWorkspace (cmd/relayfile-cli/main.go
lines 1619-1622) stop clearing record.CloudAPIURL — remove the assignment that
sets it to "" so the existing workspace Cloud API URL is preserved; for each
other site (cmd/relayfile-cli/main.go:2041-2041, 2394-2395, 2455-2456,
2528-2529, 2673-2674, 3428-3429) where the code builds a Cloud client or chooses
a cloud API URL (e.g. in the integration
connect/list/disconnect/adopt/set-metadata and ops replay paths), change the
logic that currently defaults straight to the global/default URL to instead
first use the saved workspace record.CloudAPIURL when non-empty, then fall back
to the flag/env default — i.e., prefer workspaceRecord.CloudAPIURL ||
providedFlag || default.
- Around line 5395-5410: The global delegated-credentials.json must be replaced
with a workspace-scoped path or a persisted per-workspace field; change
delegatedCredentialsPath/resolveDelegatedCredentialsPath (functions
delegatedCredentialsPath and resolveDelegatedCredentialsPath) to return a
workspace-specific filename (e.g. include workspace ID/name) or stop using a
global helper and instead store/load the delegated credentials path in the
workspaceRecord. For each affected site apply these concrete edits:
cmd/relayfile-cli/main.go (lines 5395-5410) — modify
resolveDelegatedCredentialsPath/delegatedCredentialsPath to accept or derive a
workspace identifier and return a workspace-scoped path (or remove global helper
and document workspaceRecord field); cmd/relayfile-cli/main.go (lines 847-849) —
when saving setup-minted delegated bundles, write to the workspace-specific path
(persist path into workspaceRecord if using that approach);
cmd/relayfile-cli/main.go (lines 2034-2035) — write integration-connect
bootstrap bundles to the workspace-scoped path; cmd/relayfile-cli/main.go (lines
3624-3625) — write workspace join bootstrapped bundles to the workspace-scoped
path; cmd/relayfile-cli/main.go (lines 3907-3923) — resolve/load the delegated
bundle from the selected workspace’s stored path (not the global file);
cmd/relayfile-cli/main.go (lines 4235-4258) — update
prepareWorkspaceCommandClient to load the delegated bundle from the
workspace-specific path (or workspaceRecord) so status/tree/read/export operate
per-workspace. Ensure all callers use the new API or workspaceRecord field so no
code reads/writes the old global file.
- Around line 1601-1605: The code currently backfills
bundle.RelayfileWorkspaceID with the cloud control-plane workspaceID when
bundle.Workspace() is empty; instead, change this to fail fast: if
bundle.Workspace() == "" return a descriptive error (e.g., "missing relayfile
workspace id") rather than assigning workspaceID, so ValidateForUse isn't
misled; update the error path before calling bundle.ValidateForUse() and remove
the assignment to RelayfileWorkspaceID so the runtime Relayfile workspace id
must be explicitly provided.

In `@cmd/relayfile-mount/main.go`:
- Line 513: The bearer-token precedence is inverted: change the token selection
to prefer creds.AccessToken before creds.Token so it matches
delegatedauth.Bundle.BearerToken()'s behavior; replace the call that sets token
(currently firstNonEmpty(creds.Token, creds.AccessToken, creds.RelayfileToken))
with firstNonEmpty(creds.AccessToken, creds.Token, creds.RelayfileToken) (or
otherwise call into the same Bundle.BearerToken() helper) so startup uses the
same precedence as internal/delegatedauth/delegatedauth.go:74-76.

In `@internal/delegatedauth/delegatedauth.go`:
- Around line 236-287: The Renew() flow currently maps transport timeouts and
all non-2xx HTTP responses to ErrRefreshRejected; change Renew() so client.Do
errors (including context.DeadlineExceeded/timeouts) return the original error
(or a wrapped non-ErrRefreshRejected error) instead of wrapping with
ErrRefreshRejected, and change refreshHTTPError(status,payload) so only explicit
delegation errors (parsed.Code == "delegation_expired" or
"workspace_token_revoked") return ErrRefreshRejected while all other HTTP
failures return a generic refresh error that does NOT wrap ErrRefreshRejected;
update/enable regression tests to assert that a 5xx response and a request
timeout do NOT produce ErrRefreshRejected but do return an error indicating
refresh failed (i.e., add tests exercising Renew() and refreshHTTPError for
timeout and 5xx cases).
- Around line 118-128: Validate the relayfile URL in Bundle.ValidateForUse by
parsing b.ServerURL() (use net/url.Parse or url.ParseRequestURI) and ensure it
is an absolute URL with a non-empty Scheme and Host (and optionally enforce
http/https if desired); if parsing fails or required parts are missing return a
descriptive error like "delegated relayfile credentials invalid relayfileUrl"
instead of accepting any non-empty string so malformed values (e.g.,
"relayfile") fail fast; update Bundle.ValidateForUse to perform this URL
validation alongside the existing BearerToken() and Workspace() checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9bfd888d-f495-48c6-aec0-e1e93267456d

📥 Commits

Reviewing files that changed from the base of the PR and between 845f608 and 27583cb.

📒 Files selected for processing (9)
  • cmd/relayfile-cli/background_test.go
  • cmd/relayfile-cli/main.go
  • cmd/relayfile-cli/main_test.go
  • cmd/relayfile-cli/productized_cloud_mount_e2e_test.go
  • cmd/relayfile-cli/setup_e2e_test.go
  • cmd/relayfile-cli/writeback_daemon_test.go
  • cmd/relayfile-mount/main.go
  • internal/delegatedauth/delegatedauth.go
  • internal/delegatedauth/delegatedauth_test.go

Comment on lines +285 to 291
var seenBootstrap bool
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/v1/workspaces/ws_cloud/join" {
if r.URL.Path != "/api/v1/workspaces/ws_cloud/relayfile/delegated-token" {
t.Fatalf("unexpected path: %s", r.URL.Path)
}
seenJoin = true
seenBootstrap = true
if got := r.Header.Get("Authorization"); got != "Bearer cld_access" {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard handler-shared state with atomics/mutex to keep these tests race-safe.

httptest handlers run on separate goroutines. Writes on Line 290, Line 413, Line 526, Line 2085, and Line 2173 (same pattern in nearby changed counters/flags) are read later without synchronization, which can fail under go test -race and make assertions flaky.

🔧 Suggested fix pattern
- var seenBootstrap bool
+ var seenBootstrap atomic.Bool

- seenBootstrap = true
+ seenBootstrap.Store(true)

- if !seenBootstrap {
+ if !seenBootstrap.Load() {
    t.Fatalf("expected delegated-token bootstrap call")
 }

- var refreshCount int
- var treeCount int
+ var refreshCount atomic.Int32
+ var treeCount atomic.Int32

- refreshCount++
+ refreshCount.Add(1)

- treeCount++
+ treeCount.Add(1)

- if refreshCount != 1 || treeCount != 1 {
-   t.Fatalf("refreshCount/treeCount = %d/%d, want 1/1", refreshCount, treeCount)
+ if refreshCount.Load() != 1 || treeCount.Load() != 1 {
+   t.Fatalf("refreshCount/treeCount = %d/%d, want 1/1", refreshCount.Load(), treeCount.Load())
 }

Also applies to: 407-414, 520-527, 2077-2086, 2166-2174

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/relayfile-cli/main_test.go` around lines 285 - 291, Tests mutate shared
flags from httptest handlers (e.g. the boolean seenBootstrap) without
synchronization; convert each such flag/counter used by handlers and later
assertions to an atomic-backed type (e.g. change seenBootstrap bool ->
seenBootstrap int32) and use sync/atomic.StoreInt32 in the handler and
sync/atomic.LoadInt32 in the test assertions (or protect access with a
sync.Mutex if you prefer). Apply the same change to the other handler-set
counters/flags referenced in the review so all writes in the httptest handlers
are synchronized with the reads in the test code.

Comment thread cmd/relayfile-cli/main.go
Comment on lines +1601 to +1605
if bundle.Workspace() == "" {
bundle.RelayfileWorkspaceID = workspaceID
}
if strings.TrimSpace(joined.Token) == "" {
return cloudWorkspaceJoinResponse{}, errors.New("cloud join response missing token")
if err := bundle.ValidateForUse(); err != nil {
return delegatedauth.Bundle{}, fmt.Errorf("delegated relayfile credential bundle invalid: %w", err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail when relayfileWorkspaceId is missing instead of substituting the Cloud workspace id.

workspaceID here is the Cloud control-plane id, but the rest of this file already treats the Relayfile runtime id as a separate field (RelayWorkspaceID). Backfilling a missing runtime id with the Cloud id makes ValidateForUse pass and can send later data-plane calls to the wrong workspace on deployments where those ids differ.

Suggested fix
-	if bundle.Workspace() == "" {
-		bundle.RelayfileWorkspaceID = workspaceID
-	}
+	if bundle.Workspace() == "" {
+		return delegatedauth.Bundle{}, errors.New("delegated relayfile credential bundle missing relayfileWorkspaceId")
+	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if bundle.Workspace() == "" {
bundle.RelayfileWorkspaceID = workspaceID
}
if strings.TrimSpace(joined.Token) == "" {
return cloudWorkspaceJoinResponse{}, errors.New("cloud join response missing token")
if err := bundle.ValidateForUse(); err != nil {
return delegatedauth.Bundle{}, fmt.Errorf("delegated relayfile credential bundle invalid: %w", err)
if bundle.Workspace() == "" {
return delegatedauth.Bundle{}, errors.New("delegated relayfile credential bundle missing relayfileWorkspaceId")
}
if err := bundle.ValidateForUse(); err != nil {
return delegatedauth.Bundle{}, fmt.Errorf("delegated relayfile credential bundle invalid: %w", err)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/relayfile-cli/main.go` around lines 1601 - 1605, The code currently
backfills bundle.RelayfileWorkspaceID with the cloud control-plane workspaceID
when bundle.Workspace() is empty; instead, change this to fail fast: if
bundle.Workspace() == "" return a descriptive error (e.g., "missing relayfile
workspace id") rather than assigning workspaceID, so ValidateForUse isn't
misled; update the error path before calling bundle.ValidateForUse() and remove
the assignment to RelayfileWorkspaceID so the runtime Relayfile workspace id
must be explicitly provided.

Comment thread cmd/relayfile-cli/main.go
Comment on lines +1619 to +1622
record.Server = strings.TrimRight(bundle.ServerURL(), "/")
record.LocalDir = localDir
record.CloudAPIURL = ""
record.LastUsedAt = time.Now().UTC().Format(time.RFC3339)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

The delegated bootstrap path drops the workspace's Cloud control-plane URL. persistDelegatedWorkspace clears record.CloudAPIURL, while the integration and ops verbs below rebuild their Cloud clients from flag/env defaults only. After bootstrapping against a non-default Cloud API, those commands will start talking to https://agentrelay.com/cloud unless the operator repeats --cloud-api-url on every invocation.

  • cmd/relayfile-cli/main.go#L1619-L1622: preserve record.CloudAPIURL during delegated persistence instead of blanking it.
  • cmd/relayfile-cli/main.go#L2041-L2041: when connecting integrations, fall back to the workspace's saved Cloud API URL before defaulting.
  • cmd/relayfile-cli/main.go#L2394-L2395: do the same for integration list.
  • cmd/relayfile-cli/main.go#L2455-L2456: do the same for integration disconnect.
  • cmd/relayfile-cli/main.go#L2528-L2529: do the same for integration adopt.
  • cmd/relayfile-cli/main.go#L2673-L2674: do the same for integration set-metadata.
  • cmd/relayfile-cli/main.go#L3428-L3429: do the same for ops replay.
📍 Affects 1 file
  • cmd/relayfile-cli/main.go#L1619-L1622 (this comment)
  • cmd/relayfile-cli/main.go#L2041-L2041
  • cmd/relayfile-cli/main.go#L2394-L2395
  • cmd/relayfile-cli/main.go#L2455-L2456
  • cmd/relayfile-cli/main.go#L2528-L2529
  • cmd/relayfile-cli/main.go#L2673-L2674
  • cmd/relayfile-cli/main.go#L3428-L3429
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/relayfile-cli/main.go` around lines 1619 - 1622, In
persistDelegatedWorkspace (cmd/relayfile-cli/main.go lines 1619-1622) stop
clearing record.CloudAPIURL — remove the assignment that sets it to "" so the
existing workspace Cloud API URL is preserved; for each other site
(cmd/relayfile-cli/main.go:2041-2041, 2394-2395, 2455-2456, 2528-2529,
2673-2674, 3428-3429) where the code builds a Cloud client or chooses a cloud
API URL (e.g. in the integration connect/list/disconnect/adopt/set-metadata and
ops replay paths), change the logic that currently defaults straight to the
global/default URL to instead first use the saved workspace record.CloudAPIURL
when non-empty, then fall back to the flag/env default — i.e., prefer
workspaceRecord.CloudAPIURL || providedFlag || default.

Comment thread cmd/relayfile-cli/main.go
Comment on lines +4397 to 4400
if refreshErr := c.refreshFromDelegated(); refreshErr != nil {
return body, contentType, err
}
return c.client.getBytes(ctx, pathForWorkspace(c.workspaceID))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Return the delegated-refresh error, not the stale auth error.

Both retry helpers drop refreshFromDelegated() failures and return the original 401/403 instead. That loses the new delegated-expiry signal added in this PR, so callers like runStatus can misclassify a revoked delegation as a Cloud-session expiry and the other workspace verbs surface a generic auth error.

Suggested fix
 	if refreshErr := c.refreshFromDelegated(); refreshErr != nil {
-		return body, contentType, err
+		return body, contentType, refreshErr
 	}
 	if refreshErr := c.refreshFromDelegated(); refreshErr != nil {
-		return err
+		return refreshErr
 	}

Also applies to: 4408-4411

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/relayfile-cli/main.go` around lines 4397 - 4400, The code incorrectly
returns the stale auth error `err` after calling `c.refreshFromDelegated()`
instead of the new delegated-refresh error; change the error return so that when
`refreshFromDelegated()` returns a non-nil `refreshErr` you return that
`refreshErr` (not `err`) (i.e., replace the current `return body, contentType,
err` with `return body, contentType, refreshErr`), and make the identical fix at
the other occurrence (lines 4408-4411) where `c.refreshFromDelegated()` is
checked; this ensures callers like `runStatus` and other workspace verbs receive
the delegated-expiry error from `c.refreshFromDelegated()` rather than the stale
401/403.

Comment thread cmd/relayfile-cli/main.go
Comment thread cmd/relayfile-cli/main.go
Comment thread cmd/relayfile-mount/main.go
Comment thread internal/delegatedauth/delegatedauth.go
Comment thread internal/delegatedauth/delegatedauth.go
@khaliqgant khaliqgant merged commit f79fc6a into main Jun 13, 2026
8 checks passed
@khaliqgant khaliqgant deleted the auth-unify-direct-http-2108 branch June 13, 2026 20:35
@agent-relay-code

Copy link
Copy Markdown
Contributor

pr-reviewer could not complete review for #274 in AgentWorkforce/relayfile.
The review harness exited with code 1.
No review was posted; this needs operator attention.

1 similar comment
@agent-relay-code

Copy link
Copy Markdown
Contributor

pr-reviewer could not complete review for #274 in AgentWorkforce/relayfile.
The review harness exited with code 1.
No review was posted; this needs operator attention.

@agent-relay-code

Copy link
Copy Markdown
Contributor

No code edits were needed after validating the current checkout. The delegated-auth PR surface already passes the relevant behavior and stale legacy /join assumptions were not present in the changed Go paths.

Addressed comments

  • Devin BUG_pr-review-job-c2f61fc1a26f4c2395d9e6bdd2c66929_0001: WebSocket endpoint concern is stale. Current endpoint is /v1/workspaces/%s/fs/ws in internal/mountfuse/wsinvalidate.go:200; no change.
  • Devin ..._0002: Directory create/open cache flag is stale. Current return includes fuse.FOPEN_KEEP_CACHE in internal/mountfuse/dir.go:135; no change.
  • Devin ..._0003: File metadata locking concern is stale. Current code reads under lock in internal/mountfuse/file.go:257; no change.
  • Devin ..._0004: Flush ordering concern is stale. Current order is invalidate, putFile, update buffer in internal/mountfuse/file.go:239; no change.
  • Devin BUG_pr-review-job-0d58fd55531d4b52b5bcd4223e16c975_0002: Core webhook artifact is outside this PR’s delegated-auth change surface; current core tests pass, left unchanged.
  • Devin ..._0003: Mountfuse reconnect/backoff artifact is outside this PR’s changed surface; current mountfuse tests pass, left unchanged.
  • Devin resolved duplicates for dir.go, wsinvalidate.go, and file.go: confirmed already handled at internal/mountfuse/dir.go:135, internal/mountfuse/wsinvalidate.go:200, internal/mountfuse/file.go:239, and internal/mountfuse/file.go:257.
  • Devin BUG_pr-review-job-d4770554ba7b4e5aa1f6896925a9822a_0001: File flush error-path concern is outside this PR; current flush preserves dirty state on write error before mutation in internal/mountfuse/file.go:228; no change.
  • Devin BUG_pr-review-job-f5948a9d2b7047af850e87ae1a144d2c_0003: WebSocket auth/header concern is stale. Current dial uses Authorization header in internal/mountfuse/wsinvalidate.go:134; no change.
  • Devin BUG_pr-review-job-19c9f68a0b294753987d516e35f5dfc3_0001: SDK tsconfig comment is stale/wrong path. Current packages/sdk/typescript/tsconfig.json:14 excludes src/**/*.test.ts; no change.
  • Devin BUG_pr-review-job-309b1d0508384dcc9a8f56825f979c2e_0002: SDK BulkWriteResponse.correlationId comment is stale. Current type includes it in packages/sdk/typescript/src/types.ts:120; no change.

Advisory Notes

The remaining historical bot artifacts target mountfuse, core webhooks, or an old SDK package path outside .workforce/pr.diff. I did not fold those unrelated changes into this delegated-auth PR.

Validation

Passed locally:

  • go test ./...
  • go build for cmd/relayfile, cmd/relayfile-mount, cmd/relayfile-cli
  • go vet ./...
  • SDK CI path: npm ci, core build, SDK build, npx tsc --noEmit, SDK tests
  • ./scripts/check-contract-surface.sh
  • CI=true npx tsx scripts/e2e.ts --ci
  • Root npm run test
  • Root npm run build passed after rerunning with workspace-local Go temp/cache; the first attempt only failed from /tmp disk exhaustion.

@agent-relay-code

Copy link
Copy Markdown
Contributor

pr-reviewer could not complete review for #274 in AgentWorkforce/relayfile.
The review harness exited with code 1.
No review was posted; this needs operator attention.

1 similar comment
@agent-relay-code

Copy link
Copy Markdown
Contributor

pr-reviewer could not complete review for #274 in AgentWorkforce/relayfile.
The review harness exited with code 1.
No review was posted; this needs operator attention.

@agent-relay-code

Copy link
Copy Markdown
Contributor

Implemented fixes for the validated PR review findings and re-ran the CI-equivalent checks locally.

Changes made:

  • Delegated bundles now fail fast if relayfileWorkspaceId is missing.
  • Delegated credentials are saved/loaded per workspace, with legacy global-file fallback.
  • Workspace CloudAPIURL is preserved and reused by integration/ops cloud-control commands.
  • Delegated refresh failures now propagate the refresh error, and transient relayauth failures are not mislabeled as expired/revoked credentials.
  • Delegated bundle validation now checks refresh token and URL shape.
  • relayfile-mount --creds-file token precedence matches delegatedauth.Bundle.
  • Reviewed httptest counters/flags now use atomics.
  • Fixed an unrelated but reproduced CI timing failure in internal/mountsync/syncer_test.go:2099.

Addressed comments

  • CodeRabbit 3408569835: race-unsafe httptest flags/counters fixed with atomics in cmd/relayfile-cli/main_test.go:285, cmd/relayfile-cli/main_test.go:408, cmd/relayfile-cli/main_test.go:522, cmd/relayfile-cli/main_test.go:2085, cmd/relayfile-cli/main_test.go:2173.
  • CodeRabbit 3408569837: missing relayfileWorkspaceId no longer backfills from the cloud workspace ID; fixed in cmd/relayfile-cli/main.go:1601.
  • CodeRabbit 3408569841: Cloud API URL is preserved and reused; fixed in cmd/relayfile-cli/main.go:1621, cmd/relayfile-cli/main.go:1634, cmd/relayfile-cli/main.go:2036, cmd/relayfile-cli/main.go:2400, cmd/relayfile-cli/main.go:2461, cmd/relayfile-cli/main.go:2534, cmd/relayfile-cli/main.go:2679, cmd/relayfile-cli/main.go:3434.
  • CodeRabbit 3408569842: delegated refresh errors are returned instead of stale 401/403 errors; fixed in cmd/relayfile-cli/main.go:4405 and cmd/relayfile-cli/main.go:4416.
  • CodeRabbit 3408569844: delegated credential files are workspace-scoped with legacy fallback; fixed in cmd/relayfile-cli/main.go:847, cmd/relayfile-cli/main.go:2044, cmd/relayfile-cli/main.go:3634, cmd/relayfile-cli/main.go:5407, cmd/relayfile-cli/main.go:5420, cmd/relayfile-cli/main.go:5463.
  • CodeRabbit 3408569846: only permanent refresh rejection maps to delegated credential expiry; fixed in cmd/relayfile-cli/main.go:5490 and internal/delegatedauth/delegatedauth.go:292.
  • CodeRabbit 3408569848: mount creds-file bearer precedence aligned with delegatedauth; fixed in cmd/relayfile-mount/main.go:513.
  • CodeRabbit 3408569851: delegated bundle validation now checks refresh token and absolute URL shape; fixed in internal/delegatedauth/delegatedauth.go:118.
  • CodeRabbit 3408569855: timeout/server refresh failures no longer use the permanent rejection sentinel; fixed in internal/delegatedauth/delegatedauth.go:247 and internal/delegatedauth/delegatedauth.go:295.
  • pr-reviewer failure comments 397eda59, 4b17f37e, 6d58e0fd, 786a80b8, dbdb01ec: no code finding was present; reran the review/validation locally.
  • Devin historical mountfuse comments: stale/already handled in current checkout at internal/mountfuse/wsinvalidate.go:134, internal/mountfuse/wsinvalidate.go:200, internal/mountfuse/dir.go:135, internal/mountfuse/file.go:228, internal/mountfuse/file.go:239, internal/mountfuse/file.go:257.
  • Devin historical SDK comments: stale/already handled in current checkout at packages/sdk/typescript/tsconfig.json:14 and packages/sdk/typescript/src/types.ts:120.

Advisory Notes

Validation passed:

  • go test ./...
  • Go binary builds for cmd/relayfile, cmd/relayfile-mount, cmd/relayfile-cli
  • npm run build
  • npm run test
  • npm run typecheck
  • scripts/check-contract-surface.sh
  • npx tsx scripts/e2e.ts --ci

@agent-relay-code

Copy link
Copy Markdown
Contributor

pr-reviewer could not complete review for #274 in AgentWorkforce/relayfile.
The review harness exited with code 1.
No review was posted; this needs operator attention.

2 similar comments
@agent-relay-code

Copy link
Copy Markdown
Contributor

pr-reviewer could not complete review for #274 in AgentWorkforce/relayfile.
The review harness exited with code 1.
No review was posted; this needs operator attention.

@agent-relay-code

Copy link
Copy Markdown
Contributor

pr-reviewer could not complete review for #274 in AgentWorkforce/relayfile.
The review harness exited with code 1.
No review was posted; this needs operator attention.

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.

1 participant