Identity and permissions for agents

Understand what your Platform credential can do, why a call was denied, and how to give automation its own durable Bot identity. This page extends https://app.tokay.io/llms.txt. Act only on an explicit user request or named readiness action.

Last updated

AI agent? Start with llms.txt.

The authority model

  • You act as a durable Person or Bot. An AI agent is the tool, not another identity that carries authority.
  • Current Workspace, Project, and repository memberships define which resources the principal can reach.
  • Every API, MCP, Git HTTPS, and SSH credential carries explicit action grants that can only narrow that membership authority. Browser sessions remain the recovery and account management surface.
  • API and MCP use distinct token kinds and exact audiences. API credentials work at GraphQL and API REST. MCP credentials work only at the official MCP endpoint. Git credentials work only for Git transport.
  • Platform and Tokay Access credentials never cross. A tokay_machine_... credential opens one deployed app Project and cannot manage Tokay.
  • Revocation and membership changes are checked on every protected Platform operation.

Create credentials only with explicit grants

Each destination has its own creation grant.

Grant May issue
CREDENTIAL_CREATE_API JWT for the API audience
CREDENTIAL_CREATE_MCP JWT for the MCP audience
CREDENTIAL_CREATE_GIT Git HTTPS password or SSH public key registration

Creation grants are never implied or preselected. For ordinary creation, a bearer creates a narrower credential for the same Person or Bot. The new credential cannot carry a creation grant. Git credentials cannot create anything.

API and MCP leaves default to one hour, must expire within 24 hours, and cannot outlive a finite root. Git HTTPS and SSH leaves have optional expiry instead. They may be unbounded beneath an unbounded root, while a finite root caps them at its own expiry. All leaves die when their root and its lineage are revoked.

Creating a credential for another destination requires the exact creation grant and explicit acknowledgement. In particular, an API credential created from MCP works directly outside MCP tool restrictions and review flows. People and Bots can use either Git encoding.

Git grants may be wildcard, pinned to one Workspace, or pinned to one repository. A grant must name at most one target. SSH public key, grants, owner, lineage, and access expiry are immutable after registration. renamePlatformCredential changes only the label.

Connect an outside client with OAuth

Use Platform OAuth when a CLI, desktop agent, or MCP client needs approval without copying a token.

Destination Resource
GraphQL and API REST https://api.tokay.io
Official Tokay MCP https://mcp.tokay.io/mcp

Discovery starts at the resource metadata endpoint for protected resources. The client publishes an HTTPS Client ID Metadata Document and uses authorization code with S256 PKCE. Scope names must match the exact PermissionActionType casing.

Consent chooses whether the connection acts as the approving Person or an authorized Bot. Choosing a Bot records the Person's permission to use that Bot. Removing that permission or the Person's Workspace membership disconnects the connection. A later admin demotion does not silently remove the permission.

Every authorization creates an independent connection. Platform OAuth returns no refresh token or expires_in. The connection stays active until disconnected, revoked, or its principal loses authority. OAuth roots do not rotate. Disconnect and complete OAuth again. API and MCP credentials cannot be exchanged.

Create a Bot identity

Creating a Bot establishes identity and memberships only.

mutation CreateBot($workspace: ID!, $project: ID!) {
  createBot(input: {
    workspace: $workspace
    displayName: "deploy-bot"
    projectMemberships: [{ project: $project, role: MEMBER }]
    repoMemberships: []
  }) {
    bot { id principal { id displayName } }
  }
}

The Bot cannot authenticate until an authorized Person separately provisions a root credential.

mutation ProvisionBot($bot: ID!) {
  provisionBotCredential(input: {
    bot: $bot
    label: "deploy-bot API"
    credentialKind: API_JWT
    grants: [
      { action: PROJECT_STATUS }
      { action: PROJECT_LOGS }
      { action: PROJECT_DEPLOY }
    ]
    acknowledgeSurvival: true
    acknowledgeCrossDestination: false
  }) {
    platformCredentialCreation {
      jwt
      credentialKind
      destination
      isLeaf
      expiresAt
    }
  }
}

jwt is shown once. Provisioning creates a durable handoff. The Bot root survives the issuing credential's expiry, revocation, and OAuth disconnect. The Bot's memberships remain its resource boundary.

Create a narrower worker credential

An API or MCP root with the exact target creation grant can call issuePlatformCredential. The child cannot contain CREDENTIAL_CREATE_* and cannot exceed the root's ordinary grants.

mutation IssueLeaf {
  issuePlatformCredential(input: {
    label: "one deploy"
    credentialKind: API_JWT
    grants: [{ action: PROJECT_STATUS }, { action: PROJECT_DEPLOY }]
    acknowledgeCrossDestination: true
  }) {
    result { jwt destination isLeaf expiresAt }
  }
}

At MCP, use the dedicated reviewed tools issue_platform_credential and provision_bot_credential. The first call returns exact impact and a ten minute receipt. Repeating the unchanged request with that receipt executes once and returns the credential once.

Each Person or Bot has separate limits of 100 API roots, 100 MCP roots, and 100 independent Git credentials total across HTTPS and SSH. Each API/MCP root has its own 100-leaf counter across API, MCP, Git HTTPS, and SSH leaves, so a leaf never consumes a root slot or another root's capacity.

Rotate a manual API or MCP root

A live API or MCP root that was not created through OAuth can replace only itself. Rotation preserves its principal, label, destination, grants, and root position, then atomically revokes the predecessor and every direct leaf. Omitting expiresAt preserves the old expiry. A finite root may be shortened but not extended.

mutation RotateRoot {
  rotatePlatformCredential(input: {}) {
    platformCredentialCreation { jwt credentialKind destination expiresAt }
  }
}

The successor JWT appears once. If the committed response is lost, the old root is already dead and the successor cannot be recovered. MCP callers use the reviewed rotate_platform_credential tool, which previews the exact revoked lineage and independently surviving credentials. OAuth roots must disconnect and authorize again.

Register an SSH Git credential

An API or MCP root with CREDENTIAL_CREATE_GIT can register a narrower SSH credential for the same Person or Bot through the reviewed register_ssh_key MCP tool or registerSshKey GraphQL mutation. Supply explicit Git grants and acknowledge the Git destination. The registration may have an access expiry and is revoked with its root. The key authenticates Git only and is never a Workspace VM login credential.

mutation RegisterKey($workspace: ID!) {
  registerSshKey(input: {
    label: "deploy workstation"
    publicKey: "ssh-ed25519 AAAA..."
    grants: [
      { action: REPO_READ, workspace: $workspace }
      { action: REPO_WRITE, workspace: $workspace }
    ]
    acknowledgeCrossDestination: true
  }) {
    platformCredential {
      id
      label
      sshFingerprint
      expiresAt
      grants(first: 10) { nodes { action workspace { id name } repo { id name } } }
    }
  }
}

Use provisionBotSshKey or the reviewed provision_bot_ssh_key tool when a Person deliberately creates an independent SSH credential for a Bot. That handoff requires authority to manage the Bot and acknowledgement that it survives the provisioner's credential.

Revocation

Revoke the smallest credential set that contains the problem.

  • Self revokes one credential without replacement.
  • Lineage revokes a root and the direct leaves created for the same Person or Bot.
  • Revoking a Bot root always includes its leaves.
  • Passive expiry never sweeps descendants.

A lineage may have provisioned independent credentials for another principal. Revocation preview returns one review set and lets the user explicitly include any credential they can currently manage. Selected API/MCP roots bring their direct leaves. Routine revocation never crosses that boundary silently.

At MCP, use the reviewed revoke_platform_credential tool. The first call shows the exact Self or Lineage targets and independent review set. Show that impact to the user. Repeat the unchanged request with its receipt and the explicitly selected credentials only after they approve it. Generic GraphQL mutation access cannot revoke or rotate credentials.

Grant actions

Grants use exact PermissionActionType names. Common actions include:

Action Covers
WORKSPACE_READ Discover and read Workspace context
WORKSPACE_CREATE_PROJECT, WORKSPACE_CREATE_REPO Create Projects and repositories
WORKSPACE_MANAGE_ACCESS Members, Bots, operators, and Workspace connections
PROJECT_STATUS Project and Service state
PROJECT_LOGS Logs and runtime output
PROJECT_QUERY, PROJECT_EXPORT Managed data queries and exports
PROJECT_WRITE, PROJECT_DEPLOY Configure and deploy Services
PROJECT_WRITE_SECRETS Set Secret values without reading them back
PROJECT_MANAGE_ACCESS Project and Service access, public paths, Visitors, and Machines
PROJECT_DESTROY Permanent and destructive Project operations
REPO_READ, REPO_WRITE, REPO_MANAGE_ACCESS, REPO_DESTROY Repository equivalents

REPO_READ includes committed source and readable Project Config for API/MCP credentials. Git grants pinned to a Workspace or repository authorize only Git transport. Secrets remain write only.

Read a denial

A denied call names the missing action and target, for example Not authorized for PROJECT_DEPLOY on SERVICE <id>. Report that exact action and target. Do not retry unchanged or ask for broad authority when one named grant is enough.

Credential hygiene

  • Send API credentials as Authorization: Bearer <token> only to the API resource.
  • Never write a credential into source, Config, Secrets, task commands, or logs.
  • Prefer one distinct leaf per worker or subagent instead of sharing a bearer. This preserves attribution and independent revocation.
  • Discard a credential after 401 invalid_token. OAuth connections reauthorize rather than refresh.
  • Remove a Bot only when you intend to end the entire identity, memberships, connections, operator permissions, roots, and leaves.