#!/usr/bin/env node // Mint an HMAC-signed JWT for arcadia-search and (optionally) write it // into .env.local as VITE_ARCADIA_SEARCH_TOKEN. // // arcadia-search expects: // - HS512 by default when only JWT_HMAC_SECRET is set on the server // (no PEM). HS256/HS384 work too if JWT_ALGORITHM is overridden. // - a `tenant_id` claim (configurable server-side via JWT_TENANT_CLAIM). // // Usage: // node scripts/mint-search-token.mjs # uses defaults, writes .env.local // node scripts/mint-search-token.mjs --print # print only, don't write // node scripts/mint-search-token.mjs --tenant=foo --days=30 // JWT_HMAC_SECRET=xyz node scripts/mint-search-token.mjs import { createHmac } from "node:crypto" import { readFileSync, writeFileSync, existsSync } from "node:fs" import { fileURLToPath } from "node:url" import { dirname, resolve } from "node:path" const HERE = dirname(fileURLToPath(import.meta.url)) const PROJECT_ROOT = resolve(HERE, "..") const ENV_LOCAL = resolve(PROJECT_ROOT, ".env.local") function arg(name, fallback) { const prefix = `--${name}=` const hit = process.argv.find((a) => a.startsWith(prefix)) return hit ? hit.slice(prefix.length) : fallback } const flag = (name) => process.argv.includes(`--${name}`) const SECRET = process.env.JWT_HMAC_SECRET ?? "test-secret-change-me" const TENANT = arg("tenant", process.env.VITE_ARCADIA_TENANT ?? "platform-admin") const SUBJECT = arg("sub", "arcadia-admin") const ALGORITHM = (arg("alg", "HS512")).toUpperCase() const DAYS = Number(arg("days", "365")) const PRINT_ONLY = flag("print") const HMAC_ALG = { HS256: "sha256", HS384: "sha384", HS512: "sha512" }[ALGORITHM] if (!HMAC_ALG) { console.error(`Unsupported algorithm "${ALGORITHM}". Use HS256, HS384, or HS512.`) process.exit(1) } const b64 = (v) => Buffer.from(typeof v === "string" ? v : JSON.stringify(v)).toString("base64url") const now = Math.floor(Date.now() / 1000) const header = b64({ alg: ALGORITHM, typ: "JWT" }) const payload = b64({ sub: SUBJECT, tenant_id: TENANT, iat: now, exp: now + DAYS * 86400, }) const signature = createHmac(HMAC_ALG, SECRET) .update(`${header}.${payload}`) .digest("base64url") const token = `${header}.${payload}.${signature}` if (PRINT_ONLY) { console.log(token) process.exit(0) } // Upsert VITE_ARCADIA_SEARCH_TOKEN in .env.local without disturbing other keys. const KEY = "VITE_ARCADIA_SEARCH_TOKEN" let existing = "" if (existsSync(ENV_LOCAL)) existing = readFileSync(ENV_LOCAL, "utf8") const line = `${KEY}=${token}` const next = new RegExp(`^${KEY}=.*$`, "m").test(existing) ? existing.replace(new RegExp(`^${KEY}=.*$`, "m"), line) : (existing.endsWith("\n") || existing === "" ? existing : existing + "\n") + line + "\n" writeFileSync(ENV_LOCAL, next) console.log(`Wrote ${KEY} to ${ENV_LOCAL}`) console.log(` alg: ${ALGORITHM}`) console.log(` tenant: ${TENANT}`) console.log(` sub: ${SUBJECT}`) console.log(` expires: ${new Date((now + DAYS * 86400) * 1000).toISOString()}`) console.log(` secret: ${SECRET === "test-secret-change-me" ? "(default — override with JWT_HMAC_SECRET=)" : "(from JWT_HMAC_SECRET)"}`) console.log(``) console.log(`Restart 'npm run dev' so Vite picks up the new env var.`)