Concepts
The words the rest of the guide uses, and how the parts fit. Each section ends where another page goes deeper.
The repository and its log
A repository lives in the daemon's store (~/.lonca/repos/<name>.git in
cooperative mode): a bare Git repository whose refs are trunk
(refs/heads/main), the policy (refs/lonca/policy), every proposal, and
the operation log (refs/lonca/oplog).
Every request that changes something becomes one entry in the log, with a
sequence number (#12), the actor (human:ayse, agent:claude/ses_…,
daemon), the operation and the decision: allow, deny or escalate.
Denied requests are logged too, so the log is also the record of what was
tried. Each entry is signed by the daemon and names the hash of the one
before it; a human's operations carry the human's signature as well.
- Only the daemon writes. It decides with
decide(), a pure function of the log so far, the request and the policy, signs the entry, appends it and only then applies it (moves trunk, records a lease). Nobody else writes the store, and a ref that changes behind its back is restored or freezes the repository ([integrity] on_unmediated). - Nothing is rewritten. An undo is a new operation that compensates for an old one (see Reviewing agents' work).
- Anyone can check it.
lonca-audit verifyreplays every decision and recomputes every fact and merge from the objects. The log travels withlonca push, so a remote can be audited in CI.
lonca log --repo demo lists the entries; lonca status --repo demo shows
the state they add up to.
Humans
A human is a name with a key, enrolled in a repository: the one who runs
lonca init is enrolled by it, others by an enrolled human
(lonca human enroll). Humans start sessions, approve, change the policy and
answer incidents; agents can do none of that.
- A file key (
lonca human keygen ayse) is a file in~/.config/lonca/humans. Anything running as you can sign with it, agents included. - A Secure Enclave key (
--secure-enclave, macOS) never leaves the Mac, and every signature needs Touch ID or the password. - A security key (
--security-key, a FIDO key through OpenSSH, on Linux too) never leaves the device, and every signature needs a touch. A repository withapprovals.require_presence = trueaccepts only these two kinds of key (Profiles). - A human session (
lonca human login, one hour by default) lets reads and prepared operations go without signing each time; every human command opens one by itself when it needs it.
Keys are rotated, revoked and recovered with signed operations: Recovery.
Sessions and capabilities
An agent never has a key or an account. A human gives it a session:
lonca session start --repo demo --agent claude --paths 'src/**' \
--task T-142 --ttl 2h --workspace ~/ws/claude-1 --harness claude-code
The session is a capability: what it may do, for how long, written into
the log. Its scope is the paths it may change (--paths), optionally
the symbols (--symbols), the size of one change (--max-files,
--max-lines) and directories outside the workspace it may write notes to
(--scratch). A request outside the scope is denied (scope_path:<path>,
scope_max_lines, …). A session ends at its TTL (at most
sessions.max_ttl_minutes, 8 hours by default), when the agent ends it, or
when a human revokes it.
- The token.
session startreturns a token once and writes it to the workspace (.lonca/session, mode 0600). The daemon keeps only its hash. - The workspace is a directory with trunk's files where the agent edits.
lonca syncbrings trunk in,lonca proposesends the local changes. - Child sessions. An agent can hand part of its work to a sub-agent with
cap.attenuate: a child session whose scope is the same or narrower, never wider. Revoking a session revokes every session made from it. - Continuing. A session can not be extended. A human continues its
workspace under a new session with
lonca session start --continue <dir>(Running agents).
Observation and taint
What the daemon knows about a session decides how much it trusts the
session's changes. The observation level says how much of the agent's
activity Lonca sees: unobserved, hooks, hooks_sandbox or isolated.
In cooperative mode every session is unobserved, and an unobserved
session is treated as tainted: each of its changes waits for a human.
A session is tainted when content from outside entered it: a web fetch,
a shell command that reached the network, another MCP server, a read of an
untrusted path ([taint] and [paths] untrusted in lonca.toml say which).
A tainted session's changes need a human, and it may not touch critical
paths at all ([paths] critical: build scripts, CI, lockfiles, agent
instructions such as CLAUDE.md). A taint never goes away for that session.
The levels, the harnesses and the hooks are in Running agents; the modes are in What Lonca is.
Symbols and leases
Lonca parses source files with tree-sitter and names their symbols:
src/billing.rs#Client.charge. Markdown, TOML, JSON and YAML files are split
into sections (README.md#Install.Linux); a file no parser reads is one
symbol, path#<file>. lonca symbols <file> lists the ids, and
Languages says which languages are read and what of their
newest syntax is not yet.
Before an agent changes a symbol, it leases it:
lonca lease acquire 'src/billing.rs#Client.charge' -i "rate limiting"
- Kinds.
editchanges a symbol's body;insertadds members to a type or a file (withnames, the names it will add);renamerenames or deletes it.shared, where the policy turns it on, lets several sessions edit one symbol and joins their edits line by line (Verification). - Sets. One acquire is one lease set (
lse_…), all or nothing, with an intent and a TTL (at mostleases.max_ttl_minutes, 30 by default).lonca mcprenews the sets while the agent runs; when the agent stops sending heartbeats they are freeleases.heartbeat_timeout_mslater (5 minutes). - Conflicts. Two sessions can not hold leases on one symbol, or on a
symbol and one inside it (
ClientandClient.charge), unless both areshared. Twoinsertleases go together unless they reserve the same name in one container. A refused acquire says who holds the symbol, why and until when. - Waiting.
lonca lease interestqueues for symbols someone else holds. When they are freed, the oldest interest gets them reserved for a minute (leases.reservation_ms). Agents can also settle a lease between themselves withnegotiate. - Pinned. A proposal pins the lease items it needs until the change leaves the queue, past their TTL; integration releases them.
Twenty agents can change twenty functions of one file at once: different symbols never conflict, at lease time or at merge time.
Proposals and changes
An agent (or a human) proposes the local changes of its workspace with a claim: an intent, and the symbols it says it edited, added, deleted or whose signature it changed.
lonca diff # the facts, as Lonca sees them
lonca propose -i "rate limit charge" --edits 'src/billing.rs#Client.charge'
The daemon computes the facts from the Git objects. A claim that differs
from the facts is denied (claim_mismatch:<field>); every symbol the facts
touch must be leased by the session (lease_missing:<symbol>). An allowed
proposal becomes a change with a change id: 32 letters from k to
z (kksuonzltktqystoounzkurnmrvyqxsx). Lists print its first 12.
A change has a status:
| Status | Meaning |
|---|---|
pending | waiting for human approval (lonca pending) |
queued | in the merge queue |
integrated | on trunk; commit is the trunk commit |
conflicted | the merge onto trunk met another change in the same symbols |
rejected | a human declined it, or integration failed; reasons say why |
withdrawn | taken back, or replaced by a newer proposal of the same workspace |
and a provenance: human (a human proposed it), clean (a session
that was observed and not tainted), tainted, unobserved, or reviewed
(a human approved it).
A workspace has one change waiting for review at a time: proposing again
replaces it (replaces:<change>), so the reviewer sees the latest version.
The merge queue
An allowed change is queued. The daemon's worker takes the head of the queue and:
- merges it onto current trunk with a symbol-level three-way merge: changes
to different symbols never conflict, changes to the same symbol do
(
conflicted, with the symbols named); - runs the repository's checks on the merged tree, in a sandbox with no
network:
build,test, the extended check for a change that alters a signature, and the named checks the change owes; - moves trunk only when they pass. A failure is
rejectedwithverification_failed:<check>, and the evidence (exit code, duration, the tail of the log) is in the log.
[queue] batch_size above 1 verifies several changes as one and bisects a
failure. Each check's result is recorded as evidence and never recomputed;
the merge itself is recomputed by the auditor. Commands, sandbox, quotas and
named checks: Verification.
Policy and approvals
refs/lonca/policy holds two files, and only a human changes them
(lonca policy set --config lonca.toml --policy policy.cedar):
lonca.toml: limits and settings (lease TTLs, critical paths, taint rules, verification commands, how many humans a policy change needs).policy.cedar: rules in Cedar. Each request is checked once per operation and once per file. Aforbidrule denies; aforbidwith@escalateturns the request into an approval request instead, and@approvals("2")asks for two humans. An evaluation error denies. What a rule can see, examples, andlonca policy check, which shows what a new policy would have decided differently before it is set: Writing policies.
A repository starts from a profile. The cooperative one
allows what no rule forbids and ships rules such as: agents may not delete
migrations, a change under auth/ needs two humans, a change of more than
50 files or 2000 lines needs one.
An operation is escalated when a rule says so, when its session is
unobserved or tainted (unobserved_session, tainted_session), or when an
unobserved session touches a critical path and the policy says to escalate
instead of deny. It then waits, with the number of approvals it needs
(approvals=1), until enough enrolled humans approve it or one rejects it.
With fewer enrolled humans than a rule asks for, all of them are enough
(approvals.cap_to_enrolled_humans). A human approves an operation once,
and an approval is a signed entry like any other. The day-to-day of it:
Reviewing agents' work.
Freezing
lonca freeze "<reason>" stops all work on a repository: every operation
is denied with frozen:<reason> except unfreeze, another freeze, and
the key operations of Recovery, which stay possible so that
an incident can be answered. lonca unfreeze "<reason>" resumes it.