{"record":{"id":"8fcc077b6ce05beb","repo":"ruvnet/ruflo","slug":"ruflo-x-admin-token-is-not-set-admission-is-admin-gated","errorCode":null,"errorMessage":"RUFLO_X_ADMIN_TOKEN is not set (admission is admin-gated)","messagePattern":"RUFLO_X_ADMIN_TOKEN is not set \\(admission is admin-gated\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/x-federation-tools.ts","lineNumber":187,"sourceCode":"    },\n  },\n  {\n    name: 'x_federation_invite_mint',\n    description:\n      'Mint a use-limited, expiring invite code so a new ruflo user can self-join the open federation with THEIR OWN key. Requires RUFLO_X_ADMIN_TOKEN. Use when onboarding someone. Sharing the relay owner key instead is wrong because invites are revocable, hashed at rest, and bind membership to the claimant\\'s key; the code is a bearer secret — hand it over privately.',\n    inputSchema: { type: 'object', properties: { ...gatewayArg, ttlSecs: { type: 'number', description: 'Validity (default 7 days).' }, maxUses: { type: 'number', description: 'Redemptions (default 25).' } } },\n    handler: async (input) => {\n      const t = adminToken(); if (!t) throw new Error('RUFLO_X_ADMIN_TOKEN is not set (invite minting is admin-gated)');\n      return gatewayTool('federation_invite_mint', { ...(input as Record<string, unknown>), adminToken: t });\n    },\n  },\n  {\n    name: 'x_federation_admit',\n    description:\n      'Admit a Nostr pubkey as a relay member directly (NIP-43 kind 9030). Requires RUFLO_X_ADMIN_TOKEN. Use when a known node reports its 64-hex pubkey and you want to skip the invite step. Padding or hand-editing a reported pubkey is wrong because it is a cryptographic identity; a malformed key must be re-reported, never fixed up.',\n    inputSchema: { type: 'object', properties: { ...gatewayArg, pubkey: { type: 'string', description: '64-hex secp256k1 x-only pubkey.' }, role: { type: 'string', enum: ['member', 'admin'] } }, required: ['pubkey'] },\n    handler: async (input) => {\n      const t = adminToken(); if (!t) throw new Error('RUFLO_X_ADMIN_TOKEN is not set (admission is admin-gated)');\n      return gatewayTool('federation_admit', { ...(input as Record<string, unknown>), adminToken: t });\n    },\n  },\n];\n","sourceCodeStart":169,"sourceCodeEnd":192,"githubUrl":"https://github.com/ruvnet/ruflo/blob/9c61c86f06b439af2a95085ae9bb0ca839662e41/v3/@claude-flow/cli/src/mcp-tools/x-federation-tools.ts#L169-L192","documentation":"The x_federation_admit MCP tool admits a Nostr pubkey as a relay member (NIP-43 kind 9030) but is admin-gated: it requires the RUFLO_X_ADMIN_TOKEN environment variable. Before doing anything, the handler calls adminToken() and, if no admin token is configured, throws this error to prevent unauthenticated federation admission. It is an intentional guard, not a bug.","triggerScenarios":"Calling the x_federation_admit tool (e.g. to admit a node's 64-hex pubkey without an invite) while process.env.RUFLO_X_ADMIN_TOKEN is unset or empty, so adminToken() returns undefined.","commonSituations":"Running the MCP server in an environment where the admin token was never provisioned; CI or sandbox runs without the operator's secret env vars; invoking federation admission from a non-admin automation account that lacks the token.","solutions":["Set RUFLO_X_ADMIN_TOKEN in the environment where the MCP server runs","Verify the variable is exported in the shell/service definition before launching claude-flow mcp start","If you only have an invite, use the invite-based membership flow instead of direct admission"],"exampleFix":"// before\nhandler: async (input) => { const t = adminToken(); ... } // RUFLO_X_ADMIN_TOKEN unset\n// after\nexport RUFLO_X_ADMIN_TOKEN=<admin-secret> && claude-flow mcp start","handlingStrategy":"validation","validationCode":"if (!process.env.RUFLO_X_ADMIN_TOKEN) { throw new Error('Cannot admit: RUFLO_X_ADMIN_TOKEN must be set'); }","typeGuard":"const hasAdminToken = (): boolean => typeof process.env.RUFLO_X_ADMIN_TOKEN === 'string' && process.env.RUFLO_X_ADMIN_TOKEN.length > 0;","tryCatchPattern":"try { await x_federation_admit({ pubkey, role }); } catch (e) { if (String(e.message).includes('RUFLO_X_ADMIN_TOKEN')) { /* prompt for admin token / switch environment */ } throw e; }","preventionTips":["Provision RUFLO_X_ADMIN_TOKEN in every environment that runs the MCP server","Check env presence at service startup, not at first tool call","Use a secret manager or dotenv file so the token is never missing"],"tags":["env","authentication","mcp","federation"],"backgroundTag":"missing-env-var","analyzedSha":"9c61c86f06b439af2a95085ae9bb0ca839662e41","analyzedAt":"2026-09-15T22:58:14.805Z","contentChangedAt":"2026-09-15T22:58:14.805Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}