ruvnet/ruflo · error · TypeError

Unsupported federation signature mode: ${String(signatureMod

Error message

Unsupported federation signature mode: ${String(signatureMode)}

What it means

Startup validation in the agent-federation plugin constructor: config.signatureMode (an EnvelopeSignatureMode) must be exactly 'legacy', 'prefer-jcs', or 'require-jcs'; anything else raises a TypeError immediately. When the key is absent the plugin falls back to DEFAULT_ENVELOPE_SIGNATURE_MODE, so this error always indicates an explicitly supplied bad value.

Source

Thrown at v3/@claude-flow/plugin-agent-federation/src/plugin.ts:103

  private transport: LoadedTransport | null = null;
  // A2A Agent Card well-known endpoint (opt-in via config.a2aCard). Null
  // when the HTTP surface is off — everything else works without it.
  private agentCardServer: AgentCardServerHandle | null = null;

  async initialize(context: PluginContext): Promise<void> {
    this.context = context;
    const config = context.config;

    const nodeId = (config['nodeId'] as string) ?? `node-${Date.now().toString(36)}`;
    const endpoint = (config['endpoint'] as string) ?? 'ws://localhost:9100';
    const complianceMode = (config['complianceMode'] as ComplianceMode) ?? 'none';
    const staticPeers = (config['staticPeers'] as string[]) ?? [];
    const hashSalt = (config['hashSalt'] as string) ?? `salt-${nodeId}`;
    const signatureMode =
      (config['signatureMode'] as EnvelopeSignatureMode | undefined)
      ?? DEFAULT_ENVELOPE_SIGNATURE_MODE;
    if (!['legacy', 'prefer-jcs', 'require-jcs'].includes(signatureMode)) {
      throw new TypeError(`Unsupported federation signature mode: ${String(signatureMode)}`);
    }

    // ADR-095 G2: real Ed25519 keypair instead of empty publicKey + stub
    // signatures. Persist to .claude-flow/federation/key-<nodeId>.json so
    // the same node identity survives restarts. Audit log
    // audit_1776483149979 flagged the previous "verifySignature returns
    // true unconditionally" as a critical authn bypass; this closes it.
    const keyDir = join(process.cwd(), '.claude-flow', 'federation');
    const keyPath = join(keyDir, `key-${nodeId}.json`);
    let privateKey: Uint8Array;
    let publicKeyHex: string;
    try {
      if (existsSync(keyPath)) {
        const stored = JSON.parse(readFileSync(keyPath, 'utf-8')) as { privateKey: string; publicKey: string; nodeId: string };
        privateKey = new Uint8Array(Buffer.from(stored.privateKey, 'hex'));
        publicKeyHex = stored.publicKey;
      } else {
        privateKey = ed.utils.randomPrivateKey();

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Set signatureMode to one of the exact strings: 'legacy', 'prefer-jcs', or 'require-jcs'
  2. Omit the key entirely to use the built-in default mode
  3. Add a startup config validator that checks the enum before the plugin is constructed

Example fix

// before
const config = { signatureMode: 'jcs' }; // TypeError: Unsupported federation signature mode

// after
const config = { signatureMode: 'prefer-jcs' };
Defensive patterns

Strategy: type-guard

Validate before calling

const MODES = ['legacy', 'prefer-jcs', 'require-jcs'] as const;
const raw = config['signatureMode'];
if (raw !== undefined && !isEnvelopeSignatureMode(raw)) {
  throw new TypeError(`Unsupported federation signature mode: ${String(raw)} (allowed: ${MODES.join(', ')})`);
}

Type guard

const ENVELOPE_SIGNATURE_MODES = ['legacy', 'prefer-jcs', 'require-jcs'] as const;
type EnvelopeSignatureMode = (typeof ENVELOPE_SIGNATURE_MODES)[number];
function isEnvelopeSignatureMode(v: unknown): v is EnvelopeSignatureMode {
  return typeof v === 'string' && (ENVELOPE_SIGNATURE_MODES as readonly string[]).includes(v);
}

Try / catch

try {
  host.register(plugin, config);
} catch (e) {
  if (e instanceof TypeError && e.message.startsWith('Unsupported federation signature mode')) {
    // fail deployment; surface the allowed list to the operator
  } else throw e;
}

Prevention

When it happens

Trigger: Passing signatureMode with a typo, wrong case, or invented value (e.g. 'jcs', 'Require-JCS', 'prefer_jcs'), or a config pipeline that coerces the value into a non-string (number, boolean, or nested object).

Common situations: Copying config snippets between plugin versions whose mode names changed; env-var mapping that injects the literal string 'undefined'; YAML/JSON config parsed into a nested object instead of a scalar string.

Related errors


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