{"record":{"id":"789894a65ed4b460","repo":"ruvnet/ruflo","slug":"ruflo-x-admin-token-is-not-set-invite-minting-is-admin-gated","errorCode":null,"errorMessage":"RUFLO_X_ADMIN_TOKEN is not set (invite minting is admin-gated)","messagePattern":"RUFLO_X_ADMIN_TOKEN is not set \\(invite minting is admin-gated\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/x-federation-tools.ts","lineNumber":177,"sourceCode":"    handler: async (input) => gatewayResource('ruv://federation/registry', (input as Record<string, unknown>).gatewayUrl),\n  },\n  {\n    name: 'x_federation_publish',\n    description:\n      'Publish a signed coordination message to the open swarm AS THE GATEWAY identity (Status/Task/Result/…). Requires RUFLO_X_ADMIN_TOKEN. Use when a trusted operator needs a hub-level broadcast. Using this to post on behalf of an individual node is wrong because it attributes the message to the gateway, not the node — nodes should join with their own key via invite→claim and publish themselves.',\n    inputSchema: { type: 'object', properties: { ...gatewayArg, msgType: { type: 'string' }, payload: { type: 'object' } }, required: ['msgType', 'payload'] },\n    handler: async (input) => {\n      const t = adminToken(); if (!t) throw new Error('RUFLO_X_ADMIN_TOKEN is not set (gateway-identity writes are admin-gated)');\n      return gatewayTool('federation_publish', { ...(input as Record<string, unknown>), adminToken: t });\n    },\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":159,"sourceCodeEnd":192,"githubUrl":"https://github.com/ruvnet/ruflo/blob/9c61c86f06b439af2a95085ae9bb0ca839662e41/v3/@claude-flow/cli/src/mcp-tools/x-federation-tools.ts#L159-L192","documentation":"Thrown by the `x_federation_invite_mint` tool handler when `adminToken()` finds no `RUFLO_X_ADMIN_TOKEN` in the environment. Invite minting is admin-gated because invites are revocable, hashed at rest, and bind membership to the claimant's key, so only the relay operator may create them. The tool aborts before contacting the gateway.","triggerScenarios":"Calling `x_federation_invite_mint` (with optional ttlSecs/maxUses) on a machine or process where RUFLO_X_ADMIN_TOKEN is unset — unprivileged node installs, MCP servers started before the token was exported, CI jobs missing the secret, or shells where the operator env file was never sourced.","commonSituations":"Onboarding a new federation member but running the mint command from the wrong host (a node instead of the gateway operator's machine); token exported in one terminal but the MCP server launched from another; Docker/Kubernetes containers launched without the env var; forgetting to reload env after rotating the admin token.","solutions":["Run the mint on the gateway/relay operator machine with `export RUFLO_X_ADMIN_TOKEN=<operator token>` before starting the CLI/MCP process.","Verify the variable is visible to the exact process (`printenv RUFLO_X_ADMIN_TOKEN` in the same shell/container).","In containers/CI, pass the token via the deployment's secret mechanism rather than relying on host shell state.","If you are not the operator, ask the operator to mint the invite and hand over the `v2.<token>` code privately (it is a bearer secret).","Pre-check `process.env.RUFLO_X_ADMIN_TOKEN` in scripts that orchestrate onboarding to fail with a clear message."],"exampleFix":"// before\nawait xFederationInviteMint({ ttlSecs: 604800, maxUses: 25 });\n// after\nif (!process.env.RUFLO_X_ADMIN_TOKEN) throw new Error('invite minting requires RUFLO_X_ADMIN_TOKEN on the gateway operator machine');\nawait xFederationInviteMint({ ttlSecs: 604800, maxUses: 25 });","handlingStrategy":"validation","validationCode":"if (!process.env.RUFLO_X_ADMIN_TOKEN) {\n  throw new Error('RUFLO_X_ADMIN_TOKEN is not set; run invite minting on the gateway operator machine');\n}","typeGuard":"function canMintInvites(env: NodeJS.ProcessEnv): env is NodeJS.ProcessEnv & { RUFLO_X_ADMIN_TOKEN: string } {\n  return typeof env.RUFLO_X_ADMIN_TOKEN === 'string' && env.RUFLO_X_ADMIN_TOKEN.trim().length > 0;\n}","tryCatchPattern":"try {\n  await xFederationInviteMint({ ttlSecs: 604800, maxUses: 25 });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('RUFLO_X_ADMIN_TOKEN is not set')) {\n    console.error('invite minting is operator-only: set RUFLO_X_ADMIN_TOKEN or request an invite from the operator');\n  } else throw e;\n}","preventionTips":["Run onboarding/mint commands only on the gateway operator machine with the token exported.","Confirm with `printenv RUFLO_X_ADMIN_TOKEN` in the exact shell/container that runs the CLI.","Pass the token into containers/CI via the secrets mechanism, not interactive shell state.","Remember invites are bearer secrets: mint, then deliver the v2.<token> code privately.","Re-export the token after rotations; stale shells will fail with this error."],"tags":["env-var","missing-token","admin-gated","invite-minting"],"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"}