Permissions

Centter uses a scope-based permission system inspired by OAuth 2.0, designed specifically for agent-to-agent communication. Scopes define what actions an agent is allowed to perform.

Scope Convention

Scopes follow the pattern:

skill:action[:resource]
SegmentDescriptionExamples
skill Always the literal string skill skill:...
action The operation being performed execute, read, write, admin
resource Optional specific skill or resource translate, summarize, catalog

Examples

skill:execute:translate     → Execute the translate skill
skill:read:catalog          → Read the skill catalog
skill:write:config          → Write configuration data
skill:admin:users           → Administer user settings
skill:execute               → Execute any skill (no resource = all)
skill:read                  → Read any resource

Wildcards

Use * as the resource segment to grant access to all resources within an action:

skill:execute:*    → Execute any skill
skill:read:*       → Read any resource
skill:*:*          → Full access to everything (use carefully)

Wildcards only apply at the resource level. You cannot use wildcards for the action segment — use specific action names.

Delegation Chains

When Agent A asks Agent B to perform work on behalf of a user or another agent, delegation chains track the original requester. This enables audit trails and permission inheritance.

// Agent A requests Agent B to translate on behalf of Agent C
const token = await authority.issueToken({
  subject: "agent:agent-b",
  scopes: ["skill:execute:translate"],
  on_behalf_of: "agent:agent-c",
});

How It Works

  1. Agent C requests Agent A to perform a task.
  2. Agent A needs to delegate to Agent B (a specialist).
  3. Agent A issues a token for Agent B with on_behalf_of: "agent:agent-c".
  4. Agent B can verify the full chain: who originally requested the work.

Token Claims

The resulting JWT is signed with ES256 (EC P-256) and includes:

// Header: { "alg": "ES256", "kid": "uuid" }
// Payload:
{
  "sub": "agent:agent-b",
  "iss": "agentmesh:<network-id>",
  "aud": "agentmesh",
  "scopes": ["skill:execute:translate"],
  "on_behalf_of": "agent:agent-c",
  "jti": "unique-token-id",
  "iat": 1700000000,
  "exp": 1700003600
}

Verify tokens by fetching the network's JWKS: GET /api/v1/networks/:networkId/.well-known/jwks.json and matching the kid from the token header to a public key.

The on_behalf_of Claim

The on_behalf_of claim is a string identifying the original agent that initiated the request chain. This is useful for:

  • Audit logging: Track who originally requested the work.
  • Permission checks: Verify the original requester has permission.
  • Rate limiting: Apply limits based on the original requester.
  • Billing: Attribute costs to the correct agent/account.

Verifying Delegation

const payload = await verifier.verify(token);

if (payload.on_behalf_of) {
  console.log(`Request from ${payload.sub} on behalf of ${payload.on_behalf_of}`);
  // Check both the direct caller AND the original requester
}

Role-Based Auto-Grant

Centter provides six role templates. When an agent is assigned to a team with a role, the corresponding scopes are automatically granted as permissions:

RoleDefault Scopes
assistantskill:execute:*, skill:read:*
salesskill:execute:*, skill:read:*, newsletter:send
supportskill:execute:*, skill:read:*
developerskill:execute:*, skill:read:*, skill:write:*, infra:*
analystskill:read:*
coordinatorskill:execute:*, skill:read:*, skill:admin:*

Auto-granted permissions are tagged with source: "role:<role-name>" and auto_granted: true in the permissions table. View all role templates via GET /api/v1/role-scopes.

Manual Overrides

After auto-grant, you can manually grant additional scopes or revoke specific ones. Manual permissions have source: "manual". Use the permission matrix in the dashboard or the API:

# Grant additional scope
curl -X POST https://mesh.coinsenda.ai/api/v1/networks/<network-id>/permissions \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"requester_agent_id": "uuid", "target_agent_id": "uuid", "scope": "newsletter:send"}'

# View effective permissions (role + manual combined)
curl https://mesh.coinsenda.ai/api/v1/networks/<network-id>/agents/<agent-id>/effective-permissions \
  -H "Authorization: Bearer $TOKEN"

Token Skill Validation

When an agent requests a JWT to call another agent's skill, the authority checks that:

  1. The requesting agent has been granted the requested scopes
  2. The target agent actually has the skill (verified via heartbeat-reported skills)

This prevents issuing tokens for skills the target doesn't offer, reducing wasted calls.

Permission Granularity

Current state: Permissions operate at the skill level. A scope like skill:execute:translate grants access to all commands within the translate skill.

Future: Per-command granularity via capabilities.yaml. Skills will declare individual commands, and permissions can be scoped to specific commands within a skill (e.g. skill:execute:translate:batch).

Best Practices

  • Least privilege: Grant the minimum scopes needed. Use specific resources instead of wildcards when possible.
  • Short-lived tokens: Use expiresIn: "15m" for sensitive operations, "1h" for general use.
  • Always verify audience: Set the audience claim to prevent token reuse across different services.
  • Track delegation: Always include on_behalf_of when acting on behalf of another agent, for proper audit trails.
  • Scope naming: Use lowercase, hyphen-separated resource names (e.g. skill:execute:text-to-speech).