Skip to content

[codex] improve server auth error context#3240

Merged
juliusmarminge merged 8 commits into
codex/redact-dpop-request-targetfrom
codex/server-auth-error-boundaries
Jun 21, 2026
Merged

[codex] improve server auth error context#3240
juliusmarminge merged 8 commits into
codex/redact-dpop-request-targetfrom
codex/server-auth-error-boundaries

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Jun 20, 2026

Copy link
Copy Markdown
Member

Summary

  • narrow EnvironmentAuth method error channels to the tagged failures each operation can actually produce
  • replace predicate and single-tag recovery with exhaustive catchTags mappings at HTTP and WebSocket boundaries
  • attach session, pairing-link, subject, scope, and DPoP correlation fields while preserving the original cause
  • map replay-store failures directly and retain their source error chain

Validation

  • vp check
  • vp run typecheck
  • vp test apps/server/src/auth
  • vp test apps/server/src/http.test.ts apps/server/src/server.test.ts
  • vp test apps/server/src/cloud/http.test.ts

Note

Medium Risk
Touches authentication, token exchange, DPoP replay, and cross-version HTTP error shapes; behavior is mostly additive but any client that strictly validates error JSON may need to tolerate new optional fields.

Overview
This PR replaces broad auth/internal error handling with tag-specific Effect error channels and catchTags mappings at HTTP, WebSocket, and cloud boundaries, and enriches failures with operation, reason, session, scope, and secret-path context while keeping original causes on in-process errors.

Contracts and wire behavior: Environment and cloud HTTP errors gain stable reason / operation codes (plus relay phase metadata). User-facing message strings are derived from those codes; cause is kept server-side where encoded responses omit it. Decoders still accept legacy message-only payloads for rolling deploys. Unauthorized cloud link flows now expose cloud_cli_authorization_required with the t3 connect link guidance.

Implementation areas: EnvironmentAuth narrows per-method error unions and drops predicate helpers like isServerAuthCredentialError in favor of explicit tags. ServerSecretStore errors name secretName, secretPath, and persist operation. CliTokenManager and relay client code classify OAuth/relay failures by stage/phase and log redacted URL diagnostics (no credentials in messages or JSON). New CloudRelayRequestError and shared catchEnvironmentAuthenticationErrors centralize mapping to API errors. AuthAccessStreamError and related tests assert structural fields instead of echoing upstream text.

Reviewed by Cursor Bugbot for commit 55e9cd5. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add structured error context to server auth and cloud HTTP error classes

  • Replaces generic internal error strings across auth and cloud HTTP handlers with structured error classes carrying operation identifiers, phase labels, reason codes, and diagnostic fields (e.g. URL length, protocol, hostname) that avoid leaking sensitive data.
  • Introduces new error classes (CloudRelayConfigurationError, CloudRelayRequestError) and expands existing ones (ServerAuthDpopReplayStateRecordError, CloudCliCredentialRefreshError, etc.) with typed fields and computed message getters.
  • Replaces broad catchIf/catchTag error handling throughout handlers with catchTags mappings to specific error classes, ensuring unmatched tags propagate rather than being swallowed.
  • Adds structured reason codes (e.g. invalid_relay_url, cloud_cli_authorization_required) across validation helpers and HTTP error constructors in place of freeform message strings.
  • Risk: error message text and payloads have changed across many endpoints; callers or tests that match on message strings or error shapes will need updating.

Macroscope summarized 55e9cd5.

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 95dc9ced-4c1e-4a1e-878e-af266eeb2383

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/server-auth-error-boundaries

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

@github-actions github-actions Bot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Jun 20, 2026
@macroscopeapp

macroscopeapp Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved

Refactoring PR that adds diagnostic context to error classes and improves type safety by replacing generic error catching with explicit tag-based patterns. No runtime behavior changes — only improved error messages and observability for debugging.

You can customize Macroscope's approvability policy. Learn more.

@juliusmarminge juliusmarminge force-pushed the codex/server-auth-error-boundaries branch from a92067e to 93072ff Compare June 20, 2026 16:56
@juliusmarminge juliusmarminge changed the base branch from main to codex/redact-dpop-request-target June 20, 2026 16:57
@github-actions github-actions Bot added size:XXL 1,000+ changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Jun 20, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Auth stream RPC lacks compatibility
    • Restored message: Schema.String to the wire schema, made operation optional for decode, and removed cause: Schema.Defect() from the schema to ensure backward-compatible decoding across mismatched client/server versions.

Create PR

Or push these changes by commenting:

@cursor push 9bf0ab9608
Preview (9bf0ab9608)
diff --git a/packages/contracts/src/auth.ts b/packages/contracts/src/auth.ts
--- a/packages/contracts/src/auth.ts
+++ b/packages/contracts/src/auth.ts
@@ -285,15 +285,29 @@
 export class AuthAccessStreamError extends Schema.TaggedErrorClass<AuthAccessStreamError>()(
   "AuthAccessStreamError",
   {
-    operation: AuthAccessStreamOperation,
+    operation: Schema.optional(AuthAccessStreamOperation),
     currentSessionId: Schema.optional(AuthSessionId),
-    cause: Schema.Defect(),
+    message: Schema.String,
   },
 ) {
-  override get message(): string {
+  // @effect-diagnostics-next-line overriddenSchemaConstructor:off
+  constructor(props: {
+    readonly operation: AuthAccessStreamOperation;
+    readonly currentSessionId?: string | undefined;
+    readonly cause?: unknown;
+  }) {
     const session =
-      this.currentSessionId === undefined ? "" : ` for session ${this.currentSessionId}`;
-    return `Authentication access stream operation ${this.operation} failed${session}.`;
+      props.currentSessionId === undefined ? "" : ` for session ${props.currentSessionId}`;
+    const message =
+      "message" in props && typeof (props as any).message === "string"
+        ? (props as any).message
+        : `Authentication access stream operation ${props.operation} failed${session}.`;
+    super({
+      operation: props.operation,
+      ...(props.currentSessionId === undefined ? {} : { currentSessionId: props.currentSessionId }),
+      message,
+      ...(props.cause === undefined ? {} : { cause: props.cause }),
+    } as any);
   }
 }

You can send follow-ups to the cloud agent here.

Comment thread packages/contracts/src/auth.ts
@juliusmarminge juliusmarminge force-pushed the codex/redact-dpop-request-target branch 2 times, most recently from 6eba786 to aacb560 Compare June 20, 2026 22:40
@juliusmarminge juliusmarminge force-pushed the codex/server-auth-error-boundaries branch from 0f8b837 to edf63a7 Compare June 20, 2026 22:46
@juliusmarminge juliusmarminge force-pushed the codex/redact-dpop-request-target branch from aacb560 to 77e5b36 Compare June 20, 2026 22:49
@juliusmarminge juliusmarminge force-pushed the codex/server-auth-error-boundaries branch from edf63a7 to d430f59 Compare June 20, 2026 22:50

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: WebSocket RPC leaks auth causes
    • Sanitized the cause passed to AuthAccessStreamError by wrapping internal errors in a plain Error with only the message string, preventing full internal error chains from being serialized to WebSocket clients via Schema.Defect().

Create PR

Or push these changes by commenting:

@cursor push 0b147a7013
Preview (0b147a7013)
diff --git a/apps/server/src/ws.ts b/apps/server/src/ws.ts
--- a/apps/server/src/ws.ts
+++ b/apps/server/src/ws.ts
@@ -514,7 +514,7 @@
               (cause) =>
                 new AuthAccessStreamError({
                   operation: "list-pairing-links",
-                  cause,
+                  cause: new Error(cause.message),
                 }),
             ),
           ),
@@ -524,7 +524,7 @@
                 new AuthAccessStreamError({
                   operation: "list-client-sessions",
                   currentSessionId,
-                  cause,
+                  cause: new Error(cause.message),
                 }),
             ),
           ),

You can send follow-ups to the cloud agent here.

Comment thread packages/contracts/src/auth.ts
@juliusmarminge juliusmarminge force-pushed the codex/redact-dpop-request-target branch from 77e5b36 to 6bc2561 Compare June 20, 2026 23:10
@juliusmarminge juliusmarminge force-pushed the codex/server-auth-error-boundaries branch from d430f59 to fae19ef Compare June 20, 2026 23:11
@juliusmarminge juliusmarminge force-pushed the codex/redact-dpop-request-target branch from 6bc2561 to 71f9ff5 Compare June 20, 2026 23:20
@juliusmarminge juliusmarminge force-pushed the codex/server-auth-error-boundaries branch from fae19ef to 2b5e1cf Compare June 20, 2026 23:20

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Wrong reconcile operation labels
    • Moved error handlers into per-operation inner catchTags so makeCloudLinkProof errors are labeled 'generate_link_proof' and applyCloudRelayConfig errors are labeled 'persist_relay_configuration', leaving only setCliDesiredCloudLink errors mapped to 'persist_desired_link_state' in the outer handler.

Create PR

Or push these changes by commenting:

@cursor push 51e27c9846
Preview (51e27c9846)
diff --git a/apps/server/src/cloud/http.ts b/apps/server/src/cloud/http.ts
--- a/apps/server/src/cloud/http.ts
+++ b/apps/server/src/cloud/http.ts
@@ -723,6 +723,17 @@
         },
       },
       localOrigin,
+    ).pipe(
+      Effect.catchTags({
+        ServerAuthCloudLinkJwtSigningError: (error) =>
+          failEnvironmentCloudInternalError("sign_cloud_link_jwt")(error),
+        SecretStoreReadError: failEnvironmentCloudInternalError("generate_link_proof"),
+        SecretStoreDecodeError: failEnvironmentCloudInternalError("generate_link_proof"),
+        SecretStoreEncodeError: failEnvironmentCloudInternalError("generate_link_proof"),
+        SecretStorePersistError: failEnvironmentCloudInternalError("generate_link_proof"),
+        SecretStoreConcurrentReadError: failEnvironmentCloudInternalError("generate_link_proof"),
+        PlatformError: failEnvironmentCloudInternalError("generate_link_proof"),
+      }),
     );
     const link = yield* relayClientRequest(dependencies, {
       operation: "create-environment-link",
@@ -744,24 +755,25 @@
       environmentCredential: link.environmentCredential,
       cloudMintPublicKey: link.cloudMintPublicKey,
       endpointRuntime: link.endpointRuntime,
-    });
+    }).pipe(
+      Effect.catchTags({
+        ServerAuthLinkedCloudAccountVerificationError: (error) =>
+          failEnvironmentCloudInternalError("verify_linked_cloud_account")(error),
+        SecretStoreTemporaryPathGenerationError: failEnvironmentCloudInternalError(
+          "persist_relay_configuration",
+        ),
+        SecretStorePersistError: failEnvironmentCloudInternalError("persist_relay_configuration"),
+        SecretStoreRemoveError: failEnvironmentCloudInternalError("persist_relay_configuration"),
+        SchemaError: failEnvironmentCloudInternalError("persist_relay_configuration"),
+      }),
+    );
   },
   Effect.catchTags({
-    ServerAuthLinkedCloudAccountVerificationError: (error) =>
-      failEnvironmentCloudInternalError("verify_linked_cloud_account")(error),
-    ServerAuthCloudLinkJwtSigningError: (error) =>
-      failEnvironmentCloudInternalError("sign_cloud_link_jwt")(error),
-    SecretStoreReadError: failEnvironmentCloudInternalError("persist_desired_link_state"),
     SecretStoreTemporaryPathGenerationError: failEnvironmentCloudInternalError(
       "persist_desired_link_state",
     ),
     SecretStorePersistError: failEnvironmentCloudInternalError("persist_desired_link_state"),
     SecretStoreRemoveError: failEnvironmentCloudInternalError("persist_desired_link_state"),
-    SecretStoreDecodeError: failEnvironmentCloudInternalError("persist_desired_link_state"),
-    SecretStoreEncodeError: failEnvironmentCloudInternalError("persist_desired_link_state"),
-    SecretStoreConcurrentReadError: failEnvironmentCloudInternalError("persist_desired_link_state"),
-    SchemaError: failEnvironmentCloudInternalError("persist_desired_link_state"),
-    PlatformError: failEnvironmentCloudInternalError("persist_desired_link_state"),
     CloudRelayConfigurationError: (error) =>
       failEnvironmentCloudInternalError("read_relay_url_configuration")(error),
     CloudRelayRequestError: (error) =>

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 2b5e1cf. Configure here.

Comment thread apps/server/src/cloud/http.ts
@juliusmarminge juliusmarminge force-pushed the codex/redact-dpop-request-target branch from 71f9ff5 to 8bc20f2 Compare June 20, 2026 23:49
@juliusmarminge juliusmarminge force-pushed the codex/server-auth-error-boundaries branch from 2b5e1cf to 8728ebf Compare June 20, 2026 23:49
juliusmarminge and others added 6 commits June 20, 2026 17:00
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
juliusmarminge and others added 2 commits June 20, 2026 17:00
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
@juliusmarminge juliusmarminge force-pushed the codex/redact-dpop-request-target branch from 8bc20f2 to 3f67e2b Compare June 21, 2026 00:08
@juliusmarminge juliusmarminge force-pushed the codex/server-auth-error-boundaries branch from 8728ebf to 55e9cd5 Compare June 21, 2026 00:08
@juliusmarminge juliusmarminge merged commit 70fdb85 into codex/redact-dpop-request-target Jun 21, 2026
16 checks passed
@juliusmarminge juliusmarminge deleted the codex/server-auth-error-boundaries branch June 21, 2026 00:14
juliusmarminge added a commit that referenced this pull request Jun 21, 2026
Co-authored-by: codex <codex@users.noreply.github.com>
juliusmarminge added a commit that referenced this pull request Jun 21, 2026
Co-authored-by: codex <codex@users.noreply.github.com>
juliusmarminge added a commit that referenced this pull request Jun 21, 2026
Co-authored-by: codex <codex@users.noreply.github.com>
juliusmarminge added a commit that referenced this pull request Jun 21, 2026
Co-authored-by: codex <codex@users.noreply.github.com>
juliusmarminge added a commit that referenced this pull request Jun 21, 2026
Co-authored-by: codex <codex@users.noreply.github.com>
juliusmarminge added a commit that referenced this pull request Jun 21, 2026
Co-authored-by: codex <codex@users.noreply.github.com>
juliusmarminge added a commit that referenced this pull request Jun 21, 2026
Co-authored-by: codex <codex@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant