ruvnet/ruflo · error

TruthAnchorStore requires a signingKey in config. Anchors ca

Error message

TruthAnchorStore requires a signingKey in config. Anchors cannot be created without a signing key.

What it means

The TruthAnchorStore constructor throws when config.signingKey is absent (undefined or empty). Every truth anchor is HMAC-signed and immutable once appended, so the store cannot create or verify anchors without a key — there is deliberately no unsigned mode. This is a fail-fast configuration check at construction time.

Source

Thrown at v3/@claude-flow/guidance/src/truth-anchors.ts:205

// ============================================================================

/**
 * Append-only store for truth anchors.
 *
 * Anchors are immutable once created. The store provides signing,
 * verification, querying, supersession, and capacity management
 * with LRU eviction of expired anchors only.
 */
export class TruthAnchorStore {
  private readonly config: TruthAnchorConfig;
  private readonly anchors: TruthAnchor[] = [];
  private readonly indexById: Map<string, number> = new Map();

  constructor(config: Partial<TruthAnchorConfig> = {}) {
    this.config = { ...DEFAULT_CONFIG, ...config };

    if (!this.config.signingKey) {
      throw new Error(
        'TruthAnchorStore requires a signingKey in config. ' +
        'Anchors cannot be created without a signing key.',
      );
    }
  }

  /**
   * Create and sign a new truth anchor.
   *
   * The anchor is appended to the store and can never be mutated.
   * If the store exceeds capacity, expired anchors are evicted
   * starting from the oldest.
   */
  anchor(params: AnchorParams): TruthAnchor {
    const now = Date.now();

    const partial: Omit<TruthAnchor, 'signature'> = {
      id: randomUUID(),

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Pass a non-empty signingKey: new TruthAnchorStore({ signingKey: process.env.TRUTH_ANCHOR_KEY })
  2. If no external key exists, generate a stable secret once (e.g. random 32-byte hex stored in your secret manager) and reuse it — the same key must verify previously created anchors
  3. Fail fast at config-load time by asserting required secrets before constructing the store

Example fix

// before
const store = new TruthAnchorStore(); // throws: signingKey required

// after
const store = new TruthAnchorStore({
  signingKey: process.env.TRUTH_ANCHOR_SIGNING_KEY!,
});
Defensive patterns

Strategy: validation

Validate before calling

// Resolve the key before constructing the store
const signingKey = process.env.TRUTH_ANCHOR_SIGNING_KEY;
if (!signingKey) {
  throw new Error('TRUTH_ANCHOR_SIGNING_KEY is required to start this service');
}
const store = new TruthAnchorStore({ signingKey });

Type guard

type SignedStoreConfig = Partial<TruthAnchorConfig> & { signingKey: string };
function hasSigningKey(c: Partial<TruthAnchorConfig>): c is SignedStoreConfig {
  return typeof c.signingKey === 'string' && c.signingKey.length > 0;
}

Prevention

When it happens

Trigger: Calling new TruthAnchorStore() with no arguments, or with a config object whose signingKey is undefined/empty string — typically because the key was read from an environment variable or secret manager entry that was never set in the current environment.

Common situations: Key configured in the production profile but missing in CI or local dev; secrets loaded asynchronously after the store is constructed; a Partial<TruthAnchorConfig> built from sparse JSON where the signingKey field is absent; refactoring that renamed the config field.

Related errors


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