Skip to the content

Capsid menu

src/secret-presence.ts

33 lines and 6 functions in DrDustinEdwards/capsid, at commit daa875b on 2026-10-24. This file on GitHub.

Functions

The functions of secret-presence.ts
FunctionLinesComplexityCoveredCRAPMutation
secretPresence16 to 231100%OkCRAP 1No data: mutation testing has not run on this repo yet
<anonymous:20>20 to 201100%OkCRAP 1No data: mutation testing has not run on this repo yet
<anonymous:21>21 to 211100%OkCRAP 1No data: mutation testing has not run on this repo yet
<anonymous:21>21 to 211100%OkCRAP 1No data: mutation testing has not run on this repo yet
storeSecrets25 to 27133%OkCRAP 1.3No data: mutation testing has not run on this repo yet
readStoredSecrets30 to 39580%WatchCRAP 5.2No data: mutation testing has not run on this repo yet

Imports

It imports src/env.ts, src/ops-types/views.ts. Imported by src/manual-steps.ts, src/site-convergence.ts, src/watcher-gather.ts.

Source

import type { Env } from "./env";
import type { ConvergenceSite, SecretPresence } from "./ops-types/views";

// THE SECRET PRESENCE OF A SITE'S WORKER (job_09e5f6cbf782; the reader is shared with
// job_5ac1139641e0's credentials inventory). Names, set or not, never a value. The names
// are read from Cloudflare on the watcher's pass (readSecretNames in src/ops-cloudflare.ts,
// with CF_OPS_TOKEN; permission Account / Workers Scripts / Read) and kept here in APP_KV,
// so the Portal shows them without a Portal request ever calling Cloudflare.

const KEY_PREFIX = "site-secrets:";
const KEEP_SECONDS = 7 * 86_400;

export type StoredSecrets = NonNullable<ConvergenceSite["secrets"]> & { read_at: string };

/** Each expected name, set or not, then any name the Worker has that nobody expected. */
export function secretPresence(expected: readonly string[], listed: readonly string[]): SecretPresence[] {
  const have = new Set(listed);
  const want = new Set(expected);
  return [
    ...expected.map((name) => ({ name, set: have.has(name), expected: true })),
    ...[...have].filter((name) => !want.has(name)).sort().map((name) => ({ name, set: true, expected: false })),
  ];
}

export async function storeSecrets(env: Pick<Env, "APP_KV">, namespace: string, value: StoredSecrets): Promise<void> {
  await env.APP_KV.put(KEY_PREFIX + namespace, JSON.stringify(value), { expirationTtl: KEEP_SECONDS });
}

/** What the last pass stored, or null when no pass has read it. */
export async function readStoredSecrets(env: Pick<Env, "APP_KV">, namespace: string): Promise<StoredSecrets | null> {
  const raw = await env.APP_KV.get(KEY_PREFIX + namespace);
  if (!raw) return null;
  try {
    const parsed = JSON.parse(raw) as StoredSecrets;
    return Array.isArray(parsed.rows) && typeof parsed.read_at === "string" ? parsed : null;
  } catch {
    return null;
  }
}