Something went wrong. Try again.
A local-first event pipeline for independent agents, built on Jazz.
Something went wrong. Try again.
4.1 kB · 84 lines
TypeScript
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485import { randomBytes } from "node:crypto";import fs from "node:fs/promises";import os from "node:os";import path from "node:path";import { JoseKey } from "@atproto/oauth-client-node";
const args = parseArgs(process.argv.slice(2));const origin = new URL(required(args.origin, "--origin"));if (origin.protocol !== "https:" || origin.pathname !== "/" || origin.search || origin.hash || origin.username || origin.password) { throw new Error("--origin must be one bare HTTPS origin, for example https://thought.stream");}const did = required(args.did, "--did");if (!/^did:(plc|web):/.test(did)) throw new Error("--did must be an ATProto did:plc or did:web identity");const handle = required(args.handle, "--handle");if (!/^[a-z0-9][a-z0-9.-]+$/i.test(handle)) throw new Error("--handle is invalid");const credentialsDirectory = process.env.THOUGHTSTREAM_CREDENTIALS_DIR ?? path.join(os.homedir(), ".config", "thoughtstream", "credentials");if (process.env.THOUGHTSTREAM_OAUTH_STORE_DIR) { throw new Error("THOUGHTSTREAM_OAUTH_STORE_DIR is unsupported; the systemd sandbox permits only ~/.local/share/thoughtstream-inspector-auth");}const storeDirectory = path.join(os.homedir(), ".local", "share", "thoughtstream-inspector-auth");const destination = path.join(credentialsDirectory, "inspector-oauth.env");
await refuseSymlink(credentialsDirectory);await fs.mkdir(credentialsDirectory, { recursive: true, mode: 0o700 });await fs.chmod(credentialsDirectory, 0o700);await refuseSymlink(storeDirectory);await fs.mkdir(storeDirectory, { recursive: true, mode: 0o700 });await fs.chmod(storeDirectory, 0o700);if (!args.force) { const exists = await fs.stat(destination).then(() => true, (error: NodeJS.ErrnoException) => error.code === "ENOENT" ? false : Promise.reject(error)); if (exists) throw new Error(`OAuth configuration already exists at ${destination}; use --force only for deliberate key rotation`);}
const key = await JoseKey.generate(["ES256"], `thoughtstream-${new Date().toISOString().slice(0, 10)}`);if (!key.privateJwk) throw new Error("Generated OAuth client key has no private material");const lines = [ "OAUTH_ENABLED=1", `OAUTH_PUBLIC_ORIGIN=${origin.origin}`, `OAUTH_ALLOWED_DID=${did}`, `OAUTH_EXPECTED_HANDLE=${handle}`, `OAUTH_STORE_DIR=${storeDirectory}`, `OAUTH_STORE_KEY_B64=${randomBytes(32).toString("base64")}`, `OAUTH_PRIVATE_JWK_B64=${Buffer.from(JSON.stringify(key.privateJwk), "utf8").toString("base64")}`, "OAUTH_SESSION_TTL_MS=43200000", "",];const temporary = `${destination}.tmp-${process.pid}-${randomBytes(6).toString("hex")}`;await fs.writeFile(temporary, lines.join("\n"), { mode: 0o600, flag: "wx" });await fs.rename(temporary, destination);await fs.chmod(destination, 0o600);process.stdout.write(`Wrote owner-only OAuth configuration to ${destination}\n`);process.stdout.write("Basic fallback configuration is independent and defaults to disabled when absent.\n");
function parseArgs(values: string[]): Record<string, string | boolean> { const result: Record<string, string | boolean> = {}; for (let index = 0; index < values.length; index += 1) { const current = values[index]!; if (current === "--") continue; if (current === "--force") { result.force = true; continue; } if (!["--origin", "--did", "--handle"].includes(current)) throw new Error(`Unknown argument: ${current}`); const value = values[index + 1]; if (!value) throw new Error(`Missing value for ${current}`); result[current.slice(2)] = value; index += 1; } return result;}
function required(value: string | boolean | undefined, label: string): string { if (typeof value !== "string" || !value.trim()) throw new Error(`${label} is required`); return value.trim();}
async function refuseSymlink(target: string): Promise<void> { const stat = await fs.lstat(target).catch((error: NodeJS.ErrnoException) => { if (error.code === "ENOENT") return undefined; throw error; }); if (stat?.isSymbolicLink()) throw new Error(`Refusing symlinked directory: ${target}`);}