fix(docker): avoid printing gateway token

This commit is contained in:
Vincent Koc
2026-05-23 11:21:05 +02:00
parent 6e3b3183dd
commit 5db773fad8
7 changed files with 88 additions and 29 deletions

View File

@@ -54,6 +54,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Memory/LanceDB: expose public memory artifacts through the active memory provider bridge so memory-wiki imports durable memory files, daily notes, dream reports, and event logs without depending on memory-core internals. Fixes #83604. (#85060) Thanks @brokemac79.
- Docker setup: stop printing the Gateway bearer token in setup logs and printed follow-up commands.
- Agents: let embedded compaction fallback retries proceed when PI-compatible candidates do not need agent harness plugin preparation.
- Agents/tools: honor configured custom provider API keys when deciding whether media, image-generation, video-generation, music-generation, and PDF tools are available. (#85570)
- StepFun: stop advertising stale generic API key auth choices so onboarding only offers runtime-backed Standard and Step Plan choices.

View File

@@ -572,11 +572,17 @@ else
else
echo "Bonjour/mDNS advertising: explicitly enabled (OPENCLAW_DISABLE_BONJOUR=$OPENCLAW_DISABLE_BONJOUR)."
fi
echo "Gateway token: $OPENCLAW_GATEWAY_TOKEN"
echo "Gateway token: stored in Docker environment/config (not printed)."
echo "Tailscale exposure: Off (use host-level tailnet/Tailscale setup separately)."
echo "Install Gateway daemon: No (managed by Docker Compose)"
echo ""
run_prestart_cli onboard --mode local --no-install-daemon
run_prestart_cli onboard \
--mode local \
--no-install-daemon \
--gateway-auth token \
--gateway-token-ref-env OPENCLAW_GATEWAY_TOKEN \
--skip-ui \
--suppress-gateway-token-output
fi
echo ""
@@ -711,8 +717,8 @@ echo "Gateway running with host port mapping."
echo "Access from tailnet devices via the host's tailnet IP."
echo "Config: $OPENCLAW_CONFIG_DIR"
echo "Workspace: $OPENCLAW_WORKSPACE_DIR"
echo "Token: $OPENCLAW_GATEWAY_TOKEN"
echo "Token: stored in Docker environment/config (not printed)."
echo ""
echo "Commands:"
echo " ${COMPOSE_HINT} logs -f openclaw-gateway"
echo " ${COMPOSE_HINT} exec openclaw-gateway node dist/index.js health --token \"$OPENCLAW_GATEWAY_TOKEN\""
echo " ${COMPOSE_HINT} exec openclaw-gateway sh -lc 'node dist/index.js health --token \"\$OPENCLAW_GATEWAY_TOKEN\"'"

View File

@@ -167,6 +167,7 @@ export function registerOnboardCommand(program: Command): void {
.option("--skip-search", "Skip search provider setup")
.option("--skip-health", "Skip health check")
.option("--skip-ui", "Skip Control UI/TUI prompts")
.option("--suppress-gateway-token-output", "Suppress token-bearing Gateway/UI output")
.option("--skip-hooks", "Skip hook setup")
.option("--node-manager <name>", "Node manager for skills: npm|pnpm|bun")
.option("--import-from <provider>", "Migration provider to run during onboarding")
@@ -246,6 +247,7 @@ export function registerOnboardCommand(program: Command): void {
skipSearch: Boolean(opts.skipSearch),
skipHealth: Boolean(opts.skipHealth),
skipUi: Boolean(opts.skipUi),
suppressGatewayTokenOutput: Boolean(opts.suppressGatewayTokenOutput),
skipHooks: Boolean(opts.skipHooks),
nodeManager: opts.nodeManager as NodeManagerChoice | undefined,
importFrom: opts.importFrom as string | undefined,

View File

@@ -79,6 +79,7 @@ export type OnboardOptions = OnboardDynamicProviderOptions & {
skipSearch?: boolean;
skipHealth?: boolean;
skipUi?: boolean;
suppressGatewayTokenOutput?: boolean;
skipHooks?: boolean;
nodeManager?: NodeManagerChoice;
remoteUrl?: string;

View File

@@ -141,7 +141,8 @@ function runDockerSetup(
cwd: sandbox.rootDir,
env: createEnv(sandbox, overrides),
encoding: "utf8",
stdio: ["ignore", "ignore", "pipe"],
maxBuffer: 4 * 1024 * 1024,
stdio: ["ignore", "pipe", "pipe"],
});
}
@@ -284,8 +285,11 @@ describe("scripts/docker/setup.sh", () => {
const log = await readDockerLog(activeSandbox);
expect(log).toContain("--build-arg OPENCLAW_IMAGE_APT_PACKAGES=curl wget");
expect(log).toContain(
`run --rm --no-deps ${prestartContainerEnvFlags} --entrypoint node openclaw-gateway dist/index.js onboard --mode local --no-install-daemon`,
`run --rm --no-deps ${prestartContainerEnvFlags} --entrypoint node openclaw-gateway dist/index.js onboard --mode local --no-install-daemon --gateway-auth token --gateway-token-ref-env OPENCLAW_GATEWAY_TOKEN --skip-ui --suppress-gateway-token-output`,
);
expect(result.stdout).toContain("Gateway token: stored in Docker environment/config");
expect(result.stdout).not.toContain("test-token");
expect(result.stdout).not.toContain("#token=");
expect(log).toContain(
`run --rm --no-deps ${prestartContainerEnvFlags} --entrypoint node openclaw-gateway dist/index.js config set --batch-json [{"path":"gateway.mode","value":"local"},{"path":"gateway.bind","value":"lan"},{"path":"gateway.controlUi.allowedOrigins","value":["http://localhost:18789","http://127.0.0.1:18789"]}]`,
);
@@ -702,7 +706,9 @@ describe("scripts/docker/setup.sh", () => {
expect(result.status).toBe(0);
const log = await readDockerLog(activeSandbox);
expect(log).toContain("onboard --mode local --no-install-daemon");
expect(log).toContain(
"onboard --mode local --no-install-daemon --gateway-auth token --gateway-token-ref-env OPENCLAW_GATEWAY_TOKEN --skip-ui --suppress-gateway-token-output",
);
const envFile = await readFile(join(activeSandbox.rootDir, ".env"), "utf8");
expect(envFile).toMatch(/OPENCLAW_SKIP_ONBOARDING=\n/);
});

View File

@@ -594,6 +594,43 @@ describe("finalizeSetupWizard", () => {
expect(gatewayServiceInstall).toHaveBeenCalledTimes(1);
});
it("suppresses token-bearing onboarding output when requested", async () => {
const prompter = createLaterPrompter();
await finalizeSetupWizard({
flow: "advanced",
opts: {
acceptRisk: true,
authChoice: "skip",
installDaemon: false,
skipHealth: true,
skipUi: true,
suppressGatewayTokenOutput: true,
},
baseConfig: {},
nextConfig: {},
workspaceDir: "/tmp",
settings: {
port: 18789,
bind: "loopback",
authMode: "token",
gatewayToken: "session-token",
tailscaleMode: "off",
tailscaleResetOnExit: false,
},
prompter,
runtime: createRuntime(),
});
const output = vi
.mocked(prompter.note)
.mock.calls.map((call) => call.join("\n"))
.join("\n");
expect(output).toContain("http://127.0.0.1:18789");
expect(output).not.toContain("session-token");
expect(output).not.toContain("#token=");
});
it("stops after a scheduled restart instead of reinstalling the service", async () => {
const progressUpdate = vi.fn();
const progressStop = vi.fn();

View File

@@ -77,6 +77,7 @@ export async function finalizeSetupWizard(
options: FinalizeOnboardingOptions,
): Promise<{ launchedTui: boolean }> {
const { flow, opts, baseConfig, nextConfig, settings, prompter, runtime } = options;
const suppressGatewayTokenOutput = opts.suppressGatewayTokenOutput === true;
let gatewayProbe: { ok: boolean; detail?: string } = { ok: true };
let resolvedGatewayPassword = "";
@@ -392,7 +393,7 @@ export async function finalizeSetupWizard(
tlsEnabled: nextConfig.gateway?.tls?.enabled === true,
});
const authedUrl =
settings.authMode === "token" && settings.gatewayToken
settings.authMode === "token" && settings.gatewayToken && !suppressGatewayTokenOutput
? `${links.httpUrl}#token=${encodeURIComponent(settings.gatewayToken)}`
: links.httpUrl;
if (opts.skipHealth || !gatewayProbe.ok) {
@@ -419,7 +420,7 @@ export async function finalizeSetupWizard(
await prompter.note(
[
t("wizard.finalize.webUiUrl", { url: links.httpUrl }),
settings.authMode === "token" && settings.gatewayToken
settings.authMode === "token" && settings.gatewayToken && !suppressGatewayTokenOutput
? t("wizard.finalize.webUiWithTokenUrl", { url: authedUrl })
: undefined,
t("wizard.finalize.gatewayWsUrl", { url: links.wsUrl }),
@@ -450,24 +451,22 @@ export async function finalizeSetupWizard(
}
if (gatewayProbe.ok) {
await prompter.note(
[
t("wizard.finalize.gatewayTokenShared"),
t("wizard.finalize.gatewayTokenStored"),
t("wizard.finalize.gatewayTokenView", {
command: formatCliCommand("openclaw config get gateway.auth.token"),
}),
t("wizard.finalize.gatewayTokenGenerate", {
command: formatCliCommand("openclaw doctor --generate-gateway-token"),
}),
t("wizard.finalize.dashboardTokenMemory"),
t("wizard.finalize.dashboardOpenAnytime", {
command: formatCliCommand("openclaw dashboard --no-open"),
}),
t("wizard.finalize.dashboardTokenPrompt"),
].join("\n"),
"Token",
);
const tokenNotes = [
t("wizard.finalize.gatewayTokenShared"),
t("wizard.finalize.gatewayTokenStored"),
t("wizard.finalize.gatewayTokenView", {
command: formatCliCommand("openclaw config get gateway.auth.token"),
}),
t("wizard.finalize.gatewayTokenGenerate", {
command: formatCliCommand("openclaw doctor --generate-gateway-token"),
}),
suppressGatewayTokenOutput ? undefined : t("wizard.finalize.dashboardTokenMemory"),
t("wizard.finalize.dashboardOpenAnytime", {
command: formatCliCommand("openclaw dashboard --no-open"),
}),
suppressGatewayTokenOutput ? undefined : t("wizard.finalize.dashboardTokenPrompt"),
].filter(Boolean);
await prompter.note(tokenNotes.join("\n"), "Token");
}
const hatchOptions: { value: "tui" | "web" | "later"; label: string }[] = [
@@ -505,14 +504,20 @@ export async function finalizeSetupWizard(
controlUiOpenHint = formatControlUiSshHint({
port: settings.port,
basePath: controlUiBasePath,
token: settings.authMode === "token" ? settings.gatewayToken : undefined,
token:
settings.authMode === "token" && !suppressGatewayTokenOutput
? settings.gatewayToken
: undefined,
});
}
} else {
controlUiOpenHint = formatControlUiSshHint({
port: settings.port,
basePath: controlUiBasePath,
token: settings.authMode === "token" ? settings.gatewayToken : undefined,
token:
settings.authMode === "token" && !suppressGatewayTokenOutput
? settings.gatewayToken
: undefined,
});
}
await prompter.note(
@@ -553,6 +558,7 @@ export async function finalizeSetupWizard(
gatewayProbe.ok &&
settings.authMode === "token" &&
Boolean(settings.gatewayToken) &&
!suppressGatewayTokenOutput &&
hatchChoice === null;
if (shouldOpenControlUi) {
const browserSupport = await detectBrowserOpenSupport();