From 96efe21b50140b25df48e8e6cb1b2ce7e4b42947 Mon Sep 17 00:00:00 2001 From: Guillaume ARM Date: Wed, 17 Jun 2026 01:56:03 +0200 Subject: [PATCH] fix(server): require gateway secrets --- docs/ingame-trapos-ai-mcp-guide.md | 7 +++++-- just/server.just | 18 +++++++++++------- tools/trapos-server/src/index.ts | 23 +++++++++++++++++++++-- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/docs/ingame-trapos-ai-mcp-guide.md b/docs/ingame-trapos-ai-mcp-guide.md index 8347845..8db6286 100644 --- a/docs/ingame-trapos-ai-mcp-guide.md +++ b/docs/ingame-trapos-ai-mcp-guide.md @@ -80,7 +80,10 @@ MCP endpoint (OpenCode connects here): http://127.0.0.1:4445 The MCP endpoint stays bound to `127.0.0.1` on purpose: `exec-lua` / `write-file` are as powerful as the linked computer, so only local processes (your OpenCode) reach them. -Set `TRAPOS_SERVER_PASSWORD` in `.env` to require a shared secret on the gateway connection. +`TRAPOS_SERVER_PASSWORD` (and `OPENCODE_SERVER_PASSWORD`) must be set in `.env`: the +gateway connection requires this shared secret, and the server refuses to start if either +is empty (run `just install` to generate them). An empty password would expose the gateway +unauthenticated, so it is never allowed. ## 5. Link The Computer To The Gateway @@ -88,7 +91,7 @@ On the ComputerCraft computer, point it at the gateway and reboot: ```sh set cloud.url ws://:4444 -set cloud.password secret # only if TRAPOS_SERVER_PASSWORD is set on the host +set cloud.password secret # must match TRAPOS_SERVER_PASSWORD on the host reboot ``` diff --git a/just/server.just b/just/server.just index 8961849..0e91f18 100644 --- a/just/server.just +++ b/just/server.just @@ -1,17 +1,21 @@ # Serve the trapos-server gateway (port 4444) for remote/in-game attachment using -# repo-local .env secrets. Mirrors `opencode-serve`, but auth is optional: an unset -# TRAPOS_SERVER_PASSWORD means the gateway runs with auth disabled (warn, do not fail). +# repo-local .env secrets. Mirrors `opencode-serve`. Both TRAPOS_SERVER_PASSWORD and +# OPENCODE_SERVER_PASSWORD are required: an empty one would run the gateway +# unauthenticated, so a missing secret is a fatal error (see also the guard in index.ts). [positional-arguments] serve *args: #!/usr/bin/env bash set -euo pipefail repo='{{justfile_directory()}}' if [ -f "$repo/.env" ]; then set -a; . "$repo/.env"; set +a; fi - if [ -z "${TRAPOS_SERVER_PASSWORD:-}" ]; then - printf '%s\n' 'serve: TRAPOS_SERVER_PASSWORD unset in .env — gateway auth disabled' >&2 - fi - export TRAPOS_SERVER_PASSWORD="${TRAPOS_SERVER_PASSWORD:-}" - export OPENCODE_SERVER_PASSWORD="${OPENCODE_SERVER_PASSWORD:-}" + for var in TRAPOS_SERVER_PASSWORD OPENCODE_SERVER_PASSWORD; do + if [ -z "${!var:-}" ]; then + printf '%s\n' "serve: $var is empty or unset in .env — refusing to start (run \`just install\` to generate secrets)" >&2 + exit 1 + fi + done + export TRAPOS_SERVER_PASSWORD + export OPENCODE_SERVER_PASSWORD export OPENCODE_URL="${OPENCODE_URL:-http://127.0.0.1:4242}" export TRAPOS_SERVER_HOST="${TRAPOS_SERVER_HOST:-0.0.0.0}" export TRAPOS_SERVER_PORT="${TRAPOS_SERVER_PORT:-4444}" diff --git a/tools/trapos-server/src/index.ts b/tools/trapos-server/src/index.ts index 1700f3f..a4e0f65 100644 --- a/tools/trapos-server/src/index.ts +++ b/tools/trapos-server/src/index.ts @@ -3,11 +3,14 @@ import { createApp, type Config } from "./app.js"; const config: Config = { host: process.env.TRAPOS_SERVER_HOST ?? "0.0.0.0", port: readPort(process.env.TRAPOS_SERVER_PORT, 4444), - serverPassword: process.env.TRAPOS_SERVER_PASSWORD, + serverPassword: requireSecret("TRAPOS_SERVER_PASSWORD", process.env.TRAPOS_SERVER_PASSWORD), requestTimeoutMs: readPositiveInt(process.env.TRAPOS_SERVER_REQUEST_TIMEOUT_MS, 600_000), opencodeUrl: process.env.OPENCODE_URL ?? "http://127.0.0.1:4242", opencodeUsername: process.env.OPENCODE_USERNAME ?? "opencode", - opencodeServerPassword: process.env.OPENCODE_SERVER_PASSWORD, + opencodeServerPassword: requireSecret( + "OPENCODE_SERVER_PASSWORD", + process.env.OPENCODE_SERVER_PASSWORD, + ), // MCP endpoint: on by default, bound to localhost. exec-lua / write-file are as // powerful as the linked computer, so the listener stays off the public gateway port. mcpEnabled: process.env.MCP_ENABLED !== "0", @@ -53,6 +56,22 @@ function buildLogger(): Config["logger"] { return { level }; } +// A non-empty password is mandatory: an empty TRAPOS_SERVER_PASSWORD disables auth +// on the public gateway (resolveAccount maps any secret to "local"), and an empty +// OPENCODE_SERVER_PASSWORD drops the outbound Basic-auth header. Refuse to start +// rather than run silently unauthenticated. +function requireSecret(name: string, value: string | undefined): string { + const secret = value?.trim(); + if (!secret) { + process.stderr.write( + `\n[trapos-server] ${name} is empty or unset. Set it in .env ` + + `(run \`just install\` to generate secrets). Refusing to start.\n\n`, + ); + process.exit(1); + } + return secret; +} + function readPort(value: string | undefined, fallback: number): number { return readPositiveInt(value, fallback); }