Skip to content

refactor(gateway): split startup and runtime seams#63975

Merged
gumadeiras merged 3 commits into
mainfrom
codex/gateway-runtime-seams
Apr 10, 2026
Merged

refactor(gateway): split startup and runtime seams#63975
gumadeiras merged 3 commits into
mainfrom
codex/gateway-runtime-seams

Conversation

@gumadeiras

Copy link
Copy Markdown
Member

Summary

  • split gateway startup and runtime policy out of src/gateway/server.impl.ts into focused gateway seam modules
  • move request-context routing onto live runtime state and remove the extra builder layer
  • add regression coverage for live cron routing and post-attach chat.history availability

Testing

  • pnpm tsgo
  • pnpm test src/gateway/server-request-context.test.ts src/gateway/server-startup-post-attach.test.ts src/gateway/server-startup.test.ts src/gateway/server-startup-log.test.ts src/gateway/server-close.test.ts src/gateway/server.reload.test.ts
  • pnpm check
  • pnpm build

Notes

  • pnpm test:gateway still hits an existing flaky failure in src/gateway/server.chat.gateway-server-chat.test.ts (agent.wait keeps lifecycle wait active while same-runId chat.send is active); isolated repro passes.

@gumadeiras
gumadeiras requested a review from a team as a code owner April 9, 2026 23:50
Copilot AI review requested due to automatic review settings April 9, 2026 23:50
@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: XL maintainer Maintainer-authored PR labels Apr 9, 2026
@gumadeiras gumadeiras self-assigned this Apr 9, 2026
@greptile-apps

greptile-apps Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR refactors server.impl.ts by extracting focused gateway seam modules — separating startup policy (server-startup-post-attach.ts), runtime subscriptions (server-runtime-subscriptions.ts), runtime services (server-runtime-services.ts), live state types (server-live-state.ts, server-runtime-handles.ts), aux handlers (server-aux-handlers.ts), and session-event broadcasting (server-session-events.ts). The key behavioral change is moving cron/cronStorePath reads onto JS getters in createGatewayRequestContext so config hot-reload can swap cronState without rebuilding handler closures, and making chat.history availability contingent on post-attach sidecar completion via a shared mutable Set<string>. Both behaviors are covered by new regression tests.

Confidence Score: 5/5

Safe to merge — refactoring only, all behavior preserved or explicitly improved, build and type checks pass.

All findings are P2 style suggestions; no logic errors, data loss, or security concerns identified. The getter pattern for live cron reads and the mutable-set approach for chat.history availability are both correct and well-tested.

No files require special attention.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/gateway/server-aux-handlers.ts
Line: 21-25

Comment:
**Duplicate type shadows exported definition**

`SharedGatewayAuthClient` is already exported from `server-shared-auth-generation.ts` with the exact same shape. This file already imports from that module — importing the type directly would remove the duplication.

```suggestion
import {
  disconnectStaleSharedGatewayAuthClients,
  setCurrentSharedGatewaySessionGeneration,
  type SharedGatewayAuthClient,
  type SharedGatewaySessionGenerationState,
} from "./server-shared-auth-generation.js";
```

Then remove the local `type SharedGatewayAuthClient = { ... }` block (lines 21–25).

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

Reviews (1): Last reviewed commit: "refactor(gateway): split startup and run..." | Re-trigger Greptile

Comment thread src/gateway/server-aux-handlers.ts Outdated

Copilot AI 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.

Pull request overview

Refactors the gateway server startup/runtime flow by extracting previously inlined logic from src/gateway/server.impl.ts into dedicated “seam” modules, while keeping request handling wired to live runtime state (not a rebuilt/builder context) and adding regression tests for routing correctness.

Changes:

  • Split gateway startup into early-runtime and post-attach runtime modules, and extract plugin bootstrap + startup config/secrets activation helpers.
  • Move request-context routing (notably cron/store access) onto live GatewayServerLiveState, avoiding rebuilding handler closures on reload.
  • Add regression coverage for live cron routing and for chat.history becoming available post-attach.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/gateway/server.impl.ts Rewires gateway server to use extracted startup/runtime seam modules and live runtime state.
src/gateway/server-startup.ts Converts prior monolith startup module into re-exports of new seams.
src/gateway/server-startup-post-attach.ts New: post-attach startup work (update checks, tailscale exposure, sidecars/plugin services, hooks).
src/gateway/server-startup-post-attach.test.ts New: regression test for chat.history enabling after post-attach sidecars start.
src/gateway/server-startup-plugins.ts New: isolates plugin bootstrap/maintenance/migration/subagent registry init.
src/gateway/server-startup-early.ts New: isolates early runtime setup (MCP loopback, discovery, skills timers, maintenance timers).
src/gateway/server-startup-config.ts New: isolates startup config snapshot loading, validation, and secrets activation logic.
src/gateway/server-shared-auth-generation.ts New: centralizes shared-auth session generation state + disconnect helpers.
src/gateway/server-session-events.ts New: extracts transcript/lifecycle broadcast logic into dedicated handlers.
src/gateway/server-runtime-subscriptions.ts New: encapsulates gateway event subscriptions (agent/heartbeat/transcript/lifecycle).
src/gateway/server-runtime-services.ts New: encapsulates runtime services (heartbeat runner, cron start logging, health monitor, outbound recovery, model pricing refresh).
src/gateway/server-runtime-handles.ts New: defines mutable runtime handles/state defaults for the live runtime state container.
src/gateway/server-request-context.ts New: builds request context that reads cron/store path from live runtime state.
src/gateway/server-request-context.test.ts New: regression test validating live cron/store reads through the request context.
src/gateway/server-reload-handlers.ts Refactors reload integration and adds a managed config reloader wired to new seams/state.
src/gateway/server-node-session-runtime.ts New: extracts node/session subscription runtime wiring (registry, subscriptions, voicewake broadcast).
src/gateway/server-live-state.ts New: defines GatewayServerLiveState combining mutable handles + live config-dependent state.
src/gateway/server-control-ui-root.ts New: isolates control UI root resolution/build logic from the server impl.
src/gateway/server-close.ts Adds runGatewayClosePrelude to centralize pre-close cleanup previously duplicated in server impl.
src/gateway/server-aux-handlers.ts New: extracts exec-approval/plugin-approval/secrets RPC handler wiring.

Comment thread src/gateway/server-runtime-handles.ts Outdated
Comment thread src/gateway/server-aux-handlers.ts Outdated
@gumadeiras
gumadeiras force-pushed the codex/gateway-runtime-seams branch from 7eddd30 to c6e47ef Compare April 10, 2026 01:28
@aisle-research-bot

aisle-research-bot Bot commented Apr 10, 2026

Copy link
Copy Markdown

🔒 Aisle Security Analysis

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

# Severity Title
1 🟡 Medium Unauthorized global session event subscription leaks session metadata and messages over WebSocket
1. 🟡 Unauthorized global session event subscription leaks session metadata and messages over WebSocket
Property Value
Severity Medium
CWE CWE-200
Location src/gateway/server-methods/sessions.ts:570-607

Description

The gateway adds WebSocket subscription methods that allow any connected client with READ scope to subscribe to:

  • Global session lifecycle/message events via sessions.subscribe (no per-device/session authorization)
  • Per-session message events via sessions.messages.subscribe for an arbitrary key

Once subscribed, createTranscriptUpdateBroadcastHandler broadcasts session.message and sessions.changed payloads that include an includeSession: true snapshot (sessionRow) and additional metadata (e.g., spawnedWorkspaceDir, deliveryContext, token/cost usage, model/provider, etc.) to all sessionEventSubscribers.

This creates an information disclosure/IDOR risk in multi-client deployments (multiple paired devices, operator UIs, remote access): any client granted READ scope can passively receive updates about all sessions and potentially subscribe to sessions they should not observe.

Vulnerable code (subscription endpoints + broadcast fanout):

// sessions.subscribe: global subscription, no further authorization
"sessions.subscribe": ({ client, context, respond }) => {
  const connId = client?.connId?.trim();
  if (connId) {
    context.subscribeSessionEvents(connId);
  }
  respond(true, { subscribed: Boolean(connId) }, undefined);
},// broadcast: sends full session snapshot to all global subscribers
for (const connId of params.sessionEventSubscribers.getAll()) {
  connIds.add(connId);
}
...
const sessionSnapshot = buildGatewaySessionSnapshot({
  sessionRow: loadGatewaySessionRow(sessionKey),
  includeSession: true,
});
params.broadcastToConnIds("session.message", { ...sessionSnapshot, ... }, connIds, ...);

Recommendation

Add authorization checks to subscription registration and to broadcast fanout.

Minimum hardening options:

  1. Restrict global subscription (sessions.subscribe) to an admin/operator scope intended for full visibility (or remove it entirely), and require explicit per-session subscriptions only.

  2. Enforce per-session access control in sessions.messages.subscribe:

    • Verify the requesting device/user is allowed to access the target canonicalKey (e.g., session ownership, allowed agent/workspace, or an ACL).
  3. Filter broadcast recipients:

    • Instead of broadcasting sessions.changed to sessionEventSubscribers.getAll(), broadcast only to subscribers authorized for that sessionKey.

Example (conceptual):

// sessions.messages.subscribe
if (!context.canAccessSession(client, canonicalKey)) {
  respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, "unauthorized session"));
  return;
}
context.subscribeSessionMessageEvents(connId, canonicalKey);// server-session-events fanout
const allowedConnIds = new Set<string>();
for (const connId of params.sessionMessageSubscribers.get(sessionKey)) {
  allowedConnIds.add(connId);
}// optionally: add global subscribers only if they have an admin scope

Also consider minimizing the snapshot: avoid including full sessionRow (includeSession: true) unless strictly required, and omit fields like filesystem paths (spawnedWorkspaceDir) and deliveryContext from broadcast payloads unless the subscriber is privileged.


Analyzed PR: #63975 at commit c6e47ef

Last updated on: 2026-04-10T01:31:11Z

@gumadeiras
gumadeiras merged commit 8de63ca into main Apr 10, 2026
27 checks passed
@gumadeiras
gumadeiras deleted the codex/gateway-runtime-seams branch April 10, 2026 01:28
@gumadeiras

Copy link
Copy Markdown
Member Author

Merged via squash.

Thanks @gumadeiras!

steipete pushed a commit that referenced this pull request Apr 10, 2026
Merged via squash.

Prepared head SHA: c6e47ef
Co-authored-by: gumadeiras <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
steipete pushed a commit that referenced this pull request Apr 10, 2026
Merged via squash.

Prepared head SHA: c6e47ef
Co-authored-by: gumadeiras <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
Merged via squash.

Prepared head SHA: c6e47ef
Co-authored-by: gumadeiras <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
Merged via squash.

Prepared head SHA: c6e47ef
Co-authored-by: gumadeiras <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
Merged via squash.

Prepared head SHA: c6e47ef
Co-authored-by: gumadeiras <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
Merged via squash.

Prepared head SHA: c6e47ef
Co-authored-by: gumadeiras <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
Merged via squash.

Prepared head SHA: c6e47ef
Co-authored-by: gumadeiras <[email protected]>
Co-authored-by: gumadeiras <[email protected]>
Reviewed-by: @gumadeiras
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime maintainer Maintainer-authored PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants