openai-consumer: the static-key model-access consumer (ADR 0050)
The consumer half of the OTHER model-access shape. Where anthropic-consumer receives a refreshed access token, this receives one operator-supplied API key the mesh sealed to it and the host unsealed at its secret path — no manager, no refresh, no usage. It writes the key where an OpenAI/Codex client reads it: an OPENAI_API_KEY env file and the publicly-known Codex auth.json. Pure node, no SDK import — the simplest a model-access consumer gets. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
// The consumer's scheduled run: take the API key the mesh delivered and write it where an OpenAI or
|
||||
// Codex client reads it (novox/hq ADR 0050, the static-key half). The key arrives sealed-then-unsealed
|
||||
// at the module's secret path — the host opened it with this node's private key; this process reads
|
||||
// plaintext. There is no manager, no refresh, and nothing to strip: a static-key credential is one
|
||||
// value, delivered unchanged.
|
||||
//
|
||||
// What the host delivers, per the manifest:
|
||||
// secrets.model-access -> a file holding the sealed-then-unsealed API key (host-unsealed).
|
||||
// binds.model-access -> a JSON file of the non-secret facts the licence serves (which licence,
|
||||
// model). Not needed to write the key; read only for a log line.
|
||||
//
|
||||
// Runs as `mesh-tools run` (no broker) on a schedule, so it is idempotent: same key in, same files out.
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
import { deliver } from "../credentials.js";
|
||||
|
||||
function required(name: string): string {
|
||||
const v = process.env[name];
|
||||
if (!v) throw new Error(`${name} is not set — the consumer runtime was deployed without it`);
|
||||
return v;
|
||||
}
|
||||
|
||||
function main(): void {
|
||||
const key = readFileSync(required("MESH_MODEL_ACCESS_SECRET_FILE"), "utf8").trim();
|
||||
if (!key) {
|
||||
// Nothing was delivered — which reads exactly like a credential that never arrived, so it is said
|
||||
// rather than written as an empty key file a client would take for a valid login.
|
||||
throw new Error("[openai-consumer] the delivered API key is empty; nothing was written");
|
||||
}
|
||||
|
||||
const envFile = process.env.MESH_OPENAI_ENV_FILE ?? `${home()}/.config/openai/openai.env`;
|
||||
const authFile = process.env.MESH_OPENAI_CREDENTIALS_FILE ?? `${home()}/.codex/auth.json`;
|
||||
deliver(envFile, authFile, key);
|
||||
console.error(`[openai-consumer] wrote OPENAI_API_KEY to ${envFile} and ${authFile}`);
|
||||
}
|
||||
|
||||
function home(): string {
|
||||
return process.env.HOME ?? "/root";
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user