Architecture
This document is the map. It says which crate owns what, which way dependencies
point, how threads are laid out and what happens at startup and restart. The rules a
contributor follows inside a crate are in CONVENTIONS.md.
Shape
eggfriedrice.code is a Cargo workspace of one crate per bounded context plus xtask.
Three binaries ship:
efrd, the daemon, runs as a systemd user service (systemd/efrd.service). It owns the conversations, the model loop, the hidden shells, the screens and the database.efr, the CLI, is a thin relay to the daemon. The zsh plugin (shell/zsh/efr.plugin.zsh) calls it for every,line.efr-sbx, the sandbox launcher of theautomode, is installed next to efrd in~/.local/lib/efr(/usr/lib/efrfrom the AUR packages) and is never on thePATH. The hidden shell runs a copy of it for each contained call, and efrd starts a copy for each background job (efr-sbx run --job), with no terminal, in a session of its own.
All clients speak one protocol, defined in efr-protocol, over a Unix socket at
$XDG_RUNTIME_DIR/efr/daemon.sock. With [remote] enabled, efrd also listens on the
machine's Tailscale IPv4 address for the phone app in app/ (React Native and Expo,
outside the Cargo workspace). That listener is a WebSocket with the same frames, one
per text message. efrd checks each peer with tailscaled's whois before it reads a
byte, and the phone proves a paired device key before any other method runs
(ADR 0022).
Crate map
Tiers are a reading aid; the enforced rule is the per-crate allowlist in
xtask/src/deps.rs. Kind dev means the crate is never a normal dependency of a
shipped binary.
| Crate | Kind | Tier | Owns | Allowed workspace dependencies |
|---|---|---|---|---|
efr-stdx | lib | 0 | XDG paths, Clock and Rng traits, process::command, atomic and 0600 writes, files changed through a link, named threads, UUIDv7 | none |
efr-protocol | lib | 0 | everything on the wire: frames, Method, params and results, Event, ids, Scope, ShellContext, framing, PROTOCOL_VERSION; no tokio, no IO | efr-stdx |
efr-store | lib | 1 | the only SQLite owner: migrations, the single writer, readers, events, projections, receipts, outbox, recording index, turn messages, the search index (FTS5) with its reads, its search lanes and the indexer's read connection, the tombstones and steps of conversation deletes, the enrolled devices with the feed of their revokes | efr-protocol, efr-stdx |
efr-credentials | lib | 1 | SecretStore and the 0600 file store; optional keyring | efr-stdx |
efr-permissions | lib | 1 | pure policy: path classes, the built-in policy of each permission mode (manual, cautious, auto, full), config protection, the exits of the auto sandbox, the settle pass of full mode and its delete guard, the views of the host that a line gets with no exit, the rules of the bus filter (the built-in read methods, the checks of [[dbus.allow]] and the proxy's arguments), the always-allow rules with their floor checks and their match, and the Allow / Contain / Ask / Deny decision | efr-protocol |
efr-scope | lib | 1 | cwd to Scope: git discovery, dotfiles layouts, the project registry and its changes that keep comments | efr-protocol, efr-stdx |
efr-holder | lib | 1 | the PtyHolder trait and holder wire types; no IO, no unsafe | efr-protocol, efr-stdx |
efr-http | lib | 1 | the reqwest client, SSE parser, WebSocket client, Unix-socket HTTP client, the newline-delimited JSON reader, the typed client of tailscaled's LocalAPI (status, whois, the IPN bus watch), header redaction | efr-stdx |
efr-screen | lib | 1 | the Screen trait, ScreenActor and ScreenHandle, the OSC 133 and OSC 7 scanner, the conformance suite; vt100: Screen over vt100, the Zig-free default | efr-protocol, efr-stdx |
efr-provider | lib | 1 | the Provider and TokenSource traits, canonical messages, function and freeform tool definitions | efr-protocol, efr-stdx |
efr-test-support | dev | 1 | TestClock, seeded TestRng, temp dirs, in-memory store, NDJSON reader, ReplayProvider, Wait | efr-protocol, efr-store, efr-provider, efr-stdx |
efr-render | lib | 1 | markdown and render events to ANSI: committed and live zones, colour roles and the palette, syntax colours, OSC 8 links, widths by code point or grapheme cluster; no IO, the CLI passes RenderOptions | none |
efr-sandbox | lib | 1 | the pure logic of the auto sandbox: SandboxSpec, MountPlan and the bwrap arguments, Landlock and seccomp as data, the environment and export filters, the records, the sandbox state, result.json, the surface guard, the worktree record, the probe's result types, the mount table parse and the df and findmnt --df renderer of the mount view, the rules of job port forwards (the parse of the TCP tables, the loopback listens, the ports that efr never forwards, the debounce, the peer check) and their 28-byte frames; file access only through FsView, no tokio, no unsafe | efr-protocol |
efr-screen-ghostty | lib | 2 | Screen over libghostty-vt; the only crate that needs Zig | efr-screen |
efr-pty | lib | 2 | LocalPtyHolder: openpty, setsid and TIOCSCTTY in pre_exec; the only unsafe code at milestone 1 | efr-holder, efr-stdx |
efr-shell | lib | 2 | one hidden zsh per conversation, shell state from marks, run_command, the input waits of a command, the watch of a command left running in the shell after its call answered, and the adopted run: the hand-over of a running call and its old shell to a background job when the user moves it with Ctrl+B | efr-holder, efr-screen, efr-protocol, efr-sandbox, efr-stdx |
efr-sbx | bin efr-sbx | 2 | the launcher of the auto sandbox: run (one call in bwrap with Landlock and seccomp, or the exit child as a subreaper; with --job a background job with a lifeline, low priority and a stop), inner, probe, guard-check (the surface guard of a running job), statvfs (efrd's helper that measures the mounts, which efrd can kill), mounts (the mount view's df and findmnt --df tables inside a call) bridge (the network proxy's bridge inside a call, from 127.0.0.1:3128 to the call's proxy socket, with its own Landlock domain and seccomp filter), forward (the forwarder of a contained job: it reads the job's TCP tables, reports each loopback listen to efrd over the job's forward socket and connects inside the job for each of efrd's connections, with its own Landlock domain and seccomp filter), reach (the reach helper of a contained call: it listens on the forwarded ports of its conversation's jobs in the call's own network namespace and sends each connection to efrd over the call's reach socket, with its own Landlock domain and seccomp filter) and dbus-proxy (runs xdg-dbus-proxy for one filtered bus of a call, with its own Landlock domain, seccomp filter and fd limit); checks what comes back and writes result.json last; no async runtime; its one unsafe module is fds.rs (ADR 0007) | efr-sandbox, efr-protocol |
efr-tools | lib | 2 | the Tool trait, the registry, the shell, read_file, write_file, apply_patch and edit tools, the freeform tool spec; knows nothing about permissions; patch: the patch engine of apply_patch and edit (the patch text to file operations, its Lark grammar, hunks matched with tolerant passes, every new content computed before anything is written, one exact replacement; no IO, no tokio) | efr-shell, efr-scope, efr-protocol, efr-stdx |
efr-providers | lib | 2 | openai: the Responses API client over HTTP or a WebSocket for each conversation; the model catalog (the fetch from the backend, its cache file and the built-in table, which the API key backend cuts down to the ids that its key lists), which also says which models take freeform (custom) tools and prefer WebSockets; the check of an API key; takes tokens only through TokenSource; anthropic: the Messages API client with an API key (provider id anthropic-api): the request body with its prompt cache markers (a pure function), the stream mapping, the usage sums, the error and retry classifier, the model catalog from GET /models with its cache file and no built-in table, the key check; takes the key only through TokenSource; the two modules share no code | efr-provider, efr-http, efr-protocol, efr-stdx |
efr-oauth-openai | lib | 2 | the subscription login: PKCE, loopback callback, refresh, OpenAiTokenSource | efr-http, efr-credentials, efr-provider, efr-stdx |
efr-snapshot | lib | 2 | efr's own snapshot store: one bare git repository per project or $SCRATCH in the data root, hardened git through efr_scope::Git::command, the trees before and after each call that can write, the turn's pre, last and post refs and the paths that its calls touched, the mirror of a project's refs and objects from the git dirs that efrd trusts, the check that a snapshot covers each file below a target, the changes of a call or a turn, the diff of a turn, the plan of an undo or a redo (pure: it writes no file of a root), the home store of the files outside the roots, the collector | efr-scope, efr-protocol, efr-stdx |
efr-config | lib | 2 | config.toml for efrd and efr: the schema of every key, defaults, validation, the effective view with sources, the JSON schema, the example file and the format-preserving writer; no async, no network | efr-permissions, efr-protocol, efr-stdx |
efr-proxy | lib | 2 | the network proxy of the auto sandbox, as tokio tasks in efrd: one listener per contained call, the request head and ClientHello parsers, the allow and deny lists before any lookup, the call's host grants, the address guard, the SNI check, plain GET and HEAD for http hosts, the upload budget and the refusal records; the resolver, the connector and the host's addresses are seams | efr-protocol, efr-stdx, efr-permissions |
efr-conversation | lib | 3 | one actor per conversation: queue, turn loop, the single permission check point, approvals, interrupt, steer, withdraw; in auto the launch of each shell call, exit questions and their records (in full mode too, with the judge full, the timed question of a guarded delete and the gate before each call), the review of an exit by the ExitJudge before the user with the allow rule and the naming rule, the denial pause, the away wait through PresenceReader, the cache delete questions, the quarantine question and the fallback to cautious; the undo of a turn between turns; the index redaction of search (redact_for_index, REDACTION_VERSION); drives tools through its own Toolbox trait, implemented by efr-daemon | efr-provider, efr-permissions, efr-scope, efr-store, efr-sandbox, efr-protocol, efr-stdx |
efr-transport | lib | 3 | the protocol edge: codec, Unix listener, the WebSocket entry of the phone (handshake, frames, pings; efrd owns its TCP listener and peer check), connection table, subscriptions, the Dispatcher trait; client: the client side of the protocol for efr, tests and the proxy | efr-protocol, efr-stdx |
efr-daemon | bin efrd | 4 | the composition root; one file per protocol method; the settings tool, which needs efr-config and so cannot live in efr-tools; the auto sandbox service: the launcher's copy, the probe, the spec of each call, the plan lock, the facts of a line (with the git remotes), the turn-end report, the read-only scope of model-side socket peers, the gate of full mode (full.rs: whether efrd allows or refuses it, the count of running full work) and the config hold (loosens.rs, reload.rs), and the network proxy's socket for each call with its lifetime, its host list and the hosts that a token in a project file denies; the bus filter: an xdg-dbus-proxy for each filtered bus of a call or a job, with its self-test and the refusals of its log; the writer of always-allow rules, which checks a rule and appends it to config.toml; the judge of auto exits (the reviewer's request and its chain of models), the presence of each conversation, the table of background jobs (the launch, the output file, the time limit, the stops, the sweep of a job's session and the port forwards of a contained job on 127.0.0.1), the home copies of the files that an approved exit, or an exit that full mode let through, writes outside the roots, the apply of an undo or a redo with the undo trash, and the deletes in the real tool caches; the search indexer (a background task with its text rules and its duty cycle) and the search methods with their time caps; the open pairings of new devices, in memory (pairing.rs), and the device methods; the heap trim (heap_trim.rs: one malloc_trim after the actors' idle trims), and its one unsafe module malloc.rs (ADR 0020) | every library crate above except the test crates |
efr-cli | bin efr | 4 | efr send, new, status, history, resume, conversation delete, diff, compact, context, jobs (list, stop), shell, settings, models, login (openai, openai-api, anthropic), logout, config (show, check, edit, set, unset, schema, reload), project (list, add, remove, through the daemon), paths, sandbox (check, explain, allow), undo, search, device (add with its QR code, list, revoke); the fzf picker of resume, inline below the command line; renders replies through efr-render | efr-transport, efr-config, efr-render, efr-protocol, efr-stdx |
efr-test-daemon | dev | T | TestDaemon and scenario replay; used only from tests/ of efr-daemon and efr-cli | efr-daemon, efr-test-support, efr-transport, efr-protocol |
None of these crates exists in the first commit; they land in the order of the milestone 1 file map.
The dependency rule
Dependencies point down. The only edges inside one tier are efr-tools -> efr-shell
and efr-daemon -> (everything).
cargo xtask deps reads cargo metadata (all features) and fails when:
- a member depends on a workspace crate that is not on its allowlist, or a member has no allowlist entry;
- one of these edges exists, directly or through any chain of normal dependencies:
efr-tools -> efr-permissions,efr-providers -> efr-oauth-openai,efr-conversation -> efr-shell,efr-conversation -> efr-transport,efr-transport -> efr-store,efr-protocol -> tokio,efr-test-support -> efr-daemon,efr-sandbox -> tokio,efr-sbx -> tokio,efr-proxy -> efr-daemon; - any member other than
efr-screen-ghosttyreacheslibghostty-vt, or any member other thanefr-storereachesrusqlite, except through that owner; efr-test-daemonis a dev-dependency of anything exceptefr-daemonandefr-cli, or anything hasefr-daemonas a direct dev-dependency.
CI runs the same rules a second time with literal cargo tree commands, so the two
implementations check each other. A new edge is one line in xtask/src/deps.rs plus a
sentence in the crate's README.
What each forbidden edge protects:
- The permission check happens in exactly one place,
efr-conversation/src/turn.rs, so tools cannot ask for or bypass it. - The model providers never see a refresh token.
- The conversation reaches shells only through
ShellTool. - The engine does not know about transports, and the transport does not touch the database.
- The protocol crate stays free of a runtime, so any client can compile it.
- The sandbox logic and the launcher stay free of a runtime: the launcher is a small process that runs as a foreground job of the hidden shell, and every rule it applies is a pure function that tests run without a kernel.
- The proxy is a library that efrd composes. It gets the host list, the call's grants and its seams from efrd, and knows nothing of the daemon.
- The patch engine stays pure: it computes every new content from texts that the
caller read, so a patch over several files is checked whole before the tool writes
one of them. The engine is the module
efr_tools::patch, so no crate edge can guard it;cargo xtask tidydoes (PURE_MODULESinxtask/src/tidy.rs). - Zig is a build requirement of one crate, and SQLite has one owner.
The test for splitting a module into a crate, or folding one back: it gets heavy dependencies of its own, or a second binary needs it.
The project's git dir and undo
- A snapshot never writes a project's git dir. git runs on a project's own git dir
only through
ProjectGit(crates/efr-snapshot/src/project_git.rs), with hooks, fsmonitor and automatic gc off, and only on the git dirs that efrd trusts: the work tree record ofcrates/efr-daemon/src/sandbox/projects.rswhen it matches, else<root>/.gitwhen it is a directory. The store pins both dirs by their(dev, ino)and never follows a.gitfile in a root. A repository with alternates, or with acommondirfile where none belongs, gets no mirror and no undo of its refs. - Only an applied undo writes a project's git dir, and only its refs (compare and swap) and the objects that the project lost, while efrd holds the plan lock of the root. It never writes the project's config, its hooks or its index.
efr-snapshotplans an undo and writes no file in a root or in the home directory. efrd writes them (crates/efr-daemon/src/tools/undo.rs): throughDaemonFs, never through a link, after it reads each path again and compares it with the plan. Each file that undo overwrites or removes goes to the undo trash ($XDG_DATA_HOME/efr/undo-trash/<undo-id>/) first.
Threading model
- One tokio runtime per binary, started in its
main:efrd's is multi-thread with one worker per core and at most four (efr-daemon/src/runtime.rs), because its work is waiting and the screens and the store writer have threads of their own;efr's is current-thread (efr-cli/src/run.rs). Library crates never create a runtime or callblock_on. - State belongs to actors: one actor per conversation, one
StoreWriter, oneShellSessions, oneScreenActorper screen. Actors talk over boundedmpscchannels (default capacity 64) withoneshotreplies; a handle type is the only public API of an actor. - No lock is held across an
.await(clippy denies it). - Every
Screenruns on its own named std thread. libghostty-vt types are!Send + !Syncand run callbacks synchronously on the calling thread, so a screen cannot live in a tokio task.ScreenActor::spawnstarts the thread and builds the screen inside it from aSendfactory. Commands go in over a boundedstd::sync::mpsc::sync_channel(64); replies to terminal queries (DA, DSR, DECRQM, OSC 10/11) and shell marks come out asScreenEvents on a tokio channel to the shell's async PTY writer. Only the daemon answers terminal queries. - vt100 screens run through the same actor, so the conformance suite tests the same code for both backends.
- Cost: N+1 screen threads for N open conversations, each with a 512 KiB stack. A
single shared screen thread was rejected because one slow
vt_writewould stall every conversation. A command whose output moves the cursor also gets a capture screen (replay-<conversation short id>) that replays the output and is shut down as soon as it is read, so a finished run keeps no thread. While it runs, its live tail is read the same way on a screen of the shell's size (tail-<conversation short id>), at most once perShellConfig::tail_interval(200 ms, fixed; the default ofconversation.update_interval_msis the same, but the key does not change it). - Non-goals: no
Mutex<Terminal>, nospawn_blockingwith aTerminal, noLocalSetinside the main runtime, noScreenbuilt outside its actor thread. - Blocking work (SQLite reads, file hashing) runs in
spawn_blocking. Time and randomness are injected throughClockandRng. - The daemon's settings live in a
watchofArc<Settings>(efr-config), and the permission engine in another. A reader takes the latest value when its unit of work starts and keeps it: a turn when it starts, a prompt when it arrives, a tool call for the engine. A running turn never changes its settings. One reload task (efr-daemon/src/reload.rs) sends new values: the file watcher (the daemon's own, on inotify through rustix), SIGHUP andadmin.config_reloadask it, so reloads never interleave. A file with an error changes nothing, and the keys that need a restart keep their running values. - Every fan-out has a bounded queue per consumer. Overflow closes that consumer with
Overflow { last_seq }; it never slows the producer. Drafts of a running turn are the one lossy item: they have a small room of their own in that queue, and a full room never closes the consumer. The subscription holds the drafts that do not fit, merged per part, and sends them at the next free place.
Startup and restart order
At milestone 1 there is one unit, efrd.service (Type=notify, Restart=always,
RestartSec=5, OOMPolicy=continue, Delegate=yes, and ExecReload sending SIGHUP,
which reloads the config). The hidden shells are children of the daemon, so they end
when the daemon stops. efrd starts in this order (the roots come from
EFR_<ROOT>_DIR, else $EFR_HOME/<root>, else XDG; the socket path is checked first):
- Take the exclusive
flockon$XDG_DATA_HOME/efr/daemon.lock; exit if another daemon holds it. The lock, notdaemon.json, decides single instance. Then, when the unit's cgroup is delegated, move into<unit>/daemonbefore any process starts, so each background job can get a cgroup of its own below<unit>/jobs(ADR 0021). - Load the config with
efr-config(defaults, thenconfig.toml, thenEFR_*variables, then flags). - Copy the database to
backups/efr.sqlite.<user_version>, then run the forward-only migrations. - Reconcile: mark in-flight turns cancelled, expire pending approvals as not
resumable, record queued prompts as not run (
turn_cancelled) with a notice to their terminal to send them again, cancel process-bound outbox rows. Nothing continues automatically. - Start the store writer, providers, the tool registry and the shell sessions; copy
the sandbox launcher to
$XDG_RUNTIME_DIR/efr/bin/efr-sbxand run the sandbox probe, whose status decides whetherautoturns run asautoor ascautious. - Open the Unix socket (0600) and write
daemon.jsonfor discovery. - Send
READY=1to systemd. - With
[remote] enabled, start the tailnet listener as a task. It reads the status of tailscaled, binds when Tailscale is usable, addstailnet_endpointtodaemon.json, and binds again when the address or the tailnet changes. A tailscaled that is down only delays the listener; efrd serves the Unix socket without it.
Clients that connect during a restart get a refused connection and retry; writes carry
a command_id, so a retried write returns the stored receipt instead of running twice.
From milestone 5, a second unit, efr-ptyd.service, holds the PTY masters and the
shell processes. It starts before efrd and survives efrd restarts; on start,
efrd reattaches the surviving PTYs and replays recording tails into fresh screen
actors. efr-ptyd itself restarts through a handoff manifest (validated, restored,
committed, owned). The milestone 5 change removes efr-daemon's efr-pty dependency
and adds efr-daemon -> efr-pty to the forbidden edges.
Where to find things
- Wire types and the protocol version:
crates/efr-protocol/src/. - One file per protocol method in the daemon:
crates/efr-daemon/src/methods/, with the exhaustive scope match inmethods.rs. - The permission check point:
crates/efr-conversation/src/turn.rs. The permission modes, the built-in read-only commands, theautosandbox and its exits, config protection and how a command line is read:docs/permissions.md. - The settings tool, the model's only way to change
config.toml, after an approval with the diff:crates/efr-daemon/src/tools/settings_tool.rs. - What a call or a turn changed in files: the snapshot store in
crates/efr-snapshot/, which roots a call snapshots incrates/efr-daemon/src/tools/snapshot.rs, and the diff of a turn incrates/efr-daemon/src/methods/conversation_diff.rs. The groups of the changed files (generated, tests, docs, code) incrates/efr-cli/src/format/groups.rs, the groups of[diff.groups]and their globs incrates/efr-config/src/tables/diff.rs. - The reviewer of the
autoexits (the classifier): the approver of each exit (Approver) incrates/efr-permissions/src/exits.rs, and the words that name an exit and the one check of them (names_match) incrates/efr-permissions/src/exits/names.rs; every user message of a conversation, which the record keeps, incrates/efr-store/src/user_messages.rs, and the newest turns of them that that check reads (exit::naming_messages); the seamExitJudgeincrates/efr-conversation/src/judge.rs; who decides an exit (exit::route) and the allow rule and the naming rule (exit::effective) incrates/efr-conversation/src/exit.rs; the review and the denial pause incrates/efr-conversation/src/turn/review.rs. efrd's judge, with the request, the chain of models and the check of the answer, is incrates/efr-daemon/src/judge.rs. What it keeps between exits is injudge/memory.rs, and its prompt is injudge/prompt.md. The small model of each company isVendor::classifier_modelincrates/efr-daemon/src/catalog/list.rs, from theCLASSIFIER_MODELconstant of each provider crate.[classifier]is incrates/efr-config/src/tables/classifier.rs. The eval of the corpus lines and of the auto prompt is incrates/efr-daemon/tests/it/classifier_eval.rs. - Presence and the away wait: the seam
PresenceReaderincrates/efr-conversation/src/presence.rs; the wait of a question while the user is away incrates/efr-conversation/src/turn.rs; the state of each conversation incrates/efr-daemon/src/presence.rs;presence.setincrates/efr-daemon/src/methods/presence_set.rs;efr awayincrates/efr-cli/src/commands/away.rs;[presence]incrates/efr-config/src/tables/presence.rs. - Undo and the home store: the store's refs, the mirror (
mirror.rs), git on the project's own git dir (project_git.rs), the check that a snapshot covers a target (cover.rs), the plan of an undo or a redo (undo.rs) and the home store (home.rs) incrates/efr-snapshot/src/. What a destructive line or a file tool asks to be covered (CoverAsk) incrates/efr-permissions/src/request.rs; which exits get a home copy (ExitNeed::copies,ExitNeed::backup_before_write) and the rule that a covered destructive part is routine (destructive) incrates/efr-permissions/src/exits.rs. efrd's answer to the asks before the decision incrates/efr-daemon/src/tools/snapshot.rs(CallSnapshots::covers) andcrates/efr-daemon/src/home.rs(coverage), from the facts of a call (DaemonToolbox::facts) incrates/efr-daemon/src/tools.rs; the home copies before and after a call incrates/efr-daemon/src/home.rs, from the one hook before a call runs (DaemonToolbox::invoke); the end of a turn (TurnUndoInfo) incrates/efr-daemon/src/tools/snapshot.rs; the apply of an undo and the undo trash incrates/efr-daemon/src/tools/undo.rs, throughDaemonFsincrates/efr-daemon/src/sandbox/fs.rs;turn.undoincrates/efr-daemon/src/methods/turn_undo.rs; the actor's undo and its refusals incrates/efr-conversation/src/actor/undo.rs;home_file_changedincrates/efr-conversation/src/turn.rs;efr undoincrates/efr-cli/src/commands/undo.rs; the lines at the end of a turn incrates/efr-cli/src/format/changes.rsandcrates/efr-cli/src/format/sandbox.rs. Named persistence that is no file edit: the routes of a named unit, cron or linger change incrates/efr-permissions/src/exits/persist.rs; the links that a unit change makes incrates/efr-daemon/src/sandbox/units.rs; the copies of the crontab and the linger setting incrates/efr-daemon/src/home/state.rs; thecrontabandloginctlthat efrd runs itself incrates/efr-daemon/src/trusted_programs.rs. The collector of the phase A backups, which efrd no longer writes, is incrates/efr-daemon/src/backups.rs. The tests from the prompt to the files are incrates/efr-daemon/tests/it/undo.rsandcrates/efr-daemon/tests/it/sandbox/undo.rs. - Deletes that reach the real tool caches: the walk of the whiteouts in an upper layer
(
CacheDeletes) and the read path of a layer (LayerView) incrates/efr-sandbox/src/layers/deletes.rs; the launcher's reads of the uppers that the layer helper passed incrates/efr-sbx/src/deletes.rs; the engine's decision (Engine::decide_cache_delete) incrates/efr-permissions/src/engine.rs; the question incrates/efr-conversation/src/turn/cache.rs; efrd's check and delete incrates/efr-daemon/src/tools/cache_deletes.rs, throughDaemonFs::remove_beneathincrates/efr-daemon/src/sandbox/fs.rs. - Background jobs (ADR 0010): the wire types in
crates/efr-protocol/src/jobs.rs;[jobs]incrates/efr-config/src/tables/jobs.rs; thejobsprojection, the notices that no turn took and the wake turns of the last hour incrates/efr-store/src/jobs.rs; the kinds that cannot run as a job (job_refusal) incrates/efr-permissions/src/jobs.rs; the launcher's job mode incrates/efr-sbx/src/job.rsandguard-checkincrates/efr-sbx/src/guard_check.rs; theenvfile of the hidden shell incrates/efr-shell/assets/zsh/efr-integration.zsh. In efrd: the job table incrates/efr-daemon/src/jobs.rs, the spawn with the lifeline injobs/launch.rs, the output file injobs/output.rs, the sweep by session id injobs/sweep.rs, the cgroup of each job below efrd's delegated cgroup (ADR 0021) injobs/cgroup.rs, the guard of a running job injobs/guard.rs, the jobs that keep an undo from running injobs/undo.rs, the programs that a job wrote injobs/since.rs, the daemon-wide window of wake turns injobs/wakes.rsand the removal of old job outputs injobs/retention.rs; a job's writes join its start turn throughSnapshots::after_jobincrates/efr-snapshot/src/job.rs; the start of a job and the toolsjobsandjob_stopincrates/efr-daemon/src/tools/job_tools.rs; the running jobs of the hidden shell (thejobsbuiltin) incrates/efr-daemon/src/tools/shell_children.rs;jobs.listandjobs.stopincrates/efr-daemon/src/methods/jobs_list.rsandjobs_stop.rs. In the actor: the text of a notice incrates/efr-conversation/src/notice.rs, the wake turns and their limits incrates/efr-conversation/src/wake.rsandcrates/efr-conversation/src/actor/notices.rs.efr jobsincrates/efr-cli/src/commands/jobs.rs, and the lines of the view and ofefr historyincrates/efr-cli/src/format/jobs.rs. The fake launcher of the tests iscrates/efr-test-daemon/src/fake_launcher.rs. - Job port forwards (ADR 0016). Trust: efrd decodes bytes that come from the sandbox,
so the frames have a fixed size and a hand decoder with property tests, and every
other byte is copied opaque. The pure rules (the parse of the TCP tables, the
loopback listens,
refusalwith the service ports,Debounce,peer_uid,v6_holds) and the frames incrates/efr-sandbox/src/forward.rs; the job'sForwardPlanincrates/efr-sandbox/src/spec.rs, the read-only bind of the socket incrates/efr-sandbox/src/plan/build.rs, the forwarder's Landlock domain and seccomp profile incrates/efr-sandbox/src/landlock.rsandseccomp.rs;[jobs] forward_portsincrates/efr-config/src/tables/jobs.rs; the eventjob_port_forwardedandJobInfo::forwardsincrates/efr-protocol/src/event.rsandjobs.rs. In the launcher: the forwarder incrates/efr-sbx/src/forward.rs, the start and setup that it shares with the bridge incrates/efr-sbx/src/helper.rs, its start incrates/efr-sbx/src/inner.rs, the stop that spares it incrates/efr-sbx/src/job.rs, and the test serverself-test --echoincrates/efr-sbx/src/self_test/echo.rs. In efrd: the plan of a job, the table of held ports and the lines incrates/efr-daemon/src/jobs/forward.rs, the control connection, the checks, the host listeners, the peer check and the copy injobs/forward/serve.rs, the machine's ports injobs/forward/host.rsbehind the seamHostPortsofcrates/efr-daemon/src/sandbox/seams.rs, the socket's bind incrates/efr-daemon/src/sandbox/proxy/bind.rs, the start note incrates/efr-daemon/src/tools/job_tools.rs, the stop at a reload incrates/efr-daemon/src/reload.rsand the key that loosens incrates/efr-daemon/src/loosens.rs. The projection incrates/efr-store/src/jobs.rs; the lines of the view and ofefr jobsincrates/efr-cli/src/format/jobs.rsandcrates/efr-cli/src/commands/jobs.rs. The tests of the forwarder alone are incrates/efr-sbx/tests/it/forward.rs, of efrd alone with the fake forwarder ofcrates/efr-test-daemon/src/fake_forwarder.rsincrates/efr-daemon/tests/it/jobs_forward.rs, and of both on the real launcher incrates/efr-daemon/tests/it/sandbox/forward.rs. - The reach of a contained call (ADR 0017): a contained call reaches the forwarded
ports of its conversation's jobs. The frames
Reach,Unreach,BoundandConnectincrates/efr-sandbox/src/forward.rs;ReachPlan,check_reachand the socket's paths incrates/efr-sandbox/src/spec.rs, its read-only bind incrates/efr-sandbox/src/plan/build.rs, the helper's Landlock domain and seccomp profile incrates/efr-sandbox/src/landlock.rsandseccomp.rs, andInnerReachincrates/efr-sandbox/src/inner.rs. In the launcher: the helper incrates/efr-sbx/src/reach.rs, its start incrates/efr-sbx/src/inner.rs, and the test clientself-test --dialincrates/efr-sbx/src/self_test/dial.rs. In efrd: the plan of a call, the control connection, the routes and the fact of a line incrates/efr-daemon/src/jobs/forward/reach.rs, the routes and the watch of the ports injobs/forward.rs, the copy into the job (Ctx::reach) injobs/forward/serve.rs. The factCallFacts::reach_portsincrates/efr-permissions/src/request.rs, and its use inexits/programs.rs,exits.rsandexits/needs.rs. The tests of the helper alone are incrates/efr-sbx/tests/it/reach.rs, of efrd alone with the fake helper ofcrates/efr-test-daemon/src/fake_reach.rsincrates/efr-daemon/tests/it/reach.rs, and of both on the real launcher incrates/efr-daemon/tests/it/sandbox/reach.rs. - A busy hidden shell (ADR 0017): a command that still runs in the hidden shell after
its call answered. The soft flag of a line that may ask for a password through ssh
in
crates/efr-tools/src/shell_tool/prompts.rs,Requirements::may_promptincrates/efr-permissions/src/request.rs, andCallContext::may_waitandapproved_byat the check point incrates/efr-conversation/src/turn.rs. In efr-shell: the run past its timeout while a person can answer (extension) and the stop of a wait for a secret (look_for_input) incrates/efr-shell/src/sessions.rs, the command left running, its watch, its limit andFollowersincrates/efr-shell/src/orphan.rs, the answers and their refusals incrates/efr-shell/src/session.rs, andlooks_secretincrates/efr-shell/src/input.rs. The events incrates/efr-protocol/src/event.rs, andshell.statusandshell.interruptincrates/efr-protocol/src/methods/. In efrd: the events from the shell's notices incrates/efr-daemon/src/shells/observer.rs,Followersover the connections incrates/efr-daemon/src/shells.rs, the notices incrates/efr-daemon/src/notices.rs(shell_line), the methods inmethods/shell_status.rsandmethods/shell_interrupt.rs, and the limits, the approver and the model's text of a held shell (not_ready_textofcrates/efr-tools/src/shell_tool.rs) incrates/efr-daemon/src/tools.rs. The line of the preamble incrates/efr-conversation/src/preamble.rs. In efr-cli: the view's lines and its answer line incrates/efr-cli/src/follow/view/left.rs, the words incrates/efr-cli/src/format/hidden_shell.rs, andefr shellincrates/efr-cli/src/commands/shell.rs;,shellinshell/zsh/efr.plugin.zsh. The tests over aTestDaemonare incrates/efr-daemon/tests/it/input_respond/left.rs, and on the real launcher incrates/efr-daemon/tests/it/sandbox/busy.rs. - Moving a call to the background (ADR 0018): Ctrl+B moves the agent's running
shellcall into a background job. The methodcall.backgroundincrates/efr-protocol/src/methods/call_background.rs, andJobInfo::moved_from(MovedCall) incrates/efr-protocol/src/jobs.rs;MOVED_FILEandRuntimePaths::moved_fileincrates/efr-sandbox/src/spec.rs, and the launcher's end of a moved call (call::moved) incrates/efr-sbx/src/call.rsandcrates/efr-sbx/src/exit_child.rs. In efr-shell:request_move,request_left_move(a command left running in the hidden shell, v0.0.5 OQS16) and the checks of the hand-over incrates/efr-shell/src/sessions.rsandsession.rs, the adopted run (AdoptedRun, its tee and its looks) incrates/efr-shell/src/adopt.rs, and the streaming cleaner of its output incrates/efr-shell/src/capture/stream.rs. The model's textjob_moved_textincrates/efr-tools/src/shell_tool.rs. In efrd: the registrations, the checks, the previews and the confirm incrates/efr-daemon/src/jobs/moves.rs, the tool path's side incrates/efr-daemon/src/tools/moves.rs, the handler incrates/efr-daemon/src/methods/call_background.rs, the job of a moved call (JobTable::adopt,JobProcess::Adopted) incrates/efr-daemon/src/jobs.rs, the kill of every process below the old shell (kill_descendants) incrates/efr-daemon/src/jobs/sweep.rs, and the adopted PTY (Ptys::adopt) incrates/efr-daemon/src/ptys.rs. In efr-cli: the key incrates/efr-cli/src/row.rs, the request and the question (background_key) incrates/efr-cli/src/follow.rs, the card and the block of a moved call incrates/efr-cli/src/follow/view.rsandfollow/view/call.rs, and the lines of a moved job incrates/efr-cli/src/format/jobs.rs. The tests of the hand-over are incrates/efr-shell/src/adopt/tests.rs, of the launcher's end incrates/efr-sbx/tests/it/moved.rs, of efrd over a real zsh incrates/efr-daemon/tests/it/jobs_moved.rs, of the key against a fake daemon incrates/efr-cli/src/follow/tests/row.rs, and on the real launcher incrates/efr-daemon/tests/it/sandbox/moved.rs. - Conversation search (ADR 0019): the tables in
crates/efr-store/src/migrations/0009_search.sql; the writes, the pending read and the reads incrates/efr-store/src/search.rs, with the pure parts incrates/efr-store/src/search/(query.rs,fuzzy.rs,rank.rs,snippet.rs,marks.rs) and the lanes and the stop inlane.rsandstop.rs; the index redaction incrates/efr-conversation/src/index_text.rsovercrates/efr-conversation/src/preamble/secrets.rs. In efrd: the indexer incrates/efr-daemon/src/search_index.rsand its text rules insearch_index/text.rs, the methods incrates/efr-daemon/src/methods/search.rs,conversations_search.rsandconversation_search.rs. In efr-cli:efr searchincrates/efr-cli/src/commands/search.rsandcrates/efr-cli/src/format/search.rs, the picker incrates/efr-cli/src/picker.rsandpicker/fzf.rs, and its use incrates/efr-cli/src/commands/resume.rs;,resume <words>inshell/zsh/efr.plugin.zsh. The benches are incrates/efr-store/tests/it/search_bench.rs,crates/efr-daemon/tests/it/search_index.rsandcrates/efr-cli/tests/it/search_cost.rs(just bench-search); the tests with the real fzf incrates/efr-cli/tests/it/picker.rs(just test-fzf). - Conversation delete: the method in
crates/efr-protocol/src/methods/conversation_delete.rsand the eventconversation_deleted; the tombstone, its steps and the trigger incrates/efr-store/src/deletes.rsandcrates/efr-store/src/migrations/0010_deletes.sql, the guard of the search index incrates/efr-store/src/search.rs; in efrd, the handler incrates/efr-daemon/src/methods/conversation_delete.rs, the steps after the tombstone and the end of an open delete at the start incrates/efr-daemon/src/deletes.rs, the stop of the jobs (JobTable::stop_conversation) incrates/efr-daemon/src/jobs.rs, the end of the recordings (StoreRecording::forget) incrates/efr-daemon/src/shells/recording.rs; the snapshot refs (Snapshots::forget_conversation) incrates/efr-snapshot/src/gc.rs; the scratch dirs (scratch_dirs) incrates/efr-conversation/src/scratch.rs;efr conversation deleteincrates/efr-cli/src/commands/conversation.rs, its lines incrates/efr-cli/src/format/delete.rs, and Ctrl-D of the picker incrates/efr-cli/src/commands/resume.rs. The tests over aTestDaemonare incrates/efr-daemon/tests/it/conversation_delete.rsandjobs.rs, the store's incrates/efr-store/src/deletes/tests.rs, and the real fzf's incrates/efr-cli/tests/it/picker.rs. - Enrolled devices (the remote milestone): the table in
crates/efr-store/src/migrations/0011_devices.sql; its rows, the checks of their shape and the reads incrates/efr-store/src/devices.rs; the insert, the revoke with its feed (WriterHandle::subscribe_revocations) and the time of the last proof incrates/efr-store/src/writer.rs. - The tailnet listener of the phone (ADR 0022): the listener, its rebinds and the
connection cap in
crates/efr-daemon/src/remote/listener.rs; the view of tailscaled's status and IPN bus inremote/tailnet.rs; the whois admission inremote/admission.rs;device.proveinremote/prove.rs; the close of a revoked device's sockets inremote/live.rs; the fake LocalAPI of the tests inremote/fake.rs. The LocalAPI client is incrates/efr-http/src/localapi.rs; the WebSocket handshake, frames and pings incrates/efr-transport/src/ws.rsandws/; the pairings incrates/efr-daemon/src/pairing.rs, with the device methods incrates/efr-daemon/src/methods/device_*.rs; andefr deviceincrates/efr-cli/src/commands/device.rs. The phone app is inapp/(app/README.md). - The OSC 133 and OSC 7 scanner:
crates/efr-screen/src/shell_marks/. - Freeform tools, whose input is text: the definition in
crates/efr-provider/src/request.rs, which models take the freeform form incrates/efr-providers/src/openai/catalog.rs(ModelInfo::freeform_toolsfrom the catalog'sapply_patch_tool_type), thecustomitems incrates/efr-providers/src/openai/convert.rs. The patch engine ofapply_patch:crates/efr-tools/src/patch/; the tool:crates/efr-tools/src/apply_patch.rsand its contract incrates/efr-tools/README.md; the question for each delete and move:Requirements::destructiveincrates/efr-permissions/src/engine.rs. - The
edittool of Claude models:crates/efr-tools/src/edit.rsonefr_tools::patch::replace. Which model getsapply_patchand whichedit:ModelInfo::edit_toolincrates/efr-provider/src/provider.rs(andProvider::default_edit_toolfor a model that the list does not name), read byturn::edit_toolincrates/efr-conversation/src/turn.rs; the list that offers one of the two and the refusal of the other incrates/efr-daemon/src/tools.rs. - The model's context window and its compaction: the contract in
crates/efr-conversation/README.md, section "Context"; the numbers and the estimate incrates/efr-conversation/src/context.rs; pruning, the cut, the summary request and its prompt (compaction/prompt.md) incrates/efr-conversation/src/compaction.rs; the fresh context block incrates/efr-conversation/src/fresh.rs; the guards and the compaction inside a turn incrates/efr-conversation/src/turn/compact.rs; the manual one incrates/efr-conversation/src/actor/compact.rs; the newest compactions incrates/efr-store/src/compactions.rs; the wire types incrates/efr-protocol/src/compaction.rs;[compaction]incrates/efr-config/src/tables/compaction.rs. What fills the context (conversation.context,efr context): the measure of each part incrates/efr-conversation/src/context/breakdown.rs, the actor's read incrates/efr-conversation/src/actor/context.rs, the method incrates/efr-daemon/src/methods/conversation_context.rs, the wire types incrates/efr-protocol/src/methods/conversation_context.rsand the lines incrates/efr-cli/src/format/context_report.rs. - The history that only grows, so each request starts with the request before it: the
contract in
crates/efr-conversation/README.md, section "The history only grows"; the list of turns and the byte limit incrates/efr-conversation/src/history.rs; the redaction of the last command in the kept preamble incrates/efr-conversation/src/preamble/secrets.rs; the terminal's recent commands in the preamble incrates/efr-conversation/src/preamble/recent.rs, from the plugin's ring inshell/zsh/efr.plugin.zshthroughEFR_RECENT_COMMANDSandprompt.sendrecent_commands; the measurement on the OpenAI path incrates/efr-daemon/tests/it/cache_prefix.rs. - The model catalog: the fetch, the cache file and which models are on offer in
crates/efr-providers/src/openai/catalog.rsandcrates/efr-providers/src/anthropic/catalog.rs; the built-in OpenAI table, the last fallback, incrates/efr-providers/src/openai/models.rs(Claude models have none); the catalog of either company and its cache file name incrates/efr-daemon/src/catalog/list.rs; when efrd fetches, the wait of a prompt for a first Claude list (Models::ready), the effective model list, the default model and the windows of[openai] modelsand[anthropic] modelsincrates/efr-daemon/src/catalog.rs; which provider efrd builds for[model] providerincrates/efr-daemon/src/providers.rs. - Logins with an API key: where
efrreads a key (a hidden prompt, stdin or--from-env) incrates/efr-cli/src/commands/login/key.rs; the method incrates/efr-daemon/src/methods/admin_login_api_key.rs; the checks of the key's text, its hint and the check with its provider incrates/efr-daemon/src/providers/api_key.rs; the requests of the check incrates/efr-providers/src/openai/catalog/client.rsandcrates/efr-providers/src/anthropic/catalog/client.rs(check_key); when the provider refused a stored key, foradmin.status, incrates/efr-daemon/src/providers/refusals.rs. The user's guide to the providers, their logins and the Claude settings:docs/providers.md. - The Responses WebSocket transport: the contract in
crates/efr-providers/README.md, section "WebSocket transport"; the connection of each conversation and the fallback to HTTP incrates/efr-providers/src/openai/websocket.rs; incremental input incrates/efr-providers/src/openai/websocket/continuation.rs; the WebSocket client incrates/efr-http/src/websocket.rs;[openai] websocketincrates/efr-config/src/tables.rs. - The Anthropic provider: the contract (the request, the prompt cache markers, the
catalog, the token counts and the failures) in
crates/efr-providers/README.md; the placement of the cache markers and their debug line incrates/efr-providers/src/anthropic/convert/breakpoints.rs, and the gap before each model call (gap_ms, for the measurement of the time to live) incrates/efr-conversation/src/gap.rs; which error answers are sent again incrates/efr-providers/src/anthropic/failure.rs; the fake Messages API and model list for daemon tests,MessagesServer, incrates/efr-test-daemon/src/test_daemon/messages.rs(TestDaemonBuilder::messagesruns the real provider against it), and a whole Claude conversation over it incrates/efr-daemon/tests/it/anthropic.rs; the canonical token counts of every provider incrates/efr-provider/README.md, section "Token counts". - The views of the host (ADR 0011, phase D1). The network view:
Grant::NetViewandSandboxStatus::netviewincrates/efr-protocol/src/sandbox.rsandsandbox/status.rs;[network]incrates/efr-config/src/tables/network.rs; the line rule incrates/efr-permissions/src/exits/view.rs, the view reason (Subject::View) incrates/efr-permissions/src/decision.rsand its place in the engine incrates/efr-permissions/src/engine.rs; the union of view and exit grants (exit::grant) incrates/efr-conversation/src/exit.rs; the host namespace and the profile incrates/efr-sandbox/src/plan/build.rsandcrates/efr-sandbox/src/seccomp.rs(SeccompProfile::net_view), the Landlock TCP rights incrates/efr-sandbox/src/landlock.rs; the launcher's compile of the netlink rule incrates/efr-sbx/src/seccomp.rs, its self-test incrates/efr-sbx/src/self_test.rsand the probe's second launch incrates/efr-sbx/src/probe.rs; efrd's engine after each probe (Probed) incrates/efr-daemon/src/engine.rsandcrates/efr-daemon/src/reload.rs. The mount view: the parse, the rules of the sizes and the renderers incrates/efr-sandbox/src/mounts.rs;efr-sbx statvfsandefr-sbx mountsincrates/efr-sbx/src/statvfs.rsandcrates/efr-sbx/src/mounts.rs; efrd's snapshot with its deadline and the check of its mount namespace incrates/efr-daemon/src/sandbox/mounts.rs; thedfandfindmntfunctions of the child shell incrates/efr-shell/assets/zsh/efr-child.zsh. The tests on the real launcher are incrates/efr-sbx/tests/it/netview.rs,mount_view.rsandcrates/efr-daemon/tests/it/sandbox/netview.rs,mount_view.rs; the eval of the view prompts incrates/efr-daemon/tests/it/classifier_eval.rs. - The network proxy (ADR 0012, phase D2). Trust: efrd parses bytes that come from the
sandbox here, in an unconfined process, so
efr-proxyparses only the request head (crates/efr-proxy/src/head.rs) and the TLS ClientHello (crates/efr-proxy/src/hello.rs) with hand parsers, hard caps and proptests, and forbidsunsafe; after the checks it copies opaque bytes. The host list, the presets, the pacman mirror parser and the token scanners are pure, incrates/efr-permissions/src/network.rsandnetwork/tokens.rs; the config keys incrates/efr-config/src/tables/network.rs. The proxy itself: the listener of a call with its budget and records incrates/efr-proxy/src/call.rs, the order of the checks of aCONNECTincrates/efr-proxy/src/tunnel.rs, plain HTTP incrates/efr-proxy/src/plain.rs, the lookup and the connect incrates/efr-proxy/src/upstream.rs, the address guard incrates/efr-proxy/src/guard.rs, and the system resolver, connector and addresses incrates/efr-proxy/src/system.rs(the only place that resolves or connects). In efrd: the proxy service, the bind of each call's socket, its lifetime and the refusals incrates/efr-daemon/src/sandbox/proxy.rs(withproxy/bind.rs,proxy/mirrors.rsandproxy/tokens.rs), the network plan of a call incrates/efr-daemon/src/sandbox/plan.rs, the egress of the engine incrates/efr-daemon/src/engine.rs, the git remote facts incrates/efr-daemon/src/sandbox/facts/remotes.rs, and the test seams of the proxy incrates/efr-daemon/src/sandbox/seams.rs. In the launcher: the read-only bind of the socket and the proxy variables incrates/efr-sandbox/src/plan/build.rsandcrates/efr-sandbox/src/plan.rs, the bridge's Landlock domain and seccomp profile incrates/efr-sandbox/src/landlock.rsandcrates/efr-sandbox/src/seccomp.rs, the bridge incrates/efr-sbx/src/bridge.rs, its start incrates/efr-sbx/src/inner.rsand its self-test incrates/efr-sbx/src/self_test.rs. The engine's rules under the proxy are incrates/efr-permissions/src/exits/programs.rsandcrates/efr-permissions/src/exits/needs.rs, and the naming rule with the allow list,project_remoteandrefused_by_proxyincrates/efr-conversation/src/exit.rs. The tests on the real launcher are incrates/efr-sbx/tests/it/proxy.rsandcrates/efr-daemon/tests/it/sandbox/proxy.rs. - The bus filter and always-allow rules (ADR 0013, phase E). The bus filter: the
built-in read methods, the checks of
[[dbus.allow]]and the proxy's arguments incrates/efr-permissions/src/bus.rs, with the recorded calls incrates/efr-permissions/tests/fixtures/bus-recording.toml; the read forms and the view incrates/efr-permissions/src/exits/programs.rsandexits/view.rs; the keys incrates/efr-config/src/tables/dbus.rs; the plan step with the/run/dbustmpfs andRESOLVE_UNIXincrates/efr-sandbox/src/plan/build.rs, the wrapper's Landlock domain and seccomp profile incrates/efr-sandbox/src/landlock.rsandseccomp.rs; the wrapper incrates/efr-sbx/src/dbus_proxy.rsand its self-test incrates/efr-sbx/src/self_test/bus_proxy.rs; thesystemctlfunction of the child shell incrates/efr-shell/assets/zsh/efr-child.zsh; efrd's supervisor, its self-test and its status incrates/efr-daemon/src/sandbox/bus.rs, the log parser incrates/efr-daemon/src/sandbox/bus/log.rs, the trusted program incrates/efr-daemon/src/trusted_programs.rsand the seams of the test buses incrates/efr-daemon/src/sandbox/seams.rs; the model's note incrates/efr-tools/src/shell_tool.rs(bus_note). Always-allow: the rule type, its floor checks and its match incrates/efr-permissions/src/always.rs,always/check.rsandalways/cover.rs; the key incrates/efr-config/src/tables/sandbox.rsand the writer'sadd_table_entryincrates/efr-config/src/writer.rs; the check in the turn incrates/efr-conversation/src/turn/always.rs; efrd's writer incrates/efr-daemon/src/always.rs, the reload incrates/efr-daemon/src/reload.rsand the real-path facts incrates/efr-daemon/src/sandbox/facts.rs; the card andefr sandbox allowincrates/efr-cli. The tests on the real launcher and the real proxy are incrates/efr-sbx/tests/it/dbus.rs,crates/efr-daemon/tests/it/sandbox/dbus.rs(privatedbus-daemonbuses with the fake services ofdbus/fake_bus.py) andcrates/efr-daemon/tests/it/sandbox/always.rs; the turn-level tests with a fake proxy incrates/efr-daemon/tests/it/bus.rs; the eval of the bus prompts incrates/efr-daemon/tests/it/classifier_eval.rs. - Full mode (ADR 0015): the user guide in
docs/permissions.md, section "Full mode"; the settle pass of a full call incrates/efr-permissions/src/full.rsand its place inEngine::decide(crates/efr-permissions/src/engine.rs); the support of a machine with no sandbox (AutoSupport::for_full) incrates/efr-permissions/src/support.rs; the delete guard incrates/efr-permissions/src/exits/critical.rs, with its types incrates/efr-permissions/src/critical.rs; the gate keypermissions.allow_fulland its load check incrates/efr-config/src/tables.rsandvalidate.rs; the gate at each resolve, the phone cap and the wake cap incrates/efr-conversation/src/settings.rs; the gate before each call, the record of what full mode let through and the timed question incrates/efr-conversation/src/turn/full.rs; the model's text incrates/efr-conversation/src/preamble.rs; efrd's refusals (root, undo off, no zsh) and the count of full work incrates/efr-daemon/src/full.rs; the config hold incrates/efr-daemon/src/reload.rswith the keys that loosen incrates/efr-daemon/src/loosens.rs; the forget key of sudo before a call incrates/efr-shell/src/session.rs; the marker incrates/efr-cli/src/follow/andcrates/efr-cli/src/format/sandbox.rs, and the plugin's tag inshell/zsh/efr.plugin.zsh. The tests over the socket are incrates/efr-daemon/tests/it/full.rsand, on the real launcher,crates/efr-daemon/tests/it/sandbox/full.rs. - On-disk layout and schema:
docs/storage.md. The libghostty pin:docs/ghostty-pin.md. - Decisions that are expensive to reverse:
docs/adr/.