diff --git a/docs/security/openshell-0.0.71-gateway-auth-review.mdx b/docs/security/openshell-0.0.71-gateway-auth-review.mdx index c84623c5728..4f547882d07 100644 --- a/docs/security/openshell-0.0.71-gateway-auth-review.mdx +++ b/docs/security/openshell-0.0.71-gateway-auth-review.mdx @@ -92,7 +92,7 @@ The generated config sets `[openshell.gateway.tls]` with the NemoClaw-owned loca It also scrubs inherited `OPENSHELL_DISABLE_GATEWAY_AUTH=true` from host and compatibility-container launches. The local TLS reuse check allows a fixed 5-minute certificate validity skew to absorb normal host/container clock drift while still regenerating bundles outside that bounded window; the bound is intentionally not environment-overridable for this release so deployments cannot silently widen the local mTLS acceptance window. -The sandbox JWT config uses OpenShell's `ttl_secs = 3600` gateway contract: short enough for local sandbox callbacks, long enough to avoid unnecessary re-mint churn during normal Docker-driver operations, and covered by the upstream OpenShell sandbox JWT expiry tests plus NemoClaw config-auth contract tests. +The sandbox JWT config uses OpenShell's `ttl_secs = 0` non-expiring gateway contract for local single-player Docker-driver deployments, matching OpenShell's documented default (a positive TTL is reserved for shared, multi-tenant gateways). Non-expiring local tokens avoid the file-based sandbox JWT refresh dead-end, where an expired on-disk bootstrap token can no longer call `RefreshSandboxToken` and the host-CLI relay fails closed; this remains covered by the upstream OpenShell sandbox JWT expiry tests plus NemoClaw config-auth contract tests. The Docker-hosted compatibility gateway requires `NEMOCLAW_OPENSHELL_GATEWAY_CONTAINER_PATCH=1` and keeps the main OpenShell listener on `127.0.0.1`. Sandbox callback reachability is preserved by OpenShell's Docker driver: it rewrites the sandbox-facing endpoint to `host.openshell.internal:` and the OpenShell server adds the computed Docker bridge listener when that route is needed. diff --git a/src/lib/onboard/docker-driver-gateway-config-auth-contract.test.ts b/src/lib/onboard/docker-driver-gateway-config-auth-contract.test.ts index 3be0c5c0f86..91f35e4b29b 100644 --- a/src/lib/onboard/docker-driver-gateway-config-auth-contract.test.ts +++ b/src/lib/onboard/docker-driver-gateway-config-auth-contract.test.ts @@ -88,7 +88,7 @@ describe("docker-driver-gateway auth contract", () => { gatewayId, sandboxId, iat: now, - exp: now + ttlSecs, + exp: ttlSecs === 0 ? 0 : now + ttlSecs, }); const payload = validateOpenShellStyleSandboxJwt({ @@ -104,7 +104,7 @@ describe("docker-driver-gateway auth contract", () => { iss: `openshell-gateway:${gatewayId}`, aud: `openshell-gateway:${gatewayId}`, }); - expect(payload?.exp).toBe(now + ttlSecs); + expect(payload?.exp).toBe(ttlSecs === 0 ? 0 : now + ttlSecs); expect(() => validateOpenShellStyleSandboxJwt({ token, @@ -142,8 +142,8 @@ describe("docker-driver-gateway auth contract", () => { kid, gatewayId, sandboxId, - iat: now - ttlSecs * 2, - exp: now - ttlSecs, + iat: now - 7200, + exp: now - 3600, }); expect(() => validateOpenShellStyleSandboxJwt({ diff --git a/src/lib/onboard/docker-driver-gateway-config.ts b/src/lib/onboard/docker-driver-gateway-config.ts index 0c6e2f41418..0c362a1987e 100644 --- a/src/lib/onboard/docker-driver-gateway-config.ts +++ b/src/lib/onboard/docker-driver-gateway-config.ts @@ -14,7 +14,7 @@ export { ensureDockerDriverGatewayJwtBundle } from "./docker-driver-gateway-jwt- // See docs/security/openshell-0.0.72-compatibility-review.mdx for the source-of-truth review. export const DOCKER_DRIVER_GATEWAY_CONFIG_NAME = "openshell-gateway.toml"; -export const DOCKER_DRIVER_GATEWAY_JWT_TTL_SECS = 3600; +export const DOCKER_DRIVER_GATEWAY_JWT_TTL_SECS = 0; function tomlString(value: string): string { return JSON.stringify(value);