Skip to content

gateway: harden shared auth resolution across systemd, discord, and node host#39241

Merged
joshavant merged 13 commits intoopenclaw:mainfrom
joshavant:feat/gateway-auth-surface-hardening
Mar 8, 2026
Merged

gateway: harden shared auth resolution across systemd, discord, and node host#39241
joshavant merged 13 commits intoopenclaw:mainfrom
joshavant:feat/gateway-auth-surface-hardening

Conversation

@joshavant
Copy link
Copy Markdown
Contributor

Summary

This PR consolidates the gateway auth-surface fixes into one change set, with shared credential resolution, stronger service-auth auditing, and broader regression coverage.

What changed

  1. Shared gateway auth resolver surface
  • Added src/gateway/connection-auth.ts to expose shared auth resolution helpers for gateway clients.
  • Extended resolveGatewayCredentialsWithSecretInputs options in src/gateway/call.ts so callsites can explicitly control mode/precedence/fallback behavior without duplicating logic.
  1. Removed callsite auth drift
  • src/discord/monitor/exec-approvals.ts now resolves gateway auth via the shared resolver before constructing GatewayClient.
  • src/node-host/runner.ts now uses the shared resolver and no longer carries bespoke precedence logic.
  1. Improved secret-ref failure diagnostics
  • src/gateway/call.ts now wraps SecretRef resolution failures with config-path context (for example gateway.auth.password secret reference could not be resolved ...) to make auth failures actionable.
  1. Systemd service auth audit hardening
  • src/daemon/systemd.ts now parses both Environment= and EnvironmentFile= when reading unit auth metadata.
  • Supports %h, quoted paths, multiple EnvironmentFile entries, relative paths, and optional - entries.
  • Inline Environment= values correctly override values loaded from env files.
  1. Regression guards and expanded tests
  • Added src/gateway/client-callsites.guard.test.ts to constrain production new GatewayClient(...) callsites to an explicit allowlist.
  • Added src/gateway/connection-auth.test.ts with precedence/fallback matrix coverage across local/remote mode, mode overrides, and legacy-env behavior.
  • Expanded src/daemon/systemd.test.ts for EnvironmentFile parsing and merge behavior.
  1. Docs audit and drift fixes
  • Updated gateway/node/discord/doctor CLI docs to match current credential-resolution and systemd-audit behavior.

Why this change

These issues share the same broken surface area: gateway credentials were being resolved differently across command paths and runtime clients, and service-audit token checks missed systemd EnvironmentFile sources.

This PR unifies resolver behavior and closes those drift points instead of spot-fixing each symptom.

Issue coverage

Addresses #38912
Addresses #38179
Addresses #36786
Addresses #33088
Addresses #32839
Addresses #28586
Addresses #28148
Addresses #39016
Addresses #15718

PR references

Single-PR consolidation for this auth-surface work (no separate split PRs).

Validation

  • pnpm build
  • pnpm lint
  • pnpm format:check
  • pnpm format:docs:check
  • pnpm docs:check-links
  • pnpm dlx markdownlint-cli2 docs/cli/node.md docs/nodes/index.md docs/channels/discord.md docs/cli/gateway.md docs/cli/daemon.md docs/gateway/doctor.md docs/gateway/remote.md docs/cli/index.md
  • pnpm test src/daemon/systemd.test.ts src/daemon/service-audit.test.ts src/gateway/connection-auth.test.ts src/node-host/runner.credentials.test.ts src/discord/monitor/exec-approvals.test.ts src/gateway/credentials.test.ts src/gateway/client-callsites.guard.test.ts

Note: full pnpm check is currently blocked by an unrelated existing tsgo error in src/cron/isolated-agent/delivery-dispatch.double-announce.test.ts:80.

@openclaw-barnacle openclaw-barnacle bot added docs Improvements or additions to documentation channel: discord Channel integration: discord gateway Gateway runtime size: L maintainer Maintainer-authored PR labels Mar 7, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 7, 2026

Greptile Summary

This PR consolidates gateway auth-resolution across the Discord exec-approval monitor, node-host runner, and CLI call/probe paths into a single shared surface (src/gateway/connection-auth.ts), while also extending systemd service-audit token-drift checks to cover EnvironmentFile= entries alongside existing Environment= parsing. The changes are broad but well-scoped — they address a long-standing source of credential-resolution drift and close several related issues in one coherent diff.

Key observations:

  • src/daemon/systemd.ts — inverted error-handling condition: In resolveSystemdEnvironmentFiles, the catch block contains if (!optional) { continue; }. Because there is no code after the try/catch in the inner loop body, continue and falling through the catch are behaviourally identical — all EnvironmentFile read failures are silently swallowed regardless of optionality. The condition is redundant and misleads readers into expecting different handling for optional vs. non-optional entries. If the intent is truly "always resilient", the conditional should be removed; if non-optional file failures should surface, the non-optional branch should throw rather than continue.

  • Shared resolver integration in exec-approvals.ts and runner.ts is clean — both callsites now use resolveGatewayConnectionAuth with appropriate options, and the bespoke precedence logic in runner.ts has been correctly removed.

  • call.ts SecretRef error wrapping (onResolveRefError) improves diagnostics by including the config-path in the error message.

  • client-callsites.guard.test.ts is an effective structural guard; the allowlist approach will catch unauthorised new GatewayClient( additions in future PRs.

  • Test coverage for EnvironmentFile parsing is comprehensive for the happy paths; a test validating the "non-optional file missing" resilience path would help document the intended behaviour and guard against inadvertent future changes.

Confidence Score: 4/5

  • Safe to merge with one low-risk logic issue to address in resolveSystemdEnvironmentFiles.
  • The PR is well-structured with good test coverage across the new auth-resolution surface. The one noteworthy issue — an inverted/redundant if (!optional) condition in resolveSystemdEnvironmentFiles — does not cause incorrect observable behaviour today (both branches silently continue), but it misleads readers and may mask a missing resilience test for non-optional missing files. All other changes (shared resolver, discord/node-host migration, call.ts diagnostics, guard test) look correct.
  • src/daemon/systemd.ts — review the resolveSystemdEnvironmentFiles catch-block condition (lines 188–193).

Last reviewed commit: a438ebf

Comment on lines +188 to +193
} catch {
if (!optional) {
// Keep service auditing resilient even when env files are unavailable
// in the current runtime context.
continue;
}
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.

Inverted error-handling condition swallows all file errors identically

The catch block's if (!optional) branch is inverted compared to its apparent intent, and the continue inside it is redundant. Since there is no code after the try/catch block in the inner for loop body, falling through the catch block and explicitly calling continue both advance to the next iteration — making the two paths behaviourally identical: all EnvironmentFile read failures are silently swallowed regardless of whether the entry is optional.

If the intent is to propagate errors for non-optional files (matching systemd's own semantics, where a missing non-optional EnvironmentFile is fatal), the continue should be replaced with a throw:

Suggested change
} catch {
if (!optional) {
// Keep service auditing resilient even when env files are unavailable
// in the current runtime context.
continue;
}
try {
const fromFile = await readSystemdEnvironmentFile(pathname);
Object.assign(resolved, fromFile);
} catch (error) {
if (optional) {
// Silently ignore missing optional EnvironmentFile entries (systemd `-` prefix semantics).
continue;
}
// Keep service auditing resilient even when env files are unavailable
// in the current runtime context.
continue;
}

Or, if the intentional design is "always resilient" for both cases (as the comment suggests), the dead conditional should be removed to make the intent unambiguous:

Suggested change
} catch {
if (!optional) {
// Keep service auditing resilient even when env files are unavailable
// in the current runtime context.
continue;
}
try {
const fromFile = await readSystemdEnvironmentFile(pathname);
Object.assign(resolved, fromFile);
} catch {
// Keep service auditing resilient even when env files are unavailable
// in the current runtime context — both optional and non-optional entries
// are skipped gracefully rather than aborting the audit.
continue;
}

As written, if (!optional) { continue; } is dead code: the true branch (continue) and the implicit fall-through when optional === true are equivalent. The condition also misleads readers into expecting that optional-file failures are re-thrown or handled differently — they are not.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/daemon/systemd.ts
Line: 188-193

Comment:
**Inverted error-handling condition swallows all file errors identically**

The `catch` block's `if (!optional)` branch is inverted compared to its apparent intent, and the `continue` inside it is redundant. Since there is no code after the `try/catch` block in the inner `for` loop body, falling through the `catch` block and explicitly calling `continue` both advance to the next iteration — making the two paths behaviourally identical: **all** `EnvironmentFile` read failures are silently swallowed regardless of whether the entry is optional.

If the intent is to propagate errors for non-optional files (matching systemd's own semantics, where a missing non-optional `EnvironmentFile` is fatal), the `continue` should be replaced with a `throw`:

```suggestion
      try {
        const fromFile = await readSystemdEnvironmentFile(pathname);
        Object.assign(resolved, fromFile);
      } catch (error) {
        if (optional) {
          // Silently ignore missing optional EnvironmentFile entries (systemd `-` prefix semantics).
          continue;
        }
        // Keep service auditing resilient even when env files are unavailable
        // in the current runtime context.
        continue;
      }
```

Or, if the intentional design is "always resilient" for both cases (as the comment suggests), the dead conditional should be removed to make the intent unambiguous:

```suggestion
      try {
        const fromFile = await readSystemdEnvironmentFile(pathname);
        Object.assign(resolved, fromFile);
      } catch {
        // Keep service auditing resilient even when env files are unavailable
        // in the current runtime context — both optional and non-optional entries
        // are skipped gracefully rather than aborting the audit.
        continue;
      }
```

As written, `if (!optional) { continue; }` is dead code: the `true` branch (`continue`) and the implicit fall-through when `optional === true` are equivalent. The condition also misleads readers into expecting that optional-file failures are re-thrown or handled differently — they are not.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a438ebfcc3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +408 to +411
const auth = await resolveGatewayConnectionAuth({
config: this.opts.cfg,
env: process.env,
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Pass URL override into shared auth resolver

This new auth lookup ignores the URL override that was just computed, so when gatewayUrl (or OPENCLAW_GATEWAY_URL via buildGatewayConnectionDetails) points to a different host, the resolver can still return implicit gateway.auth.*/env credentials and those secrets are then sent to the override endpoint. This commit introduced credential forwarding for this handler, so override scenarios that previously carried no shared auth can now leak token/passwords unless override-aware auth checks are applied.

Useful? React with 👍 / 👎.

@joshavant joshavant force-pushed the feat/gateway-auth-surface-hardening branch from b549d89 to 28b10e8 Compare March 7, 2026 23:05
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 28b10e8f99

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +587 to +591
const resolvedValue = await resolveConfiguredGatewaySecretInput({
config: nextConfig,
path,
env: params.env,
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Continue to password refs when token ref resolution fails

The new eager pre-resolution loop aborts on the first resolvable-looking SecretRef instead of falling through to other valid auth paths. In local mode with gateway.auth.token as a missing SecretRef but gateway.auth.password as a valid SecretRef, this line throws before password resolution is attempted, whereas the previous logic allowed password fallback in this scenario. That turns a recoverable configuration into a hard failure for callers now using shared auth resolution (for example node host / Discord approvals) whenever the preferred token ref is unavailable but password auth is still viable.

Useful? React with 👍 / 👎.

@joshavant joshavant force-pushed the feat/gateway-auth-surface-hardening branch from 28b10e8 to a3e93ec Compare March 7, 2026 23:22
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ad967a07ef

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +100 to +103
const mergedEnvironment = {
...environmentFromFiles,
...environment,
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply systemd EnvironmentFile precedence correctly

The merged environment currently gives inline Environment= entries precedence over EnvironmentFile= values ({ ...environmentFromFiles, ...environment }), but systemd applies EnvironmentFile= after Environment= so file values should win. When both sources define the same key (for example OPENCLAW_GATEWAY_TOKEN during service migrations), the audit path can read the wrong effective credential and produce false token-drift results. Reverse the merge order so the computed environment matches what systemd actually executes.

Useful? React with 👍 / 👎.

joshavant added a commit to joshavant/openclaw that referenced this pull request Mar 7, 2026
@joshavant joshavant force-pushed the feat/gateway-auth-surface-hardening branch from ad967a0 to 7aa0637 Compare March 7, 2026 23:50
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7aa0637fb7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

: normalizeSecretInputString(configuredPassword));

return { token, password };
return await resolveGatewayConnectionAuth({
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Prevent local node-host auth from inheriting remote creds

This call now uses shared resolver defaults, and in local mode that resolver falls back to gateway.remote.token/password when gateway.auth.* is unset. In a common setup where gateway.mode=local and gateway.remote.token is configured for separate remote workflows, node-host will now send the remote token to the local gateway; that explicit token also suppresses GatewayClient's stored device-token fallback, so previously paired node hosts can start failing with token mismatch. Node-host should keep mode-specific credential selection (or explicitly disable remote fallback in local mode) to avoid this regression.

Useful? React with 👍 / 👎.

@joshavant joshavant force-pushed the feat/gateway-auth-surface-hardening branch from 7aa0637 to 0f52043 Compare March 8, 2026 00:10
@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot bot commented Mar 8, 2026

🔒 Aisle Security Analysis

We found 3 potential security issue(s) in this PR:

# Severity Title
1 🟠 High Gateway token/password leakage in node daemon status JSON via systemd EnvironmentFile parsing
2 🟠 High Gateway URL override can leak persisted device-auth token (ACP server) due to missing explicit-auth enforcement
3 🟠 High Unvalidated AgentParams.workspaceDir enables arbitrary host filesystem write/mount via gateway agent endpoint

1. 🟠 Gateway token/password leakage in node daemon status JSON via systemd EnvironmentFile parsing

Property Value
Severity High
CWE CWE-532
Location src/cli/node-cli/daemon.ts:212-233

Description

readSystemdServiceExecStart() now parses EnvironmentFile= and merges key/value pairs into the returned command.environment.

runNodeDaemonStatus(..., { json: true }) then serializes and prints the entire command object (including command.environment) to stdout without redaction.

This can expose secrets such as:

  • OPENCLAW_GATEWAY_TOKEN
  • OPENCLAW_GATEWAY_PASSWORD

If the systemd unit references an environment file containing credentials (a common pattern), openclaw node daemon status --json will output them in plaintext JSON. This is especially risky when JSON output is collected by logging/telemetry, CI artifacts, shell history capture, or shared support bundles.

Vulnerable sink (JSON output includes command unredacted):

const payload = {
  service: {
    ...buildDaemonServiceSnapshot(service, loaded),
    command,
    runtime,
  },
};

if (json) {
  defaultRuntime.log(JSON.stringify(payload, null, 2));
  return;
}

Data flow:

  • input: systemd unit file content EnvironmentFile=... and env-file contents
  • processing: readSystemdEnvironmentFile() reads file(s) and parses KEY=VALUE
  • output sink: JSON.stringify(payload) in node daemon status prints secrets

Recommendation

Redact/whitelist service environment variables in all JSON/text outputs that include command.environment.

For example, mirror the daemon status implementation by filtering env keys before JSON output:

import { filterDaemonEnv } from "../daemon-cli/shared.js";

const safeCommand = command?.environment
  ? { ...command, environment: filterDaemonEnv(command.environment) }
  : command;

const payload = {
  service: {
    ...buildDaemonServiceSnapshot(service, loaded),
    command: safeCommand,
    runtime,
  },
};

Alternatively (more conservative): omit command.environment entirely from JSON outputs for status commands, since it can contain secrets from Environment= and EnvironmentFile=.


2. 🟠 Gateway URL override can leak persisted device-auth token (ACP server) due to missing explicit-auth enforcement

Property Value
Severity High
CWE CWE-201
Location src/acp/server.ts:17-36

Description

The ACP gateway client accepts URL overrides but does not enforce ensureExplicitGatewayAuth (or any equivalent guard) before connecting.

As a result:

  • When opts.gatewayUrl (CLI --url) or OPENCLAW_GATEWAY_URL is set, resolveGatewayConnectionAuth() may return no token/password (by design for override-safety).
  • GatewayClient will still fall back to a persisted device-auth token (from loadDeviceAuthToken) whenever no explicit token is provided.
  • This causes the client to transmit a valid bearer credential to whatever WSS endpoint the URL override points to (attacker-controlled in phishing/misconfig scenarios), violating the intended “override-safe” contract and enabling credential exfiltration.

Vulnerable flow:

  • input: opts.gatewayUrl (CLI) or process.env.OPENCLAW_GATEWAY_URL
  • sink: GatewayClient WebSocket connect handshake includes deviceToken / auth.token fallback

Vulnerable code (callsite):

const creds = await resolveGatewayConnectionAuth({
  config: cfg,
  explicitAuth: { token: opts.gatewayToken, password: opts.gatewayPassword },
  env: process.env,
  urlOverride: gatewayUrlOverrideSource ? connection.url : undefined,
  urlOverrideSource: gatewayUrlOverrideSource,
});

const gateway = new GatewayClient({
  url: connection.url,
  token: creds.token,
  password: creds.password,// deviceIdentity defaults => enables persisted device-token fallback
});

Recommendation

Enforce override-safety for long-running clients the same way callGatewayWithScopes() does.

Option A (recommended): call ensureExplicitGatewayAuth() after resolving creds and before creating/starting GatewayClient:

import { ensureExplicitGatewayAuth } from "../gateway/call.js";

const resolved = await resolveGatewayConnectionAuth({
  config: cfg,
  env: process.env,
  explicitAuth: { token: opts.gatewayToken, password: opts.gatewayPassword },
  urlOverride: gatewayUrlOverrideSource ? connection.url : undefined,
  urlOverrideSource: gatewayUrlOverrideSource,
});

ensureExplicitGatewayAuth({
  urlOverride: gatewayUrlOverrideSource ? connection.url : undefined,
  urlOverrideSource: gatewayUrlOverrideSource,
  explicitAuth: { token: opts.gatewayToken, password: opts.gatewayPassword },
  resolvedAuth: resolved,
  errorHint: "Fix: pass --token or --password",
});

Option B: add a dedicated guard in resolveGatewayConnectionAuth() that throws when an override is present but no explicit (CLI) or no env creds (env override).

Additionally, consider hardening GatewayClient so persisted device-token fallback is disabled when any explicit shared auth is present or when a URL override is in use (requires a new option like disableStoredDeviceTokenFallback).


3. 🟠 Unvalidated AgentParams.workspaceDir enables arbitrary host filesystem write/mount via gateway agent endpoint

Property Value
Severity High
CWE CWE-22
Location src/gateway/server-methods/agent.ts:646-651

Description

The gateway agent RPC accepts a client-supplied workspaceDir (JSON: workspaceDir) and forwards it into the agent runtime when spawnedBy is provided. This value is not validated (no allowed-root constraint, no realpath containment, no absolute-path prohibition).

As a result, an authenticated gateway client with access to the agent method can:

  • Create directories and write bootstrap files in attacker-chosen locations on the host via ensureAgentWorkspace().
  • Potentially cause sandbox escape / host filesystem exposure when sandbox/Docker mode is enabled, because the chosen workspace path is later used to select/mount the workspace directory.

Vulnerable call path (input → sink):

  • Input: workspaceDir from RPC params
  • Pass-through: src/gateway/server-methods/agent.ts forwards the value when spawnedBy is non-empty
  • Sink: src/commands/agent.ts uses it as the workspace directory and calls fs.mkdir(..., { recursive: true }) and writes bootstrap files (e.g. AGENTS.md, SOUL.md, etc.).

Vulnerable code (gateway ingress):

// Internal-only: allow workspace override for spawned subagent runs.
workspaceDir: spawnedByValue ? request.workspaceDir : undefined,

Sink (filesystem writes):

const workspaceDirRaw = opts.workspaceDir?.trim() ?? resolveAgentWorkspaceDir(cfg, sessionAgentId);
const workspace = await ensureAgentWorkspace({ dir: workspaceDirRaw, ensureBootstrapFiles: ... });

Client exposure: the Swift gateway models added workspacedir / workspaceDir, making it easier for client code to send this field.

Recommendation

Do not allow network-facing callers to choose an arbitrary host path.

Recommended mitigations (defense-in-depth):

  1. Reject workspaceDir at the gateway boundary unless the request is coming from a strictly-internal/trusted callsite (not merely spawnedBy being present). For example, require an explicit internal capability/flag, or require senderIsOwner and verify spawnedBy refers to an existing session owned by the same operator.

  2. Server-side path validation before use:

    • require the value to be relative to a configured workspace root, or
    • resolve with realpath and ensure it is within an allowed root (and reject roots like /).

Example (allowed-root enforcement):

import fs from "node:fs";
import path from "node:path";

function resolveWorkspaceOverrideOrThrow(input: string, allowedRoot: string): string {
  const candidate = path.resolve(input);
  const rootReal = fs.realpathSync(allowedRoot);
  const candidateReal = fs.realpathSync(candidate);
  if (!candidateReal.startsWith(rootReal + path.sep)) {
    throw new Error("workspaceDir must stay within the configured workspace root");
  }
  return candidateReal;
}
  1. If the goal is “subagent inherits parent workspace”, ignore the client-provided path entirely and instead derive it from the parent session on the server (e.g., resolve the parent session’s effective workspace directory and reuse that).

Analyzed PR: #39241 at commit 7844645

Last updated on: 2026-03-08T01:29:43Z

@openclaw-barnacle openclaw-barnacle bot added the app: macos App: macos label Mar 8, 2026
@joshavant joshavant force-pushed the feat/gateway-auth-surface-hardening branch from b16c18e to 7116a0e Compare March 8, 2026 00:19
@openclaw-barnacle openclaw-barnacle bot added the cli CLI command changes label Mar 8, 2026
Signed-off-by: joshavant <[email protected]>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 784464563c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +119 to +123
return await resolveGatewayConnectionAuth({
config: configForResolution,
env: params.env,
includeLegacyEnv: false,
localTokenPrecedence: "env-first",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep node-host remote auth from falling back to local creds

Switching resolveNodeHostGatewayCredentials to the shared resolver without overriding remote fallback semantics now allows gateway.auth.* to be used in gateway.mode=remote when gateway.remote.* is unset. In that scenario, node-host will send a local credential to the remote gateway, and because runNodeHost passes it as an explicit token/password, GatewayClient no longer uses its stored device-token fallback; previously paired nodes can fail with token mismatch and local credentials can be forwarded to the wrong endpoint.

Useful? React with 👍 / 👎.

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

Labels

app: macos App: macos channel: discord Channel integration: discord cli CLI command changes docs Improvements or additions to documentation gateway Gateway runtime maintainer Maintainer-authored PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant