# Operations What reading byre's code won't tell you: external setup, vendor and platform quirks seen live, and the intent behind rules. `api/openapi.yaml` is the API's source of truth; `go generate ./internal/api` regenerates `internal/api/api.gen.go`. ## Data The data directory must remain private to the service's OS user (0700): it contains transcripts, credentials and local agent listener bearer URLs. Other processes running as that same OS user remain trusted. Add SQL migrations in `internal/store/migrations/` as `NNN-name.sql`. The number only orders; a migration is identified by its name. A migration's SQL is fixed once applied anywhere: write it out in full rather than sharing Go constants that later change. Concurrent task branches can each add migrations, so after a merge a migration can land before others the database has already applied, and it still runs. Give a new migration a name no other branch will use, and make any data migration safe to run twice. `sqlite3 byre.db 'SELECT * FROM migrations'` lists what has been applied. Migrations up to a threshold are folded into `100-baseline.sql`, the schema as they left it, and no data conversion survives below it. A database that hasn't applied the migration `baselineThrough` names (in `internal/store/store.go`) is refused at startup as too old; one that has is marked as on the baseline. To move the threshold up, apply the migrations to be folded to an empty database, dump its schema into the baseline file in place of the old one, delete those migrations' files, and set `baselineThrough` to the last one's name. Tool output is most of the database, so an `agent.tool_result` event's `content` is kept zstd-compressed in the `events.content` column, outside `payload`. The store splits it out on write and puts it back on read, so nothing above the store sees the difference. It never goes in an index or a `json_extract`, and a `sqlite3` query on `payload` won't see it. At startup byre runs `VACUUM` when at least a quarter of the file's pages are free, so a migration that shrinks rows gives the space back. ## Worktrees byre runs nothing in a new worktree. Installing dependencies or copying untracked files such as `.env` is the project's own instruction in its AGENTS.md, carried out by the agent with its own tools. Closing a task deletes its branch, worktree and snapshot refs, so a task continued from a closed one (Send in a merged or discarded task, which becomes Continue) starts from the default branch: a merged task's work is there, a discarded task's is gone. Only the agent's conversation carries over, through the vendor's own session files, which outlive the worktree. ## Running as a service The service always runs from the app: `/Applications/Byre.app/Contents/Helpers/byre install`, which the app's Server tab and connection screen run. `byre install` takes `PATH` from the shell it is run in, since the service's `PATH` must find `claude`, `codex`, `agy`, `git`, `curl` and `npx`. When the service is already loaded, `install` hands `launchctl bootout` and `bootstrap` to a detached `sh` and exits: `bootout` kills every process in the service's process groups, which includes `byre install` itself when an agent byre runs is the one running it. `bootout` returns once it has sent SIGTERM, not once the server has exited, and a `bootstrap` while the old job is still tearing down fails with `Bootstrap failed: 5: Input/output error`, leaving no service at all; so the `sh` waits until `launchctl print` no longer finds the service before `bootstrap`, and writes its output to `~/Library/Logs/byre.log`. `byre serve` exits when its executable is replaced (logging `executable replaced, exiting`) and launchd starts the new one. A missing path counts as not yet replaced, so the moment in `install.sh` between removing the old bundle and moving the new one in doesn't trigger it. The service log is owner-only (0600), including existing logs: Tailscale can put a sign-in URL there. Logs go to `~/Library/Logs/byre.log`; `launchctl print gui/$(id -u)/run.fc.byre` shows the service. ## Slow requests An API request that takes 200 ms or more is logged on both ends. The server's line, `request slow`, in `byre.log`, has the handler's time and its spans: each git process, SQL begin, exec and query, the wait for the store's write lock, and the tailnet `whois`. Every response also carries these spans in a `Server-Timing` header. On the client, `log show --start