diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md index ac94137..9e19568 100644 --- a/docs/DEPLOY.md +++ b/docs/DEPLOY.md @@ -243,3 +243,33 @@ Neither command needs a secret key. Balance checks take public keys: - **Never** commit `.env` with secrets (already in `.gitignore`). - **Never** put `S…` secret keys in `VITE_*` — they end up in public JS. - Rotate any key that was pasted in chat or logs. + +## Support log redaction & troubleshooting + +Before attaching terminal transcripts, container logs, or browser exports to an +issue, pull request, or support channel, strip every credential, bearer token, +and pre-reveal secret listed below. Structured logger output from +`@sub-rosa/logging` automatically scrubs common secret keys (`secret`, `token`, +`password`, `privateKey`, `credential`, `cookie`, `authorization`, `apiKey`, +`mnemonic`, `seedPhrase`), `S…` Stellar secret seeds, PEM blocks, `Bearer` / +`Basic` headers, and URL credentials to `[REDACTED]`, but raw shell command +lines, unhandled crash dumps, and DevTools HAR files can still include them: + +- **Keeper logs (`services/keeper`):** Remove `KEEPER_SECRET`, `OPERATOR_SECRET`, + and `BIDDER_SECRET` (`S…` Stellar secret seeds), provider-authenticated + `RPC_URL` or `STELLAR_RPC_URL` strings (basic-auth userinfo + `https://user:pass@…` or query parameters such as `?api_key=…` / `?token=…`), + and any unrevealed bid `value` or `nonce` prior to Drand round `R`. +- **Appraisal API logs (`services/appraisal-api`):** Remove + `FACILITATOR_SECRET`, `Authorization` (`Bearer` / `Basic`) headers, x402 + payment headers (`X-Payment` / signed XDR payloads), and provider-keyed + `RPC_URL` endpoints. +- **SDK & relayer logs (`packages/sdk`):** Remove `STELLAR_SECRET_KEY`, + `OZ_CHANNELS_API_KEY`, session Ed25519 private keys, round auditor secret + keys, and unrevealed bid plaintext (`value`, `nonce`) before `open_reveal` + succeeds on-chain. +- **Browser & DevTools logs (`apps/web`):** Remove passkey/session key material + in `localStorage` or `sessionStorage`, `Cookie` and `Authorization` headers in + Network HAR exports, wallet seed phrases or `S…` secret keys, and local + unrevealed bid values/nonces before the round enters `Revealing`. + diff --git a/scripts/check-deploy-docs.test.mjs b/scripts/check-deploy-docs.test.mjs index 86628e0..728191f 100644 --- a/scripts/check-deploy-docs.test.mjs +++ b/scripts/check-deploy-docs.test.mjs @@ -1,13 +1,14 @@ // Copyright (c) 2026 Sub Rosa contributors import { execFileSync } from "node:child_process"; -import { mkdirSync, mkdtempSync, copyFileSync, writeFileSync, rmSync } from "node:fs"; +import { mkdirSync, mkdtempSync, copyFileSync, readFileSync, writeFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, it } from "node:test"; +import { fileURLToPath } from "node:url"; import assert from "node:assert/strict"; -const SCRIPT_PATH = new URL("check-deploy-docs.mjs", import.meta.url).pathname; -const CWD = new URL("..", import.meta.url).pathname; +const SCRIPT_PATH = fileURLToPath(new URL("check-deploy-docs.mjs", import.meta.url)); +const CWD = fileURLToPath(new URL("..", import.meta.url)); function runCheck(env = {}) { return execFileSync("node", [SCRIPT_PATH], { @@ -140,4 +141,46 @@ describe("check-deploy-docs", () => { rmSync(tmpDir, { recursive: true, force: true }); } }); + + it("documents safe support-log redaction across keeper, appraisal, SDK, and browser logs", () => { + const deployDoc = readFileSync(join(CWD, "docs", "DEPLOY.md"), "utf8"); + const sectionIdx = deployDoc.indexOf("## Support log redaction & troubleshooting"); + assert.ok( + sectionIdx !== -1, + "docs/DEPLOY.md must include '## Support log redaction & troubleshooting'", + ); + + const section = deployDoc.slice(sectionIdx); + for (const surface of [ + "Keeper logs (`services/keeper`)", + "Appraisal API logs (`services/appraisal-api`)", + "SDK & relayer logs (`packages/sdk`)", + "Browser & DevTools logs (`apps/web`)", + ]) { + assert.ok( + section.includes(surface), + `support-log redaction section must cover ${surface}`, + ); + } + + for (const field of [ + "KEEPER_SECRET", + "OPERATOR_SECRET", + "BIDDER_SECRET", + "FACILITATOR_SECRET", + "STELLAR_SECRET_KEY", + "OZ_CHANNELS_API_KEY", + "Authorization", + "Cookie", + "localStorage", + "sessionStorage", + "nonce", + "[REDACTED]", + ]) { + assert.ok( + section.includes(field), + `support-log redaction section must list ${field}`, + ); + } + }); });