Write a policy¶
A policy declares which identities may read or write the files under a path. The moment a path is covered by a read policy, TOVIO encrypts its files for you — automatically, on every commit — so the plaintext never lands in a tracked snapshot. This guide walks you through declaring your first policy and explains the difference between a clear file and an encrypted policy object.
Phase 1 — built today
Policies are part of the Phase 1 permission layer, which is done. tovio policy set and the
encrypt-on-snapshot behavior described here are built and enforced today: a path with a non-ANY
read policy is encrypted before it is hashed into a snapshot, so plaintext never lands in a tracked
tree — proven offline and over TLS.
flowchart LR
S["save file under path P"] --> M{"policy manifest<br/>matches P?"}
M -- no --> C["clear object (plaintext)"]
M -- yes --> E["encrypted at snapshot<br/>(DEK, wrapped per recipient)"]
More diagrams for the full permission and review flow: Permission & review workflows.
Before you start¶
- You need a repository created in Team or Agentic mode (
tovio init --mode team). A Simple-Mode repo has no cryptographic identity, so it has no policies — that is by design. An existing Simple repo can be upgraded in place withtovio identity init. - Policies live in the policy manifest, a versioned, signed object mirrored into
.tovio/policies/manifest.vex. You never hand-edit it;tovio policy setandtovio policy removerewrite and re-sign it. A supported policy-manifest commit retained in the local op-log can be reversed withtovio undo. Undo does not recall policy observations already received by peers or reverse independently issued, revoked, or rotated key material.
Declare a policy on a path¶
Use tovio policy set with a path pattern and a read expression. For example, to protect everything
under config/production/:
$ tovio policy set "config/production/**" --read "role=senior & clearance=secrets"
✓ Policy set for config/production/**
Files under config/production/** will be encrypted on the next commit.
That is the whole step. From now on, any file you save under config/production/ is encrypted before it
is hashed into a snapshot. You keep editing the plaintext on disk as normal — TOVIO handles the
encryption transparently when the change is committed.
Add a separate write expression if you want to gate who can push changes to the path, not just who can read it:
$ tovio policy set "config/production/**" \
--read "role=senior & clearance=secrets" \
--write "role=staff | role=security"
✓ Policy set for config/production/**
Files under config/production/** will be encrypted on the next commit.
--read is required; --write is optional and defaults to the read expression when you leave it
out. Once you set both, the two sides are independent: a path can be world-readable but write-gated, or
read-gated but open to write — whatever your situation needs.
Writing the expression¶
A policy expression is a boolean over attribute claims. The attributes (role, team, clearance, and
others) are issued to identities by your Key Authority — see grant and revoke access.
| You want | Write |
|---|---|
| A single attribute | role=admin |
| Both must hold | role=senior & clearance=secrets |
| Either is enough | role=admin \| role=engineer |
| Group with parentheses | (role=engineer \| role=senior) & clearance=secrets |
| Only AI agents with a clearance | entity=agent & clearance=deployment |
| A clear (plaintext) path | ANY |
Operator precedence runs tightest-to-loosest as NOT → AND → OR, so a=1 & b=2 | c=3 means
(a=1 & b=2) | c=3. Matches are exact and case-sensitive — clearance=secrets is a literal value, not a
level. The keyword forms AND, OR, NOT are exactly equivalent to &, |, ! if you prefer
readability, but only in upper case: lowercase and, or, and not are ordinary attribute names or
values, not operators.
Prefer positive grants for read policies
For --read, say who gets in (team=payments) rather than who is kept out (!team=contractors).
A negation in a read policy asks the system to reason about an attribute someone lacks, which is
error-prone. Negation is fine in a --write expression.
Clear files vs policy objects¶
Every file under a path is one of two things, decided solely by that path's effective read policy:
- A clear object — stored in plaintext, readable by anyone who can read the repository. This is every
ordinary file, and any path whose read policy is the keyword
ANY. - A policy object — stored encrypted. Its protected payload is opaque to an endpoint that lacks both an applicable policy decision and usable recipient key material. Merely storing or relaying the envelope does not provide that key; a full clone without an authorized recipient or recovery key still cannot decrypt it.
The policy expression itself is not secret. The manifest that lists every policy is always clear-readable, so anyone can see that a path is protected and by which expression — they just cannot read the protected content. This is what lets TOVIO evaluate policies offline, with no server.
.tovioignore is not access control
Ignoring a file keeps it out of version control entirely; it does not protect a tracked secret.
To keep a secret safely in the repository, give its path a non-ANY read policy. Never rely on an
ignore rule to hide credentials.
Carve a clear hole in an encrypted tree¶
When two policies match the same file, the most specific (longest, most literal) path wins — for both the read and write side. That makes "encrypt the directory, but leave one file public" easy to express:
$ tovio policy set "config/**" --read "role=devops"
✓ Policy set for config/**
Files under config/** will be encrypted on the next commit.
$ tovio policy set "config/README.md" --read "ANY"
✓ Policy set for config/README.md
Files under config/README.md stay clear (read = ANY).
config/README.md stays clear because its path is the more specific match; everything else under
config/ is encrypted. A less-specific policy never overrides a more-specific one in either direction.
Inspect and change policies¶
$ tovio policy list # every policy in the manifest
$ tovio policy show config/production/api-keys.env # the effective policy for one path
$ tovio policy test config/production/api-keys.env --identity ./alice-identity.pub # would Alice satisfy it?
$ tovio policy remove "config/production/**" # remove a policy (undoable)
tovio policy test answers "would this identity satisfy the policy?" entirely offline, which is handy
before you grant access or hand a path to a teammate. Note the two argument shapes: it takes a single
path, not a glob, and --identity takes the file holding the recipient's identity.pub (the
64-byte public identity a teammate shares from their own .tovio/identity.pub) — not a DID string.
Omit --identity and it evaluates your own identity.
tovio policy show also tells you what will happen on the next commit:
$ tovio policy show config/production/api-keys.env
config/production/api-keys.env → matched config/production/**
read: role=senior & clearance=secrets
write: role=staff | role=security
on commit: encrypted (policy object)
Who may change a policy
Only the repository owner can write an authoritative manifest. A manifest is policy authority
only while it carries the owner root's attestation (ADR-0305), so running tovio policy set on a
clone whose pinned owner root is somebody else refuses with TVO-PERM-007 rather than writing
something every peer and Forge would drop. If a manifest is present but unattested, tovio policy
sign re-attests it under the owner root without changing its content.
Next steps¶
- Grant and revoke access — add an identity to a policy you just wrote.
- Check access — confirm who can and cannot read a protected path.
- For the precise grammar and matching rules, the reference links the policy specification.
Last reviewed September 9, 2026
Suggest an improvement to this page Not for security reports — see disclosure