CoDNA Request access
Contents

Docs Decisions

0021 · An agent is a member of an instance, and knows what is shared with it

Status: Accepted · 2026-10-20.

Context

Today every agent connected to CoDNA acts for a person. Claude or ChatGPT signs in as someone and sees exactly what they see (0012). That serves someone working with their own assistant. It does not serve an agent a team runs for other people: a support bot, an onboarding guide, an assistant in a channel. Such an agent is nobody's assistant. It answers whoever talks to it, from what the team decided it should know.

Building that agent is not CoDNA's job. CoDNA calls no LLM (0010), and the agent's model, prompts, tools and hosting belong to whoever builds it. What CoDNA can be is its knowledge: a place where people decide, page by page, what the agent knows, and keep it current.

Decision

An agent is a third kind of member, beside people and management. It is a users row with kind = 'agent', a slug (support-bot), a name and a description. It has no password and cannot sign in to the web UI. It connects over MCP only.

Who it is

  • It authenticates as itself, with the instance's identity provider. The console makes it a confidential client in the instance's realm, with a service account. The agent exchanges its client id and secret for a short-lived token (client credentials) and presents that to /mcp. CoDNA checks it exactly as it checks a person's: issuer, signature, the <url>/mcp audience. 0012 holds: no long-lived key reaches /mcp, and CoDNA issues no tokens.
  • Its identity is linked when it is made, never on first sight. Creating an agent records identities (issuer, subject) for the service account. Auto-provisioning never makes an agent; an unknown service account has no email and is refused as it is today.
  • It is in no group. Not in Everyone, which every person is in, and not in any named group: management cannot add one. What it knows is only what was shared with it by name, so a page shared with Everyone or with a team does not silently become what a public bot answers from.
  • It is not a seat. Its calls are counted under its own row in usage_daily, and nothing that counts people counts it.

What it knows

  • It uses its own grants, not those of whoever talks to it. Sharing a page with an agent therefore shares it with everyone who can talk to the agent, and the share dialog says so.
  • Only its responsible people share with it. Management names them: in the hosted service, the console's admins for that customer. They may give it anything they can share, which since 0020 is anything they can edit. Anyone else who wants a page in the agent's knowledge asks one of them.
  • An agent is given read. A share with an agent asking for write is refused: it does not edit what people wrote.
  • It never shares, whatever it holds, and it deletes nothing, restores nothing, runs no automations, ingests nothing into raw and proposes no links.
  • It writes only to output: its own notes and drafts, which it may change and move. Those are private to it, as any creator's pages are, until a responsible person shares them.

Its MCP tool list is cut to match: reading and searching, and writing its own pages. A tool it may not use is not listed, and refused if called anyway.

Who answers for it

  • Its responsible people stand in for its creator on the pages the agent wrote: they read, edit, share and delete them. A policy rule, not grants, so the pages follow the list as it changes.
  • They see what it sees. A responsible person can switch to viewing as the agent: the tree, folders and search show exactly what it can read, read-only, with a banner, and every switch is in the audit. They read all of it in full. Hiding it would leave them answerable for what they cannot see, and anyone who can talk to the agent can draw it out anyway.
  • An agent page lists what the agent knows, who shared each page with it and when, with a remove button any responsible person may use; what it wrote; and what it recently searched and read.
  • Being made responsible is announced. It is a fourth way management widens an audience, beside group membership, ownership transfer and a quarantine's release: the person is told (agent.responsible_added, agent.responsible_removed), and it is in the audit.

Ending one

Disabling an agent stops its token at once. Deleting it disables it, unlinks its identity, and hands what it wrote to a person, by the same transfer a departing person's pages go through. The row stays, so its grants and its pages' history keep a name.

The management API

The console and the codna agents command use the same calls. {id} is an id or a slug.

POST   /api/manage/agents                    create: {slug, name, description, subject, responsible: [email]}
GET    /api/manage/agents                    list
GET    /api/manage/agents/{id}               one
PATCH  /api/manage/agents/{id}               {name, description, active}, each optional
PUT    /api/manage/agents/{id}/responsible   {people: [email]}: the whole list
DELETE /api/manage/agents/{id}?transfer_to=  disable, unlink, hand its pages to a person

An agent is answered as {id, slug, name, description, active, subject, responsible: [{id, email, name}], created_at, last_used_at}. A slug is 2 to 40 of a-z, 0-9 and -. A slug or subject already taken is a conflict; a responsible person who is not an active person is refused.

Consequences

  • An agent built anywhere, on any framework, gets a knowledge base that people curate in the tool where they already write, and that stays current as they edit it.
  • What an agent can say is decided by named people, page by page, and can be checked by viewing as it.
  • The agent's own answers are not in CoDNA. Who asked it what, and what it said, is the builder's to keep.
  • An agent cannot act for the person talking to it, so it cannot answer from what only that person may see. Acting on someone's behalf (token exchange) is a separate decision, if wanted.
  • An agent framework that cannot fetch a token, and wants a fixed key, does not work as is. The token lifetime can be set per client before that is reconsidered.
  • One more kind of member is one more case in every policy function, tested in the same matrix.

Alternatives rejected

An agent builder inside CoDNA, with a model and key set in the console. It contradicts 0010, brings prompts, tool orchestration, hosting and a model opinion into the product, and is not needed to find out whether teams want CoDNA as their agents' knowledge.

An agent as a person with a service login. It would be in Everyone and every group, its knowledge would grow whenever anyone shared widely, and nobody in particular would answer for it.

A static API key for agents. Simpler for some frameworks, but 0012 rejected long-lived bearer keys on /mcp, and disabling the agent in the identity provider would no longer be the one place it ends.

Anyone may share with an agent, or anyone may with a responsible person's approval. Sharing with an agent publishes to its audience. Keeping that to the people who answer for it is the point; an approval step can come later if asking them is too slow.