kosa8 docs

Guides / Agent identity

Agent identity

Without an identity, every agent on a machine is the same agent, and "agent" in the audit log is a word the caller chose for itself. An agent identity is a name, a policy scope and a token that kosa8's service checks on every request.

Why a label is not an identity

When an AI client drives kosa8, each request it sends is marked as coming from an agent. That mark is a claim. Nothing checks it, and nothing in it says which agent. Two things an organisation needs are impossible while that is all there is.

A self-declared label

  • The reviewer bot and the release bot get the same tools, because policy has one default scope for "an agent".
  • The audit log says an agent did it. It cannot say which.
  • An approval given to one agent's call fits another agent making the same call.

A verified identity

  • Each agent acts in its own scope, chosen from its record and never by the caller.
  • Every audit entry carries the agent's id, name and scope.
  • An approval belongs to the agent that asked.

How it works

How an agent token is verified A person mints a token with kosa8 agent token create. The token is shown once and given to the agent's gateway in the KOSA8_AGENT_TOKEN environment variable. The gateway presents it on every request to the kosa8d service. The service compares it with the digest in its registry, finds the record, takes the policy scope from that record, and writes the agent's id, name and scope into every audit entry. a person mints kosa8 agent token create reviewer the token shown once, never stored kosa8 mcp serve reads KOSA8_AGENT_TOKEN every request kosa8d hashes the token and looks it up in agents.json reviewer its record: id, scope, expiry scope: eng from the record, never the caller audit log every entry carries refs.agent: id agt_… name reviewer scope eng
The token proves which record. The record decides what the agent may do.
  1. A person creates the identity. An agent cannot.
  2. The token is printed once. kosa8 keeps only a SHA-256 digest of it.
  3. The agent's gateway is started with the token in its environment.
  4. The service receives the token with each request, hashes it, and looks for the match.
  5. A match gives a record: who this is, when it expires, whether it was revoked.
  6. Tool calls are decided in the scope written in that record.
  7. The audit entry for the request names the agent, by an id that never changes.

Walkthrough

Mint an identity

$ kosa8 agent token create reviewer --scope eng --ttl 1h
agent reviewer (agt_1a2b3c4d5e6f), scope eng, expires 2026-…
the token is shown once and is not stored; give it to the agent's gateway as KOSA8_AGENT_TOKEN
k8a_…

The two lines of explanation go to standard error and the token goes alone to standard output, so a script can capture it without it passing through a terminal's scrollback.

FlagMeaningDefault
--scopeThe MCP policy scope the agent acts in.The policy's default_scope
--ttlHow long the token works, as a duration such as 8h or 720h.24 hours. At most 8760h, which is 365 days.

A name is 1 to 64 characters from letters, digits, -, _ and .. Names appear in audit entries and queries, so they are identifiers, not sentences.

Give it to the agent's gateway

The gateway is the kosa8 mcp serve process your AI client launches. Put the token in its environment as KOSA8_AGENT_TOKEN. It is read from the environment and not from a flag so that it never shows up in a process listing.

$ KOSA8_AGENT_TOKEN=k8a_… kosa8 mcp serve
kosa8 MCP gateway: AI session …
kosa8 MCP gateway: serving agent reviewer (agt_…), scope eng

The gateway asks the service who the token is before it does anything else. If the service does not accept it, the gateway does not start.

See who exists

$ kosa8 agent token ls
ID  NAME  SCOPE  STATE  EXPIRES

One row per identity, newest first. STATE is live, expired or revoked. A scope shown as (default) means the identity follows the policy's default scope. Tokens are never listed, because kosa8 does not have them.

Take it away

$ kosa8 agent token revoke reviewer
revoked reviewer

Revoke by name or by id. The record stays, marked as revoked, so old audit entries that name the id still lead somewhere. The service checks the registry on every request, so the next request with that token is refused.

What changes once a gateway is identified

ScopeWhich tool servers and tools the agent may use, and which rules apply, are decided in the identity's scope. Not the policy default, and never a scope the caller asks for.
AuditEvery request the gateway makes is recorded with refs.agent: the id, name and scope the service verified. That covers tool decisions, tool results, sandboxes it creates, and everything else.
ApprovalsA call held by an ask rule is held for this agent. An approval is bound to its id and cannot be spent by another agent making the same call. See Rules for tool calls.
HTTP clientsWith --http, and no KOSA8_MCP_TOKEN set, clients authenticate to the gateway with the same agent token (Authorization: Bearer …). One credential names the agent at both ends, and revoking it closes both.

A different scope for each agent

Here are two agents with different jobs. The reviewer reads. The release bot may merge, and only after a person has approved that exact merge.

Two agents, two scopes, one policy Two gateways on one machine. The first holds the reviewer's token and acts in the review scope, where only read tools are listed. The second holds the release bot's token and acts in the release scope, where merging is allowed after a person approves the exact call. Both scopes are in one signed policy. reviewer its own token signed policy scope "review" scope "release" read tools only merge tools are never offered release-bot a different token may merge once a person approves that call
Neither agent can ask for the other's scope. The scope is in the record its token leads to.
  1. The reviewer's gateway presents the reviewer's token.
  2. The service finds its record and applies the scope written there.
  3. That scope lists read tools, so those are the only ones the reviewer is shown.
  4. The release bot presents a different token and lands in a different scope.
  5. There, merging is permitted behind an ask rule.

The policy:

{
  "name": "example-corp",
  "default_scope": "review",
  "scopes": [
    { "name": "review",
      "description": "read-only agents",
      "servers": [
        { "server": "github",
          "allow_tools": ["list_*", "get_*", "search_*"] }
      ] },
    { "name": "release",
      "description": "may merge, with a person's approval",
      "servers": [
        { "server": "github",
          "allow_tools": ["list_*", "get_*", "merge_*"],
          "rules": [
            { "id": "merge-needs-a-person",
              "tools": ["merge_*"],
              "effect": "ask",
              "reason": "merging changes what ships" }
          ] }
      ] }
  ]
}

The two identities, minted by a person:

$ kosa8 agent token create reviewer --scope review --ttl 8h
$ kosa8 agent token create release-bot --scope release --ttl 8h

Each gateway is started with its own token. Making the least capable scope the default means a gateway with no identity gets the least.

Check the scope name

Minting checks that a scope is a well-formed name, not that the policy defines it. An identity whose scope the policy does not define has every call denied, with a message that lists the scopes that do exist. kosa8 mcp-policy scopes prints them.

Requiring identity

By default a gateway with no token still works, as the unidentified agent it always was. An organisation can end that with one field in the signed organisation policy:

{
  "name": "example-corp",
  "require_agent_identity": true
}

Sign and install it as described in Company rules (kosa8 policy sign, then kosa8 policy install). Under it, two things are refused.

WhatWhat it is told
Starting kosa8 mcp serve with no tokenthe org policy requires agents to identify themselves: mint a token with `kosa8 agent token create NAME` and start this gateway with KOSA8_AGENT_TOKEN set to it
Any request to the service that says it is from an agent and carries no tokenHTTP 401: the org policy requires agents to identify themselves, and this request carries no agent token

Commands you type are not marked as an agent's and are unaffected. If the organisation policy is present and fails verification, the requirement is treated as set: a requirement that cannot be read is not assumed to be absent.

How tokens are kept

QuestionAnswer
What is stored?A SHA-256 digest of the token, in ~/.kosa8/agents.json, readable only by you. A copy of that file in a backup or a support bundle is not a set of working credentials.
Can I see a token again?No. It is shown once. A lost token is replaced, not recovered.
What does a token look like?k8a_, the identity's twelve hex digits, and a 256-bit random secret. The id on its own is agt_ and the same twelve digits.
How do I rotate one?Revoke the name, then create it again. One name can have only one live token: creating a second is refused with an agent named "reviewer" already has a live token (agt_…); revoke it first with `kosa8 agent token revoke reviewer`.
What happens at expiry?The token stops working. Create a new one. There is no "never": the longest lifetime is 365 days, and asking for more answers a token may live at most 365 days; rotate longer-lived identities.

A bad token is refused, not ignored

A request that presents a token which is wrong, expired or revoked is answered with HTTP 401. It is never served as if it had presented no token at all. If it were, revoking an identity would leave behind a working anonymous one, and the revoke would have revoked nothing.

agent token is not recognised; mint one with `kosa8 agent token create NAME`
agent reviewer (agt_…) was revoked on 2026-…; mint a new token with `kosa8 agent token create`
agent reviewer (agt_…) expired on 2026-…; mint a new token with `kosa8 agent token create`

A gateway started with such a token prints the same sentence after the daemon did not accept the agent token: and exits. An unknown id and a wrong secret get the same answer on purpose: saying "that id exists" would be telling the caller half of a credential.

An agent cannot mint identities

An identity carries a scope. An agent that could mint itself a second identity could pick a wider scope than the one it was given. So a request to create an identity that arrives with an agent token, or marked as an agent's, is refused with HTTP 403: an agent cannot mint agent identities.

What the audit log shows

An entry made by an identified agent has the actor agent and a refs.agent object. An entry with the actor agent and no refs.agent is now visibly an unverified claim: something said it was an agent and proved nothing.

Verified

"actor": "agent",
"action": "mcp.decision",
"refs": {
  "agent": {
    "id": "agt_1a2b3c4d5e6f",
    "name": "reviewer",
    "scope": "eng"
  },
  "mcp": { "phase": "call", … }
}

Unverified claim

"actor": "agent",
"action": "mcp.decision",
"refs": {
  "mcp": { "phase": "call", … }
}

Refused attempts are recorded as well, as agent.auth.refused with status 401 and the reason. Read entries with kosa8 audit ls --actor agent, or kosa8 audit ls --json for the refs. More in The audit log.

Licensing

Minting an identity needs the Governance subscription, or a trial. Listing and revoking never do: a lapsed subscription must never be the reason an identity could not be taken away.

What this does not do

  • Tokens are bearer tokens. Whoever holds the token is that agent. It is not tied to a process, a machine or a sandbox. Keep lifetimes short and revoke what you no longer use.
  • There is no single sign-on. Identities are created with a command on each machine and are not connected to a company directory.
  • It identifies an agent to the service. It does not stop a process pretending to be you. The service's socket belongs to your user, and anything running as you can call it without a token, as a person would. What changes is that such a call cannot carry an agent's identity, and that an agent entry with no identity is visible as an unverified claim.
  • The rule against agents minting identities trusts the same mark. It stops an agent's own gateway. It does not stop a process that lies about being an agent.
  • The token is in the gateway's environment. A program that can read that environment can copy it. Run the agent itself in a sandbox, where the gateway's environment is out of reach.
  • A revoke stops a running gateway at its next call, not mid-call. The gateway records each decision with the service before it runs the tool; once the service rejects the identity, the gateway refuses the call (this gateway's agent identity (…) is no longer accepted by the daemon). A tool call that was already running when you revoked finishes. Stop the gateway too if that matters.

Where to go next