#!/usr/bin/env node import childProcess from "node:child_process"; import fs from "node:fs"; import net from "node:net"; import os from "node:os"; import path from "node:path"; import process from "node:process"; import { setTimeout as delay } from "node:timers/promises"; import { pathToFileURL } from "node:url"; const PLUGIN_ID = "secret-provider-proof"; const INTEGRATION_ID = "vault"; const PROVIDER_ALIAS = "team-secrets"; const TOKEN_V1 = "proof-gateway-token-v1"; const TOKEN_V2 = "proof-gateway-token-v2"; const ENV_TOKEN = "proof-env-token"; const FILE_TOKEN = "proof-file-token"; const MANUAL_EXEC_TOKEN = "proof-manual-exec-token"; const PLUGIN_EXEC_TOKEN = "proof-plugin-exec-token"; const OPENAI_PROFILE = "openai:secretref-proof"; const OPENAI_LIVE_PROOF_MODEL = "openai/gpt-5.5"; const COMMAND_TIMEOUT_MS = readPositiveInt(process.env.OPENCLAW_SECRET_PROOF_COMMAND_MS, 120000); const READY_TIMEOUT_MS = readPositiveInt(process.env.OPENCLAW_SECRET_PROOF_READY_MS, 120000); const RPC_TIMEOUT_MS = readPositiveInt(process.env.OPENCLAW_SECRET_PROOF_RPC_MS, 15000); const TEARDOWN_GRACE_MS = 5000; const RESULTS_PATH = process.env.OPENCLAW_SECRET_PROOF_RESULTS_PATH?.trim() || path.join(os.tmpdir(), `openclaw-secret-provider-e2e-results-${process.pid}.json`); const results = []; let gatewayClientStateCounter = 0; function requireFullMatrix() { return process.env.OPENCLAW_SECRET_PROOF_FULL === "1"; } function readPositiveInt(raw, fallback) { const text = String(raw ?? "").trim(); if (!/^\d+$/u.test(text)) { return fallback; } const parsed = Number(text); return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback; } function remainingDeadlineMs(started, timeoutMs) { return Math.max(1, timeoutMs - (Date.now() - started)); } function formatErrorMessage(error) { if (error instanceof Error) { return error.message; } if (typeof error === "string") { return error; } if (error && typeof error === "object") { try { return JSON.stringify(error); } catch { return Object.prototype.toString.call(error); } } return String(error); } function writeJson(file, value) { fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`, "utf8"); } function readJson(file) { return JSON.parse(fs.readFileSync(file, "utf8")); } function scrub(text) { return String(text) .replaceAll(TOKEN_V1, "") .replaceAll(TOKEN_V2, "") .replaceAll(ENV_TOKEN, "") .replaceAll(FILE_TOKEN, "") .replaceAll(MANUAL_EXEC_TOKEN, "") .replaceAll(PLUGIN_EXEC_TOKEN, "") .replace(/sk-[A-Za-z0-9_-]{20,}/gu, ""); } function parseJsonOutput(stdout) { const text = stdout.trim(); if (!text) { throw new Error("expected JSON output, got empty stdout"); } const first = text.indexOf("{"); const last = text.lastIndexOf("}"); if (first < 0 || last < first) { throw new Error(`expected JSON object output, got: ${scrub(text.slice(0, 500))}`); } return JSON.parse(text.slice(first, last + 1)); } function resolveOpenClawRunner() { if (process.env.OPENCLAW_ENTRY) { return { command: "node", baseArgs: [process.env.OPENCLAW_ENTRY], label: process.env.OPENCLAW_ENTRY, }; } if (process.env.OPENCLAW_SECRET_PROOF_USE_DIST === "1") { for (const candidate of ["dist/index.mjs", "dist/index.js"]) { const resolved = path.join(process.cwd(), candidate); if (fs.existsSync(resolved)) { return { command: "node", baseArgs: [resolved], label: candidate }; } } } return { pnpm: true, baseArgs: ["openclaw"], label: "pnpm openclaw" }; } function makeEnv(name) { const root = fs.mkdtempSync(path.join(os.tmpdir(), `openclaw-secret-proof-${name}-`)); const home = path.join(root, "home"); const stateDir = path.join(home, ".openclaw"); const agentDir = path.join(stateDir, "agents", "main", "agent"); const hostHome = os.homedir(); const serviceProfile = `secret-proof-${process.pid}-${name.replace(/[^a-z0-9-]/giu, "-")}`; fs.mkdirSync(stateDir, { recursive: true, mode: 0o755 }); const env = { ...process.env, HOME: home, USERPROFILE: home, OPENCLAW_HOME: home, OPENCLAW_STATE_DIR: stateDir, OPENCLAW_CONFIG_PATH: path.join(stateDir, "openclaw.json"), OPENCLAW_AGENT_DIR: agentDir, PI_CODING_AGENT_DIR: "", OPENCLAW_NO_ONBOARD: "1", OPENCLAW_SKIP_PROVIDERS: "0", OPENCLAW_LOG_COLOR: "0", OPENCLAW_PROFILE: serviceProfile, OPENCLAW_LAUNCHD_LABEL: `ai.openclaw.${serviceProfile}`, OPENCLAW_SYSTEMD_UNIT: `openclaw-gateway-${serviceProfile}.service`, OPENCLAW_WINDOWS_TASK_NAME: `OpenClaw Gateway (${serviceProfile})`, NO_COLOR: "1", PNPM_HOME: process.env.PNPM_HOME ?? (process.platform === "darwin" ? path.join(hostHome, "Library", "pnpm") : path.join(hostHome, ".local", "share", "pnpm")), COREPACK_HOME: process.env.COREPACK_HOME ?? (process.platform === "darwin" ? path.join(hostHome, "Library", "Caches", "node", "corepack") : path.join(hostHome, ".cache", "node", "corepack")), XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ?? path.join(hostHome, ".cache"), }; delete env.OPENCLAW_GATEWAY_TOKEN; delete env.OPENCLAW_GATEWAY_PASSWORD; return { root, home, stateDir, env }; } async function cleanupEnv(root) { if (process.env.OPENCLAW_SECRET_PROOF_KEEP_TMP === "1") { console.log(`[keep] ${root}`); return; } for (let attempt = 0; attempt < 5; attempt += 1) { try { fs.rmSync(root, { recursive: true, force: true }); return; } catch { await delay(250); } } } function runCommand(command, args, options = {}) { const timeoutMs = options.timeoutMs ?? COMMAND_TIMEOUT_MS; return new Promise((resolve, reject) => { const child = childProcess.spawn(command, args, { cwd: options.cwd ?? process.cwd(), env: options.env ?? process.env, shell: options.shell, stdio: options.stdio ?? ["pipe", "pipe", "pipe"], windowsVerbatimArguments: options.windowsVerbatimArguments, }); let stdout = ""; let stderr = ""; const timer = setTimeout(() => { child.kill("SIGTERM"); setTimeout(() => child.kill("SIGKILL"), 1000).unref(); reject(new Error(scrub(`command timed out: ${command} ${args.join(" ")}`))); }, timeoutMs); child.stdout.on("data", (chunk) => { stdout += chunk.toString("utf8"); }); child.stderr.on("data", (chunk) => { stderr += chunk.toString("utf8"); }); child.on("error", (error) => { clearTimeout(timer); reject(error); }); child.on("close", (code, signal) => { clearTimeout(timer); const result = { code: code ?? 0, signal, stdout, stderr }; if (result.code !== 0 && options.allowFailure !== true) { reject( new Error( scrub( `command failed (${result.code}): ${command} ${args.join(" ")}\n${stderr || stdout}`, ), ), ); return; } resolve(result); }); if (options.input !== undefined) { child.stdin.end(options.input); } else { child.stdin.end(); } }); } async function runOpenClaw(args, env, options = {}) { const command = await resolveOpenClawCommand(args, env, options); return await runCommand(command.command, command.args, { ...options, ...command.options, }); } export async function resolveOpenClawCommand(args, env, options = {}) { const runner = options.runner ?? resolveOpenClawRunner(); const stdio = options.stdio ?? ["pipe", "pipe", "pipe"]; if (runner.pnpm) { const { createPnpmRunnerSpawnSpec } = await import("../pnpm-runner.mjs"); return createPnpmRunnerSpawnSpec({ comSpec: options.comSpec, cwd: options.cwd ?? process.cwd(), detached: options.detached, env, nodeExecPath: options.nodeExecPath, npmExecPath: options.npmExecPath, platform: options.platform, pnpmArgs: [...runner.baseArgs, ...args], stdio, }); } return { command: runner.command, args: [...runner.baseArgs, ...args], options: { cwd: options.cwd ?? process.cwd(), detached: options.detached, env, shell: options.shell, stdio, windowsVerbatimArguments: options.windowsVerbatimArguments, }, }; } async function allocatePort() { const server = net.createServer(); await new Promise((resolve, reject) => { server.once("error", reject); server.listen(0, "127.0.0.1", resolve); }); const address = server.address(); const port = typeof address === "object" && address ? address.port : 0; await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())), ); if (!port) { throw new Error("failed to allocate a local port"); } return port; } function proofProviderConfig() { return { source: "exec", pluginIntegration: { pluginId: PLUGIN_ID, integrationId: INTEGRATION_ID, }, }; } function proofSecretRef(id) { return { source: "exec", provider: PROVIDER_ALIAS, id }; } function baseConfig(port, overrides = {}) { return { gateway: { mode: "local", port, bind: "loopback", auth: { mode: "token", token: proofSecretRef("gateway/token") }, controlUi: { enabled: false }, ...overrides.gateway, }, plugins: { enabled: true, entries: { [PLUGIN_ID]: { enabled: true }, }, ...overrides.plugins, }, secrets: { providers: { [PROVIDER_ALIAS]: proofProviderConfig(), }, ...overrides.secrets, }, agents: { defaults: { model: "openai/gpt-5.4-nano", }, ...overrides.agents, }, ...overrides.root, }; } function writeProofPlugin(envCtx, options = {}) { const pluginRoot = path.join(envCtx.stateDir, "extensions", PLUGIN_ID); fs.mkdirSync(pluginRoot, { recursive: true, mode: 0o755 }); writeJson(path.join(pluginRoot, "openclaw.plugin.json"), { id: PLUGIN_ID, name: "Secret Provider Proof", enabledByDefault: true, activation: { onStartup: true }, secretProviderIntegrations: { [INTEGRATION_ID]: { source: "exec", command: "${node}", args: ["./resolver.mjs"], providerAlias: PROVIDER_ALIAS, displayName: "Secret Provider Proof", description: "Local E2E proof resolver for plugin-managed SecretRef providers.", timeoutMs: 1200, noOutputTimeoutMs: 800, maxOutputBytes: 8192, passEnv: [ "PROOF_SECRET_STORE_PATH", ...(options.includeOpenAiPassEnv ? ["OPENAI_API_KEY"] : []), ], env: { PROOF_PLUGIN_ENV: "manifest" }, }, }, configSchema: { type: "object", additionalProperties: false, properties: {}, }, }); fs.writeFileSync( path.join(pluginRoot, "index.js"), `import fs from "node:fs"; import { createRequire } from "node:module"; import path from "node:path"; import { pathToFileURL } from "node:url"; const OPENAI_PROFILE = ${JSON.stringify(OPENAI_PROFILE)}; const EXPECTED_ID = "plugin-exec/token"; const EXPECTED_VALUE = ${JSON.stringify(PLUGIN_EXEC_TOKEN)}; const REPO_ROOT = ${JSON.stringify(process.cwd())}; function resolveAuthProfilesPath() { const agentDir = process.env.OPENCLAW_AGENT_DIR; if (agentDir) { return path.join(agentDir, "auth-profiles.json"); } const stateDir = process.env.OPENCLAW_STATE_DIR; if (stateDir) { return path.join(stateDir, "agents", "main", "agent", "auth-profiles.json"); } throw new Error("missing agent profile directory environment"); } function readConfig() { const configPath = process.env.OPENCLAW_CONFIG_PATH; if (!configPath) { throw new Error("missing OPENCLAW_CONFIG_PATH"); } return JSON.parse(fs.readFileSync(configPath, "utf8")); } function readPersistedProfile() { const store = JSON.parse(fs.readFileSync(resolveAuthProfilesPath(), "utf8")); const profile = store.profiles?.[OPENAI_PROFILE]; const ref = profile?.keyRef; if ( !ref || ref.source !== "exec" || ref.provider !== "${PROVIDER_ALIAS}" || ref.id !== EXPECTED_ID ) { throw new Error("expected auth-profile SecretRef is not persisted"); } return ref; } async function loadSecretRuntime() { const requireFromRepo = createRequire(path.join(REPO_ROOT, "package.json")); const resolved = requireFromRepo.resolve("openclaw/plugin-sdk/secret-ref-runtime"); return await import(pathToFileURL(resolved).href); } async function resolveProfileSecretRef(ref) { const { resolveSecretRefValues } = await loadSecretRuntime(); const resolved = await resolveSecretRefValues([ref], { config: readConfig(), env: process.env, }); const values = Array.from(resolved.values()); if (values[0] !== EXPECTED_VALUE) { throw new Error("SecretRef resolver did not return expected persisted-profile secret"); } } export default { register(api) { api.registerGatewayMethod("secret-provider-proof.serviceProbe", async ({ respond }) => { try { const ref = readPersistedProfile(); await resolveProfileSecretRef(ref); respond(true, { ok: true, profileId: OPENAI_PROFILE, id: ref.id }, undefined); } catch (error) { respond(false, undefined, { code: "UNAVAILABLE", message: error instanceof Error ? error.message : String(error), }); } }); }, }; `, { mode: 0o644 }, ); fs.writeFileSync( path.join(pluginRoot, "resolver.mjs"), `#!/usr/bin/env node import fs from "node:fs"; const storePath = process.env.PROOF_SECRET_STORE_PATH; if (!storePath) { console.error("missing PROOF_SECRET_STORE_PATH"); process.exit(4); } function readStdin() { return new Promise((resolve) => { let body = ""; process.stdin.setEncoding("utf8"); process.stdin.on("data", (chunk) => { body += chunk; }); process.stdin.on("end", () => resolve(body)); }); } function readStore() { const store = JSON.parse(fs.readFileSync(storePath, "utf8")); store.calls = Number(store.calls || 0) + 1; fs.writeFileSync(storePath, JSON.stringify(store, null, 2) + "\\n", "utf8"); return store; } const request = JSON.parse(await readStdin()); const store = readStore(); if (Number(store.sleepMs || 0) > 0) { await new Promise((resolve) => setTimeout(resolve, Number(store.sleepMs))); } if (store.mode === "fail") { process.stdout.write(JSON.stringify({ protocolVersion: 1, errors: Object.fromEntries((request.ids || []).map((id) => [id, "proof resolver forced failure"])), })); process.exit(0); } const values = {}; const errors = {}; for (const id of request.ids || []) { const entry = store.values?.[id]; if (entry && typeof entry === "object" && typeof entry.env === "string") { const value = process.env[entry.env]; if (typeof value === "string" && value.length > 0) { values[id] = value; } else { errors[id] = "required environment variable is missing"; } } else if (entry !== undefined) { values[id] = entry; } else { errors[id] = "missing proof secret"; } } process.stdout.write(JSON.stringify({ protocolVersion: 1, ...(Object.keys(values).length ? { values } : {}), ...(Object.keys(errors).length ? { errors } : {}), })); `, { mode: 0o755 }, ); return { pluginRoot, resolverPath: path.join(pluginRoot, "resolver.mjs") }; } function writeSecretStore(envCtx, values = {}) { const storePath = path.join(envCtx.stateDir, "proof-secret-store.json"); writeJson(storePath, { mode: "ok", calls: 0, sleepMs: 0, values: { "gateway/token": TOKEN_V1, "command/value": "proof-command-value", "plugin-exec/token": PLUGIN_EXEC_TOKEN, "openai/apiKey": { env: "OPENAI_API_KEY" }, ...values, }, }); envCtx.env.PROOF_SECRET_STORE_PATH = storePath; return storePath; } function mutateStore(storePath, update) { const current = readJson(storePath); const next = update(current); writeJson(storePath, next ?? current); } function envWithout(source, keys) { const next = { ...source }; for (const key of keys) { delete next[key]; } return next; } function serviceManagerEnv(source) { const hostHome = os.homedir(); return { ...source, // systemd/launchd discover user service definitions from the real account // home, while OpenClaw state/config below remain pinned to the proof root. HOME: hostHome, USERPROFILE: hostHome, }; } async function startGateway(envCtx, port, token = TOKEN_V1) { const command = await resolveOpenClawCommand( [ "gateway", "run", "--port", String(port), "--bind", "loopback", "--allow-unconfigured", ], envCtx.env, { detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"], }, ); const child = childProcess.spawn(command.command, command.args, { ...command.options, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"], }); let stdout = ""; let stderr = ""; child.stdout.on("data", (chunk) => { stdout += chunk.toString("utf8"); }); child.stderr.on("data", (chunk) => { stderr += chunk.toString("utf8"); }); const started = Date.now(); let lastHealthResult; let lastHealthError; while (Date.now() - started < READY_TIMEOUT_MS) { if (child.exitCode !== null) { throw new Error( scrub(`gateway exited during startup (${child.exitCode})\n${stderr || stdout}`), ); } const remainingMs = remainingDeadlineMs(started, READY_TIMEOUT_MS); try { const health = await gatewayCall( envCtx.env, port, token, "health", {}, { allowFailure: true, timeoutMs: Math.min(RPC_TIMEOUT_MS + 10000, remainingMs), }, ); lastHealthResult = health; if (health.code === 0) { return { child, output: () => ({ stdout, stderr }), stop: async () => { await stopGateway(child); }, }; } } catch (error) { lastHealthError = error; } await delay(Math.min(500, remainingDeadlineMs(started, READY_TIMEOUT_MS))); } terminateProcessTree(child, "SIGTERM"); const lastHealthOutput = lastHealthError instanceof Error ? lastHealthError.message : lastHealthError ? formatErrorMessage(lastHealthError) : lastHealthResult ? lastHealthResult.stderr || lastHealthResult.stdout : ""; throw new Error(scrub(`gateway did not become ready\n${lastHealthOutput}\n${stderr || stdout}`)); } async function stopGateway(child) { if (!child || child.exitCode !== null) { return; } terminateProcessTree(child, "SIGTERM"); const started = Date.now(); while (Date.now() - started < TEARDOWN_GRACE_MS) { if (child.exitCode !== null) { return; } await delay(100); } terminateProcessTree(child, "SIGKILL"); } function terminateProcessTree(child, signal) { if (process.platform === "win32") { try { childProcess.spawnSync("taskkill", ["/pid", String(child.pid), "/t", "/f"], { stdio: "ignore", }); return; } catch { child.kill(signal); return; } } try { process.kill(-child.pid, signal); } catch { child.kill(signal); } } async function gatewayCall(env, port, token, method, params = {}, options = {}) { const clientStateDir = path.join( path.dirname(env.OPENCLAW_CONFIG_PATH), "gateway-call-clients", `${Date.now()}-${gatewayClientStateCounter++}`, ); fs.mkdirSync(clientStateDir, { recursive: true }); return await runOpenClaw( [ "gateway", "call", method, "--url", `ws://127.0.0.1:${port}`, "--token", token, "--timeout", String(RPC_TIMEOUT_MS), "--json", "--params", JSON.stringify(params), ], { ...env, OPENCLAW_STATE_DIR: clientStateDir, OPENCLAW_HOME: clientStateDir, }, { timeoutMs: options.timeoutMs ?? RPC_TIMEOUT_MS + 10000, allowFailure: options.allowFailure }, ); } async function expectGatewayCallOk(env, port, token, method = "health", params = {}) { const result = await gatewayCall(env, port, token, method, params); return parseJsonOutput(result.stdout); } async function expectGatewayCallFails(env, port, token, method = "health", params = {}) { const result = await gatewayCall(env, port, token, method, params, { allowFailure: true }); if (result.code === 0) { throw new Error(`expected gateway ${method} call to fail`); } return result; } async function expectReloadMayCloseForAuthChange(env, port, token) { const result = await gatewayCall(env, port, token, "secrets.reload", {}, { allowFailure: true }); if (result.code === 0) { return parseJsonOutput(result.stdout); } const output = scrub(`${result.stdout}\n${result.stderr}`); if (!/gateway auth changed/iu.test(output)) { throw new Error(`secrets.reload failed without auth-change close: ${output}`); } return { ok: true, connectionClosedForAuthChange: true }; } async function expectGatewayStartupFails(envCtx, port, reason) { const command = await resolveOpenClawCommand( [ "gateway", "run", "--port", String(port), "--bind", "loopback", "--allow-unconfigured", ], envCtx.env, { detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"], }, ); const child = childProcess.spawn(command.command, command.args, { ...command.options, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"], }); let stdout = ""; let stderr = ""; child.stdout.on("data", (chunk) => { stdout += chunk.toString("utf8"); }); child.stderr.on("data", (chunk) => { stderr += chunk.toString("utf8"); }); const code = await new Promise((resolve, reject) => { const timer = setTimeout(() => { terminateProcessTree(child, "SIGTERM"); reject(new Error(`gateway did not fail closed for ${reason}`)); }, 20000); child.on("close", (exitCode) => { clearTimeout(timer); resolve(exitCode); }); child.on("error", reject); }); if (code === 0) { throw new Error(`gateway unexpectedly started for ${reason}`); } const rawCombined = `${stdout}\n${stderr}`; for (const forbidden of [TOKEN_V1, TOKEN_V2, PLUGIN_EXEC_TOKEN]) { if (rawCombined.includes(forbidden)) { throw new Error(`startup failure for ${reason} leaked a secret value`); } } const combined = scrub(rawCombined); return combined; } async function uninstallManagedGateway(env) { let lastResult; for (let attempt = 1; attempt <= 2; attempt += 1) { lastResult = await runOpenClaw(["gateway", "uninstall", "--json"], env, { timeoutMs: 60000, allowFailure: true, }); if (lastResult.code === 0) { return; } if (attempt < 2) { await delay(1000); } } throw new Error( scrub( `managed gateway uninstall failed after service proof (${lastResult?.code ?? "unknown"}): ${ lastResult?.stderr || lastResult?.stdout || "" }`, ), ); } async function waitForManagedGatewayStatus(env, token) { const started = Date.now(); let lastResult; let lastError; while (Date.now() - started < READY_TIMEOUT_MS) { try { lastResult = await runOpenClaw( [ "gateway", "status", "--deep", "--require-rpc", "--json", "--token", token, "--timeout", String(RPC_TIMEOUT_MS), ], env, { timeoutMs: Math.min( RPC_TIMEOUT_MS + 10000, remainingDeadlineMs(started, READY_TIMEOUT_MS), ), allowFailure: true, }, ); if (lastResult.code === 0) { return parseJsonOutput(lastResult.stdout); } } catch (error) { lastError = error; } await delay(Math.min(500, remainingDeadlineMs(started, READY_TIMEOUT_MS))); } const lastOutput = lastError instanceof Error ? lastError.message : lastError ? formatErrorMessage(lastError) : lastResult?.stderr || lastResult?.stdout || ""; throw new Error(scrub(`managed gateway did not become RPC-ready\n${lastOutput}`)); } async function runWithProof(name, description, fn) { const started = Date.now(); try { const evidence = await fn(); const elapsedMs = Date.now() - started; results.push({ name, status: "pass", elapsedMs, evidence }); console.log( `[PASS] ${name} ${description} (${elapsedMs}ms) ${evidence ? scrub(evidence) : ""}`, ); } catch (error) { const elapsedMs = Date.now() - started; const message = error instanceof Error ? error.message : String(error); results.push({ name, status: "fail", elapsedMs, evidence: scrub(message) }); console.error(`[FAIL] ${name} ${description} (${elapsedMs}ms)`); console.error(scrub(message)); throw error; } } async function withProofEnv(name, fn, values, pluginOptions) { const envCtx = makeEnv(name); try { const plugin = writeProofPlugin(envCtx, pluginOptions); const storePath = writeSecretStore(envCtx, values); return await fn(envCtx, plugin, storePath); } finally { await cleanupEnv(envCtx.root); } } async function p1StartupSucceeds() { await withProofEnv("p1", async (envCtx, _plugin, storePath) => { const port = await allocatePort(); writeJson(envCtx.env.OPENCLAW_CONFIG_PATH, baseConfig(port)); const authPath = path.join(envCtx.stateDir, "agents", "main", "agent", "auth-profiles.json"); writeJson(authPath, { version: 1, profiles: { [OPENAI_PROFILE]: { type: "api_key", provider: "openai", keyRef: proofSecretRef("plugin-exec/token"), }, }, }); const gateway = await startGateway(envCtx, port, TOKEN_V1); try { await expectGatewayCallOk(envCtx.env, port, TOKEN_V1); const callsBeforeProbe = readJson(storePath).calls; const probe = await expectGatewayCallOk( envCtx.env, port, TOKEN_V1, "secret-provider-proof.serviceProbe", ); if (probe.ok !== true || probe.profileId !== OPENAI_PROFILE) { throw new Error("proof plugin serviceProbe returned unexpected payload"); } const callsAfterProbe = readJson(storePath).calls; if (callsAfterProbe <= callsBeforeProbe) { throw new Error("proof plugin serviceProbe did not invoke the SecretRef resolver"); } } finally { await gateway.stop(); } }); return "gateway health succeeded and proof plugin resolved persisted keyRef through SecretRef API"; } async function p2StartupFailsClosed() { return await withProofEnv("p2", async (envCtx, _plugin, storePath) => { const port = await allocatePort(); writeJson(envCtx.env.OPENCLAW_CONFIG_PATH, baseConfig(port)); mutateStore(storePath, (store) => ({ ...store, mode: "fail" })); const output = await expectGatewayStartupFails(envCtx, port, "unresolved plugin integration"); if (!/secret|ref|resolve|provider/iu.test(output)) { throw new Error(`startup failure did not include actionable SecretRef context: ${output}`); } return "gateway exited non-zero without exposing resolved credential"; }); } async function p3ThroughP6StaticReloadAndCommandSnapshot() { await withProofEnv("p3-p6", async (envCtx, _plugin, storePath) => { const port = await allocatePort(); writeJson(envCtx.env.OPENCLAW_CONFIG_PATH, baseConfig(port)); const gateway = await startGateway(envCtx, port, TOKEN_V1); try { const before = readJson(storePath).calls; mutateStore(storePath, (store) => ({ ...store, values: { ...store.values, "gateway/token": TOKEN_V2 }, })); await expectGatewayCallOk(envCtx.env, port, TOKEN_V1); await expectGatewayCallFails(envCtx.env, port, TOKEN_V2); const afterStaticCalls = readJson(storePath).calls; if (afterStaticCalls !== before) { throw new Error( `resolver was called after static capture (${before} -> ${afterStaticCalls})`, ); } await expectReloadMayCloseForAuthChange(envCtx.env, port, TOKEN_V1); await expectGatewayCallOk(envCtx.env, port, TOKEN_V2); await expectGatewayCallFails(envCtx.env, port, TOKEN_V1); mutateStore(storePath, (store) => ({ ...store, mode: "fail" })); await expectGatewayCallFails(envCtx.env, port, TOKEN_V2, "secrets.reload"); await expectGatewayCallOk(envCtx.env, port, TOKEN_V2); mutateStore(storePath, (store) => ({ ...store, mode: "ok" })); const resolved = await expectGatewayCallOk(envCtx.env, port, TOKEN_V2, "secrets.resolve", { commandName: "secret-provider-proof", targetIds: ["gateway.auth.token"], allowedPaths: ["gateway.auth.token"], forcedActivePaths: ["gateway.auth.token"], }); const assignment = resolved.assignments?.find?.( (entry) => entry.path === "gateway.auth.token", ); if (!assignment || assignment.value !== TOKEN_V2) { throw new Error( "secrets.resolve did not return the active gateway.auth.token snapshot value", ); } } finally { await gateway.stop(); } }); return "static capture, reload success, reload LKG, and command snapshot resolution proved"; } async function p7AuthProfileSecretRefPersistsAndResolves() { await withProofEnv("p7", async (envCtx, _plugin, storePath) => { const port = await allocatePort(); writeJson( envCtx.env.OPENCLAW_CONFIG_PATH, baseConfig(port, { root: { models: { providers: { openai: {}, }, }, }, }), ); const authPath = path.join(envCtx.stateDir, "agents", "main", "agent", "auth-profiles.json"); writeJson(authPath, { version: 1, profiles: { [OPENAI_PROFILE]: { type: "api_key", provider: "openai", keyRef: proofSecretRef("plugin-exec/token"), }, }, }); const callsBefore = readJson(storePath).calls; const result = await runOpenClaw( [ "models", "status", "--json", "--probe", "--probe-provider", "openai", "--probe-profile", OPENAI_PROFILE, "--probe-timeout", "15000", ], envCtx.env, { allowFailure: true, timeoutMs: 45000 }, ); const combined = scrub(`${result.stdout}\n${result.stderr}`); if ( /unresolved_ref|could not resolve SecretRef|missing PROOF_SECRET_STORE_PATH/iu.test(combined) ) { throw new Error( `auth-profile SecretRef did not resolve through plugin integration: ${combined}`, ); } const callsAfter = readJson(storePath).calls; if (callsAfter <= callsBefore) { throw new Error("auth-profile proof did not invoke the plugin-managed resolver"); } if (!combined.includes(OPENAI_PROFILE)) { throw new Error(`auth-profile proof did not mention expected profile ${OPENAI_PROFILE}`); } }); return "auth-profile keyRef reached the model status probe without unresolved-ref diagnostics"; } async function p8ManagedServiceEnvProof() { if (process.env.OPENCLAW_SECRET_PROOF_SERVICE !== "1") { if (requireFullMatrix()) { throw new Error("OPENCLAW_SECRET_PROOF_SERVICE=1 is required for full matrix service proof"); } return "not run in local rehearsal; final matrix must set OPENCLAW_SECRET_PROOF_SERVICE=1 on a service-capable host"; } await withProofEnv("p8", async (envCtx) => { const port = await allocatePort(); writeJson( envCtx.env.OPENCLAW_CONFIG_PATH, baseConfig(port, { gateway: { auth: { mode: "token", token: TOKEN_V1 } }, }), ); const authPath = path.join(envCtx.stateDir, "agents", "main", "agent", "auth-profiles.json"); writeJson(authPath, { version: 1, profiles: { [OPENAI_PROFILE]: { type: "api_key", provider: "openai", keyRef: proofSecretRef("plugin-exec/token"), }, }, }); let installAttempted = false; let proofError; let cleanupError; const managerEnv = serviceManagerEnv(envCtx.env); try { const callsBeforeInstall = readJson(envCtx.env.PROOF_SECRET_STORE_PATH).calls; installAttempted = true; const install = await runOpenClaw( ["gateway", "install", "--force", "--port", String(port), "--json"], managerEnv, { timeoutMs: 120000 }, ); const payload = parseJsonOutput(install.stdout); if (payload.ok !== true) { throw new Error( `gateway install did not succeed: ${scrub(install.stdout || install.stderr)}`, ); } const callsAfterInstall = readJson(envCtx.env.PROOF_SECRET_STORE_PATH).calls; if (callsAfterInstall !== callsBeforeInstall) { throw new Error( "managed service proof unexpectedly resolved the plugin SecretRef during install", ); } await waitForManagedGatewayStatus(managerEnv, TOKEN_V1); const callsBeforeProbe = readJson(envCtx.env.PROOF_SECRET_STORE_PATH).calls; const probe = await expectGatewayCallOk( envWithout(envCtx.env, ["PROOF_SECRET_STORE_PATH"]), port, TOKEN_V1, "secret-provider-proof.serviceProbe", ); if (probe.ok !== true || probe.profileId !== OPENAI_PROFILE) { throw new Error(`managed service proof method returned unexpected payload`); } const callsAfterProbe = readJson(envCtx.env.PROOF_SECRET_STORE_PATH).calls; if (callsAfterProbe <= callsBeforeProbe) { throw new Error("managed service auth-profile proof did not invoke the resolver"); } } catch (error) { proofError = error; } finally { if (installAttempted) { try { await uninstallManagedGateway(managerEnv); } catch (error) { cleanupError = error; if (proofError) { const message = error instanceof Error ? error.message : String(error); console.error(`[cleanup] ${scrub(message)}`); } } } } if (proofError) { throw proofError; } if (cleanupError) { throw cleanupError; } }); return "real managed service install preserved auth-profile exec provider passEnv"; } async function p9ProviderVariants() { await withProofEnv("p9", async (envCtx, plugin, storePath) => { const scenarios = [ { name: "env", token: ENV_TOKEN, env: { PROOF_GATEWAY_TOKEN: ENV_TOKEN }, config: (port) => baseConfig(port, { gateway: { auth: { mode: "token", token: { source: "env", provider: "default", id: "PROOF_GATEWAY_TOKEN" }, }, }, secrets: { providers: { default: { source: "env", allowlist: ["PROOF_GATEWAY_TOKEN"] } }, }, }), }, { name: "file", token: FILE_TOKEN, before: () => { const filePath = path.join(envCtx.stateDir, "file-secret.txt"); fs.writeFileSync(filePath, FILE_TOKEN, { mode: 0o600 }); return { filePath }; }, config: (port, ctx) => baseConfig(port, { gateway: { auth: { mode: "token", token: { source: "file", provider: "filemain", id: "value" } }, }, secrets: { providers: { filemain: { source: "file", path: ctx.filePath, mode: "singleValue" }, }, }, }), }, { name: "manual exec", token: MANUAL_EXEC_TOKEN, before: () => { mutateStore(storePath, (store) => ({ ...store, values: { ...store.values, "manual-exec/token": MANUAL_EXEC_TOKEN }, })); return {}; }, config: (port) => baseConfig(port, { gateway: { auth: { mode: "token", token: { source: "exec", provider: "manualexec", id: "manual-exec/token" }, }, }, secrets: { providers: { manualexec: { source: "exec", command: process.execPath, args: [plugin.resolverPath], trustedDirs: [plugin.pluginRoot, path.dirname(process.execPath)], passEnv: ["PROOF_SECRET_STORE_PATH"], timeoutMs: 1200, noOutputTimeoutMs: 800, }, }, }, }), }, { name: "plugin exec", token: PLUGIN_EXEC_TOKEN, config: (port) => baseConfig(port, { gateway: { auth: { mode: "token", token: proofSecretRef("plugin-exec/token") } }, }), }, ]; for (const scenario of scenarios) { const port = await allocatePort(); const ctx = scenario.before?.() ?? {}; writeJson(envCtx.env.OPENCLAW_CONFIG_PATH, scenario.config(port, ctx)); const childEnv = { ...envCtx.env, ...scenario.env }; const scenarioCtx = { ...envCtx, env: childEnv }; const gateway = await startGateway(scenarioCtx, port, scenario.token); try { await expectGatewayCallOk(childEnv, port, scenario.token); } finally { await gateway.stop(); } } }); return "env, file, manual exec, and plugin exec providers each authenticated a live gateway"; } async function p10UntrustedPluginFailsClosed() { return await withProofEnv("p10", async (envCtx) => { const port = await allocatePort(); writeJson( envCtx.env.OPENCLAW_CONFIG_PATH, baseConfig(port, { plugins: { entries: { [PLUGIN_ID]: { enabled: false }, }, }, }), ); await expectGatewayStartupFails(envCtx, port, "disabled plugin integration"); return "disabled plugin integration blocked startup"; }); } async function p11TimeoutFailClosedAndLkg() { await withProofEnv("p11", async (envCtx, _plugin, storePath) => { const failPort = await allocatePort(); writeJson(envCtx.env.OPENCLAW_CONFIG_PATH, baseConfig(failPort)); mutateStore(storePath, (store) => ({ ...store, sleepMs: 3000 })); await expectGatewayStartupFails(envCtx, failPort, "resolver timeout"); mutateStore(storePath, (store) => ({ ...store, sleepMs: 0 })); const port = await allocatePort(); writeJson(envCtx.env.OPENCLAW_CONFIG_PATH, baseConfig(port)); const gateway = await startGateway(envCtx, port, TOKEN_V1); try { mutateStore(storePath, (store) => ({ ...store, sleepMs: 3000 })); await expectGatewayCallFails(envCtx.env, port, TOKEN_V1, "secrets.reload"); mutateStore(storePath, (store) => ({ ...store, sleepMs: 0 })); await expectGatewayCallOk(envCtx.env, port, TOKEN_V1); } finally { await gateway.stop(); } }); return "timeout fails startup and reload timeout preserves Last Known Good"; } async function p12OpenAiLiveProof() { if (!process.env.OPENAI_API_KEY) { if (requireFullMatrix()) { throw new Error("OPENAI_API_KEY is required for full matrix OpenAI proof"); } return "OPENAI_API_KEY not present; final live matrix must forward the provided OpenAI env profile"; } await withProofEnv( "p12", async (envCtx, _plugin, storePath) => { const port = await allocatePort(); writeJson( envCtx.env.OPENCLAW_CONFIG_PATH, baseConfig(port, { agents: { defaults: { model: OPENAI_LIVE_PROOF_MODEL } } }), ); const authPath = path.join(envCtx.stateDir, "agents", "main", "agent", "auth-profiles.json"); writeJson(authPath, { version: 1, profiles: { [OPENAI_PROFILE]: { type: "api_key", provider: "openai", keyRef: proofSecretRef("openai/apiKey"), }, }, }); const callsBefore = readJson(storePath).calls; const result = await runOpenClaw( [ "models", "status", "--json", "--probe", "--probe-provider", "openai", "--probe-profile", OPENAI_PROFILE, "--probe-timeout", "60000", "--probe-max-tokens", "8", ], envCtx.env, { timeoutMs: 90000, allowFailure: true }, ); const combined = scrub(`${result.stdout}\n${result.stderr}`); if (result.code !== 0) { throw new Error(`OpenAI live probe failed: ${combined}`); } const payload = parseJsonOutput(result.stdout); const probeResult = payload.auth?.probes?.results?.find?.( (entry) => entry?.profileId === OPENAI_PROFILE && entry?.source === "profile", ); if (!probeResult || probeResult.status !== "ok") { throw new Error(`OpenAI live probe did not report ok for ${OPENAI_PROFILE}: ${combined}`); } const callsAfter = readJson(storePath).calls; if (callsAfter <= callsBefore) { throw new Error("OpenAI proof did not invoke the plugin-managed resolver"); } if (!combined.includes(OPENAI_PROFILE)) { throw new Error(`OpenAI proof did not mention expected profile ${OPENAI_PROFILE}`); } if (!/openai/iu.test(combined) || /unresolved_ref/iu.test(combined)) { throw new Error(`OpenAI live probe did not produce usable OpenAI proof: ${combined}`); } }, undefined, { includeOpenAiPassEnv: true }, ); return "OpenAI model auth probe consumed API key through plugin-managed auth-profile SecretRef"; } async function runPtySecretsConfigurePreset(envCtx) { const { spawn } = await import("@lydell/node-pty"); const command = await resolveOpenClawCommand( [ "secrets", "configure", "--providers-only", "--apply", "--yes", "--allow-exec", "--json", ], envCtx.env, ); const child = spawn( command.command, command.args, { name: "xterm-256color", cols: 100, rows: 30, cwd: command.options.cwd ?? process.cwd(), env: command.options.env ?? envCtx.env, }, ); let output = ""; let phase = "providers-menu"; const sendKeys = (keys) => { keys.forEach((key, index) => { setTimeout(() => child.write(key), index * 80); }); }; return await new Promise((resolve, reject) => { const timer = setTimeout(() => { child.kill(); reject(new Error(`secrets configure preset timed out: ${scrub(output)}`)); }, 60000); child.onData((data) => { output += data; if (phase === "providers-menu" && output.includes("Configure secret providers")) { phase = "selecting-preset"; sendKeys(["\x1b[B", "\r"]); return; } if (phase === "selecting-preset" && output.includes("Select plugin preset")) { phase = "preset-selected"; sendKeys(["\r"]); output = ""; return; } if (phase === "preset-selected" && output.includes("Configure secret providers")) { phase = "continue-selected"; sendKeys(["\x1b[A", "\r"]); } }); child.onExit(({ exitCode }) => { clearTimeout(timer); if (exitCode !== 0) { reject(new Error(`secrets configure preset failed (${exitCode}): ${scrub(output)}`)); return; } resolve(output); }); }); } async function p13SecretsConfigurePreset() { await withProofEnv("p13", async (envCtx) => { const port = await allocatePort(); writeJson(envCtx.env.OPENCLAW_CONFIG_PATH, baseConfig(port, { secrets: { providers: {} } })); await runPtySecretsConfigurePreset(envCtx); const config = readJson(envCtx.env.OPENCLAW_CONFIG_PATH); const provider = config.secrets?.providers?.[PROVIDER_ALIAS]; if (JSON.stringify(provider) !== JSON.stringify(proofProviderConfig())) { throw new Error( `secrets configure did not persist pluginIntegration provider: ${JSON.stringify(provider)}`, ); } }); return "interactive secrets configure selected plugin preset and wrote only pluginIntegration metadata"; } async function p14ConfigPatchValidation() { await withProofEnv("p14", async (envCtx) => { const port = await allocatePort(); writeJson(envCtx.env.OPENCLAW_CONFIG_PATH, baseConfig(port, { secrets: { providers: {} } })); const validPatch = { secrets: { providers: { [PROVIDER_ALIAS]: proofProviderConfig(), }, }, }; const valid = await runOpenClaw( ["config", "patch", "--stdin", "--dry-run", "--allow-exec", "--json"], envCtx.env, { input: JSON.stringify(validPatch), timeoutMs: 60000 }, ); const validPayload = parseJsonOutput(valid.stdout); if (!validPayload.valid && validPayload.ok !== true && validPayload.changed === undefined) { throw new Error( `valid pluginIntegration config patch was not accepted: ${scrub(valid.stdout)}`, ); } const invalidPatch = { secrets: { providers: { [PROVIDER_ALIAS]: { source: "exec", pluginIntegration: { pluginId: PLUGIN_ID, integrationId: "missing" }, }, }, }, }; const invalid = await runOpenClaw( ["config", "patch", "--stdin", "--dry-run", "--allow-exec", "--json"], envCtx.env, { input: JSON.stringify(invalidPatch), timeoutMs: 60000, allowFailure: true }, ); if (invalid.code === 0) { throw new Error("invalid pluginIntegration config patch unexpectedly succeeded"); } const output = scrub(`${invalid.stdout}\n${invalid.stderr}`); if (!/plugin|integration|secret/iu.test(output)) { throw new Error(`invalid pluginIntegration patch did not explain the failure: ${output}`); } }); return "config patch accepts valid pluginIntegration and rejects invalid integration metadata"; } async function p15ModelsAuthCliScope() { const envCtx = makeEnv("p15"); try { const help = await runOpenClaw(["models", "auth", "paste-api-key", "--help"], envCtx.env, { timeoutMs: 30000, }); const text = help.stdout; if (/keyRef|SecretRef|--ref|--secret/iu.test(text)) { throw new Error( "models auth paste-api-key appears to expose a SecretRef input; add a live creation proof for it", ); } } finally { await cleanupEnv(envCtx.root); } return "models auth has no non-interactive SecretRef creation flag; auth-profile encounter path is covered by P7/P8/P12"; } async function p16DiagnosticsNoLeak() { await withProofEnv("p16", async (envCtx, _plugin, storePath) => { const port = await allocatePort(); writeJson(envCtx.env.OPENCLAW_CONFIG_PATH, baseConfig(port)); mutateStore(storePath, (store) => ({ ...store, mode: "fail" })); const output = await expectGatewayStartupFails(envCtx, port, "diagnostic redaction"); if ( output.includes(TOKEN_V1) || output.includes(TOKEN_V2) || output.includes(PLUGIN_EXEC_TOKEN) ) { throw new Error("diagnostic output leaked a proof secret"); } if (!/secret|provider|resolve|ref/iu.test(output)) { throw new Error(`diagnostic output was not actionable: ${output}`); } }); return "startup diagnostics are actionable and do not include secret values"; } async function p17StaticMetadataAlignment() { const envCtx = makeEnv("p17"); try { const schema = await runOpenClaw(["config", "schema"], envCtx.env, { timeoutMs: 60000 }); const schemaText = schema.stdout; if (!schemaText.includes("pluginIntegration") || !schemaText.includes("integrationId")) { throw new Error("config schema does not expose pluginIntegration metadata"); } const secretsHelp = await runOpenClaw(["secrets", "configure", "--help"], envCtx.env, { timeoutMs: 30000, }); if ( !secretsHelp.stdout.includes("--providers-only") || !secretsHelp.stdout.includes("--allow-exec") ) { throw new Error("secrets configure help is missing expected provider/exec flags"); } await runCommand( "node", ["--import", "tsx", "scripts/generate-config-doc-baseline.ts", "--check"], { timeoutMs: 60000 }, ); } finally { await cleanupEnv(envCtx.root); } return "schema/help/static diff metadata aligned"; } async function main() { console.log(`[info] runner=${resolveOpenClawRunner().label}`); console.log(`[info] results=${RESULTS_PATH}`); let runError; try { await runWithProof( "P1", "startup succeeds with plugin-managed exec SecretRef", p1StartupSucceeds, ); await runWithProof( "P2", "startup fails closed when plugin integration cannot resolve", p2StartupFailsClosed, ); await runWithProof( "P3-P6", "static capture, reload, LKG, and secrets.resolve", p3ThroughP6StaticReloadAndCommandSnapshot, ); await runWithProof( "P7", "persisted auth-profile keyRef resolves through plugin integration", p7AuthProfileSecretRefPersistsAndResolves, ); await runWithProof( "P8", "managed service install/start preserves auth-profile exec passEnv", p8ManagedServiceEnvProof, ); await runWithProof( "P9", "env/file/manual-exec/plugin-exec provider variants", p9ProviderVariants, ); await runWithProof( "P10", "disabled/untrusted plugin integration fails closed", p10UntrustedPluginFailsClosed, ); await runWithProof( "P11", "resolver timeout fails closed and reload keeps LKG", p11TimeoutFailClosedAndLkg, ); await runWithProof("P12", "real OpenAI auth-profile SecretRef live probe", p12OpenAiLiveProof); await runWithProof( "P13", "interactive secrets configure plugin preset", p13SecretsConfigurePreset, ); await runWithProof( "P14", "config patch validation for pluginIntegration", p14ConfigPatchValidation, ); await runWithProof("P15", "models auth SecretRef creation scope", p15ModelsAuthCliScope); await runWithProof("P16", "diagnostics are actionable and redacted", p16DiagnosticsNoLeak); await runWithProof("P17", "schema/help/static metadata alignment", p17StaticMetadataAlignment); } catch (error) { runError = error; } finally { writeJson(RESULTS_PATH, { generatedAt: new Date().toISOString(), runner: resolveOpenClawRunner().label, results, }); } if (runError) { throw runError; } const failed = results.filter((entry) => entry.status !== "pass"); if (failed.length > 0) { process.exitCode = 1; } } export { gatewayCall, runCommand, startGateway, waitForManagedGatewayStatus }; if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) { await main(); }