Coding agents read instruction files: CLAUDE.md, AGENTS.md, GEMINI.md, .cursorrules, .cursor/rules/*, .github/copilot-instructions.md, .windsurfrules, .clinerules. People keep personal and team versions of them out of git, so they exist on one machine only. Govern stores, versions, approves and layers these files, and records which exact versions an agent session started with.
Content is opaque text: Govern parses nothing in it. Skills and harness packs are managed elsewhere. There is no console page for agent files yet; the first-party apps and the approvals inbox use the API.
Layers and the effective set
| Layer | Owner | Notes |
|---|---|---|
organization | The organisation | May hold mandatory fragments |
department | A department | |
team | A team | |
project | A project | |
repository | The indexed repository | Read-only: the files committed at the repository root and directly in .cursor/rules/ |
personal | One person | Visible to its owner only |
GET /api/v1/context/agent-files/effective?repo=&project=&tool= returns the effective set for the caller:
- Which files apply: the organisation, the departments and teams the person belongs to, the project (when given, or matched from
repo; the caller must be able to read it), the repository layer of that project, and the person's personal files. - Which tool:
claudereads CLAUDE.md;codexandagentsAGENTS.md;geminiGEMINI.md and AGENTS.md;cursor.cursorrules,.cursor/rules/*and AGENTS.md;copilot.github/copilot-instructions.mdand AGENTS.md;windsurfandclinetheir file and AGENTS.md. A custom file applies to the tools it names, or to all. - Order: organisation, department, team, project, repository, personal; mandatory fragments first within their layer.
- Only effective versions are served. Identical content (same hash) appears once, in the earliest layer.
- Withheld, never deleted: files that apply but are not served (the policy was tightened, a deny rule now matches, a committed file holds a secret) are listed in
withheldwith a reason and never their text. - Rendering:
rendering.textis the sections in order, each between provenance markers (layer, owner, file, version, SHA-256, mandatory) with a boundary token derived from the set hash, so a file cannot close its own section. Contradictory layers are shown, not resolved.setHashidentifies the exact set.
Every read is audited (context.agentfile.effective.read) with the person, the verified OAuth client and the declared X-Keystone-Client.
Mandatory fragments
An organisation administrator can mark organisation-layer files as mandatory. They always come first, are served whatever the policy says about layer, kind or size, and nothing in a lower layer can remove or replace them. A lower layer cannot be mandatory (context.agentfile.mandatory_layer).
Versions and approval
- Every change is a new immutable version with author, time, reason (1 to 1000 characters, required), content hash and parent. Nothing is updated or deleted: the tables are append-only at the database.
- A new version is a draft until submitted. Submitting makes it effective, or pending approval where the layer needs approval. A draft or pending version never replaces the effective one.
- Approval follows the policy. By default the organisation and department layers need one approver. The collaboration policy can require
approval(one approver) ortwo_person(two distinct approvers) per layer. - Nobody approves their own version, in any mode and through any credential of theirs (
403 context.agentfile.separation_of_duties). API keys cannot approve. - Pending versions also appear in the approvals inbox (
GET /api/v1/approvals/inbox, sourcecontext_agent_file). - Rollback writes a new version with the content of an earlier effective one. Archive writes a tombstone version that removes the file from every effective set.
- Legal hold (organisation administrators) forbids archiving the file and postpones its deletion.
- Classification is per version and never looser than the previous version or the layer's space.
Files Govern never reads
A hard deny list, compiled in, applies to every write and share, by path and by content. No person, administrator or policy can remove a rule from it; an organisation can only add patterns.
| Rule | Examples |
|---|---|
| Environment and secret files | .env, .env.*, *.env, .envrc, secrets.*, credentials*, .pgpass, .htpasswd |
| Credential files | .netrc, .npmrc, .pypirc, .git-credentials, .gitconfig, .docker/config.json, kubeconfig* |
| Infrastructure state | *.tfvars, *.tfstate |
| Keys and certificates | *.pem, *.key, *.p12, *.pfx, *.jks, *.keystore, *.ppk, *.gpg, *.asc, *.p8, id_rsa, id_ed25519, id_ecdsa, id_dsa |
| Cloud credentials | .aws, .azure, .gcloud, .boto, service-account*.json |
| Git internals and SSH | .git, .ssh, .gnupg |
Names are normalised first (Unicode compatibility forms and look-alike letters folded, case folded, separators unified, dot segments and trailing dots removed, NTFS stream suffixes cut). Content rules refuse private key blocks, cloud and registry credentials, Terraform state and text that is mostly KEY=value lines, then the organisation's DLP engine scans the content and the reason. A refusal returns the rule and the class, never the content (context.agentfile.denied, context.agentfile.secret_found).
The same list is applied by the code indexer, so these files are never fetched from a repository either.
Custom shares
A person can share one exact file that is not a known instruction file as a custom file, to the members of a node.
- Off by default. Without the organisation's opt-in (
agent_file_policy.custom_shares_enabled), the answer is403 context.agentfile.share_disabled. - One exact file name: no folder, no pattern, no synchronisation of a directory. The name must match the organisation's allow-list and not the deny list.
- The policy also sets the allowed layers and audiences, the number and size of custom files, and whether a share needs approval.
- Every step is an audit event: attempt (recorded before anything is checked), refusal, publish or pending, approval, first view by each person, revocation.
- Administrators list every share (
GET /api/v1/context/agent-files/shares) and revoke any (POST .../shares/{shareId}/revoke), which writes a tombstone that takes effect at once.
Clients must show the person exactly what will be shared (name, size, hash, layer, audience, reason) before sending, and run the same deny list locally first.
Committed files
| Case | Govern | The client |
|---|---|---|
| The repository has CLAUDE.md, Govern has none | It is the repository layer; nothing is copied | Inject the effective set into the session |
| Both exist | Both are layers; identical content appears once | Same |
| The committed file changes on push | A new repository-layer version and an agentfile.updated feed event | Fetch the effective set again |
| A person edits the committed file locally | Accepted only as a personal overlay or a proposal (POST /api/v1/context/agent-files/tracked-edits); Govern never writes to the repository | Offer "save as personal overlay" or "propose to the repository" |
| A tool expects its file at a tracked path | Nothing | Never overwrite a tracked file; write to the tool's local override location and add it to .git/info/exclude first |
Session evidence
When an agent session starts, the client records it:
POST /api/v1/context/agent-files/sessions
{"projectId": "<id>", "tool": "claude", "clientSessionId": "<client id>", "setHash": "<hash the client holds>"}
The server builds the set itself and compares the client's hash (changedSince). The record is stored and appended to the HMAC-chained evidence (type agentfile_session, the same chain as retrieval attributions), so an auditor can reconstruct which instructions governed an agent's actions. The same clientSessionId returns the first record.
Routes
| Method | Path | What |
|---|---|---|
GET, POST | /api/v1/context/agent-files | List files the caller may read; create a file with its first version |
GET | /api/v1/context/agent-files/{id} | One file with its effective content |
GET, POST | /api/v1/context/agent-files/{id}/versions | List versions; add a version |
GET | /api/v1/context/agent-files/{id}/versions/{vid} | One version with content |
GET | /api/v1/context/agent-files/{id}/diff?from=&to= | Line diff of two versions |
POST | .../versions/{vid}/submit, /approve, /reject | Submit a draft; approve or reject a pending version |
POST | /api/v1/context/agent-files/{id}/rollback, /archive | Rollback; tombstone |
PUT, DELETE | /api/v1/context/agent-files/{id}/legal-hold | Set or clear a legal hold |
GET | /api/v1/context/agent-files/effective | The effective set |
POST, GET | /api/v1/context/agent-files/sessions, /sessions/{id} | Record and read session evidence |
POST | /api/v1/context/agent-files/tracked-edits | Overlay or proposal for a tracked file |
POST, GET | /api/v1/context/agent-files/shares | Custom share; list shares |
POST | /api/v1/context/agent-files/{id}/share | Share a personal file outward |
POST | /api/v1/context/agent-files/shares/{shareId}/revoke | Revoke a share |
Write permissions follow the layer: the organisation layer needs context:space:admin on the organisation root; department, team and project layers need context:space:write on the node; personal files belong to their owner. The read routes return actions that tell a client which controls to show; the routes still decide.
Not available yet
- Nested instruction files in subdirectories of a repository are not read; only the root and
.cursor/rules/. - There is no export route and no retention job: versions are never deleted.
- The collaboration policy's approver roles and mandatory fragment references are not applied to agent files; approval falls to administrators of the layer's node or approvers the decision lists.