ruvnet/ruflo · error

ProofChain requires an explicit signingKey — hardcoded defau

Error message

ProofChain requires an explicit signingKey — hardcoded defaults are not secure

What it means

ProofChain's constructor requires a non-empty signingKey because every envelope in the tamper-evident hash chain is HMAC-signed with it — a hardcoded default key would let anyone forge envelopes and defeat the entire design, so the class fails fast instead. The factory createProofChain({ signingKey }) has the same requirement. Empty string, undefined, or null all trigger this.

Source

Thrown at v3/@claude-flow/guidance/src/proof.ts:136

// ============================================================================
// ProofChain
// ============================================================================

/**
 * A tamper-evident, hash-chained sequence of ProofEnvelopes.
 *
 * Each envelope links to the previous one via `previousHash`, forming
 * a blockchain-like structure. Every envelope is HMAC-signed so any
 * modification to the chain can be detected.
 */
export class ProofChain {
  private envelopes: ProofEnvelope[] = [];
  private readonly signingKey: string;

  constructor(signingKey: string) {
    if (!signingKey) {
      throw new Error('ProofChain requires an explicit signingKey — hardcoded defaults are not secure');
    }
    this.signingKey = signingKey;
  }

  /**
   * Append a new ProofEnvelope to the chain.
   *
   * @param runEvent - The RunEvent to wrap
   * @param toolCalls - Tool call records from the run
   * @param memoryOps - Memory operations from the run
   * @param metadata - Optional metadata overrides
   * @returns The newly created and signed ProofEnvelope
   */
  append(
    runEvent: RunEvent,
    toolCalls: ToolCallRecord[] = [],
    memoryOps: MemoryOperation[] = [],
    metadata?: Partial<ProofEnvelopeMetadata>,

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Generate a strong secret, e.g. openssl rand -hex 32, and pass it explicitly to the constructor
  2. Load the key from a secret manager or environment variable and validate it at startup before constructing the chain
  3. Fail fast with a clear config error if the key is missing rather than defaulting or falling back
  4. Never commit the key; rotate by re-verifying and re-exporting chains under the new key if rotation is required

Example fix

// before
const chain = new ProofChain(process.env.PROOF_KEY); // undefined if unset -> throws
// after
const signingKey = process.env.PROOF_SIGNING_KEY;
if (!signingKey || signingKey.length < 32) {
  throw new Error('PROOF_SIGNING_KEY must be set to a >=32-char secret');
}
const chain = new ProofChain(signingKey);
Defensive patterns

Strategy: validation

Validate before calling

const signingKey = process.env.PROOF_SIGNING_KEY;
if (!signingKey || signingKey.trim().length < 32) {
  throw new Error('PROOF_SIGNING_KEY is missing or too weak; generate with: openssl rand -hex 32');
}
const chain = new ProofChain(signingKey);

Type guard

function isValidSigningKey(key: unknown): key is string {
  return typeof key === 'string' && key.trim().length > 0;
}

Prevention

When it happens

Trigger: new ProofChain('') or new ProofChain(undefined as any); reading the key from an unset environment variable; tests constructing the chain without injecting a secret; config loading that silently yields '' for missing values.

Common situations: Missing PROOF_SIGNING_KEY-style env var in CI or a new environment; .env files not loaded before construction; deployment manifests that omit the secret reference.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/2c8cbeb3cb06868a. Report an issue: GitHub.