ruvnet/ruflo · error
RUFLO_X_ADMIN_TOKEN is not set (admission is admin-gated)
Error message
RUFLO_X_ADMIN_TOKEN is not set (admission is admin-gated)
What it means
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.
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
Example fix
// before
handler: async (input) => { const t = adminToken(); ... } // RUFLO_X_ADMIN_TOKEN unset
// after
export RUFLO_X_ADMIN_TOKEN=<admin-secret> && claude-flow mcp start Defensive patterns
Strategy: validation
Validate before calling
if (!process.env.RUFLO_X_ADMIN_TOKEN) { throw new Error('Cannot admit: RUFLO_X_ADMIN_TOKEN must be set'); } Type guard
const hasAdminToken = (): boolean => typeof process.env.RUFLO_X_ADMIN_TOKEN === 'string' && process.env.RUFLO_X_ADMIN_TOKEN.length > 0;
Try / catch
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; } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- mcp-caller-auth-enabled-but-no-token
- Federation not initialized
- gateway tool error
- mcp-caller-auth-verification-failed
- policy administration requires an authenticated user context
AI-assisted analysis of ruvnet/ruflo@9c61c86f06 (2026-09-15).
Data as JSON: /api/errors/8fcc077b6ce05beb.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/mcp-tools/x-federation-tools.ts:187
},
},
{
name: 'x_federation_invite_mint',
description:
'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.',
inputSchema: { type: 'object', properties: { ...gatewayArg, ttlSecs: { type: 'number', description: 'Validity (default 7 days).' }, maxUses: { type: 'number', description: 'Redemptions (default 25).' } } },
handler: async (input) => {
const t = adminToken(); if (!t) throw new Error('RUFLO_X_ADMIN_TOKEN is not set (invite minting is admin-gated)');
return gatewayTool('federation_invite_mint', { ...(input as Record<string, unknown>), adminToken: t });
},
},
{
name: 'x_federation_admit',
description:
'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.',
inputSchema: { type: 'object', properties: { ...gatewayArg, pubkey: { type: 'string', description: '64-hex secp256k1 x-only pubkey.' }, role: { type: 'string', enum: ['member', 'admin'] } }, required: ['pubkey'] },
handler: async (input) => {
const t = adminToken(); if (!t) throw new Error('RUFLO_X_ADMIN_TOKEN is not set (admission is admin-gated)');
return gatewayTool('federation_admit', { ...(input as Record<string, unknown>), adminToken: t });
},
},
];
View on GitHub (pinned to 9c61c86f06)