Skip to main content

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 verify replays every decision and recomputes every fact and merge from the objects. The log travels with lonca 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 with approvals.require_presence = true accepts 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 start returns 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 sync brings trunk in, lonca propose sends 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. edit changes a symbol's body; insert adds members to a type or a file (with names, the names it will add); rename renames 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 most leases.max_ttl_minutes, 30 by default). lonca mcp renews the sets while the agent runs; when the agent stops sending heartbeats they are free leases.heartbeat_timeout_ms later (5 minutes).
  • Conflicts. Two sessions can not hold leases on one symbol, or on a symbol and one inside it (Client and Client.charge), unless both are shared. Two insert leases 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 interest queues 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 with negotiate.
  • 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:

StatusMeaning
pendingwaiting for human approval (lonca pending)
queuedin the merge queue
integratedon trunk; commit is the trunk commit
conflictedthe merge onto trunk met another change in the same symbols
rejecteda human declined it, or integration failed; reasons say why
withdrawntaken 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:

  1. 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);
  2. 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;
  3. moves trunk only when they pass. A failure is rejected with verification_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. A forbid rule denies; a forbid with @escalate turns the request into an approval request instead, and @approvals("2") asks for two humans. An evaluation error denies. What a rule can see, examples, and lonca 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.