No description
  • Rust 99.6%
  • Nix 0.3%
Find a file
tsk 164f32518f
Some checks failed
CI / nix (push) Failing after 2s
fix: recover sessions from invalid context compactions
Replay empty or stale legacy compactions as audited no-ops while rejecting new
invalid compaction facts before append.

Summarize the prior compacted projection plus the new tail so compaction
requests stay within provider context windows. Record an empty summary as a
provider failure instead of poisoning the journal.
2026-09-03 12:49:50 +00:00
.github/workflows init commit 2026-09-01 21:41:01 +00:00
crates fix: recover sessions from invalid context compactions 2026-09-03 12:49:50 +00:00
docs chore: update git url 2026-09-02 13:06:35 +00:00
fuzz init commit 2026-09-01 21:41:01 +00:00
tests/fixtures fix: make approval waits responsive and prompt layout stable 2026-09-02 22:03:38 +00:00
.gitignore init commit 2026-09-01 21:41:01 +00:00
AGENTS.md init commit 2026-09-01 21:41:01 +00:00
Cargo.lock feat: add model-aware context compaction 2026-09-02 13:06:33 +00:00
Cargo.toml chore: update git url 2026-09-02 13:06:35 +00:00
CODE_STYLE.md init commit 2026-09-01 21:41:01 +00:00
DEVELOPMENT.md init commit 2026-09-01 21:41:01 +00:00
flake.lock init commit 2026-09-01 21:41:01 +00:00
flake.nix chore: update git url 2026-09-02 13:06:35 +00:00
justfile init commit 2026-09-01 21:41:01 +00:00
LICENSE init commit 2026-09-01 21:41:01 +00:00
README.md fix: make approval waits responsive and prompt layout stable 2026-09-02 22:03:38 +00:00
rust-toolchain.toml init commit 2026-09-01 21:41:01 +00:00
SECURITY.md init commit 2026-09-01 21:41:01 +00:00

Fite

CI License: MIT

A local-first coding agent whose conversations are signed, replayable event journals—not opaque application state.

Fite combines the Goose agent runtime with a Nostr-native persistence and transport layer. It provides a scrolling terminal interface, workspace tools, Agent Skills, OpenRouter/OpenAI/Ollama providers, MCP and ACP integration, and verified sharing and forking. The default experience is one command: Fite starts its embedded loopback daemon, creates a session, and opens the TUI.

Fite is alpha software. The event format and command-line interface may still change before 1.0. Use exports for important sessions and review tool approvals carefully.

What is novel about Fite?

Fite's distinctive idea is that an agent session is a verifiable protocol, not just a transcript stored by a UI.

Verifiable conversation state

Every durable session fact is signed by a session-scoped authority and linked to its predecessor. A deterministic fold reconstructs the conversation, provider lifecycle, tools, approvals, deliveries, and recovery state. SQLite indexes and TUI models are disposable projections; the signed event journal is the source of truth.

This makes history independently checkable, allows public sessions to be replayed without trusting Fite, and gives forks an exact parent event rather than a best-effort copy of rendered chat.

Side effects are part of the protocol

Provider requests, tool requests, approvals, starts, outcomes, cancellation, and delivery attempts are durable facts. After a crash, Fite never blindly repeats a provider or tool call merely because a final UI message is missing. An interrupted non-idempotent tool becomes tool.outcome-unknown, forcing an explicit recovery choice.

Forking does not replay the world

A fork copies only verified Goose conversation-effect batches into a fresh session with new reader and writer capabilities. It does not rerun models, tools, or external deliveries. Read-only bundles can be replayed and forked without granting authority to continue the original journal.

Privacy is fixed at session genesis

New sessions are public by default: their signed content is plaintext and can be replayed or forked by anyone holding the events. Private and shared sessions encrypt durable content to an independent session-reader key. Commands remain encrypted to the session authority in every mode. Visibility cannot silently change halfway through a conversation.

One event boundary for every interface

The TUI, CLI, ACP server, Buzz adapter, and sync path all use the same authenticated command/query boundary. No interface gets a privileged in-process agent handle or its own authoritative conversation model. The embedded relay binds only to loopback and requires NIP-42 authentication.

Quick start

Fite currently supports x86-64 and AArch64 Linux through Nix.

Run it directly from the Git repository:

nix run git+https://git.cashu.dev/thesimplekid/fite

Or build a checkout:

git clone https://git.cashu.dev/thesimplekid/fite
cd fite
nix build
./result/bin/fite

Bare fite starts a local daemon when needed, creates a public session, and opens the scrolling terminal chat. Type a message and press Enter. Use /help for session, provider, approval, cancellation, skill, and display commands; use /quit to exit. You do not need to run fite-daemon separately for normal interactive use.

The composer supports persistent prompt history, Tab completion for slash commands and workspace paths, Ctrl-J for multiline prompts, and Ctrl-E or /editor for $VISUAL/$EDITOR. /provider <PROFILE> selects an exact configured provider. Assistant Markdown and diff-like output receive lightweight terminal highlighting.

When a session is created, Fite discovers AGENTS.md files from the repository root through the selected working directory. Their exact contents and digests are captured in session genesis so replay uses the same instructions even if the working tree later changes. /instructions lists the captured files and /instructions full displays their content. Instruction snapshots follow the session visibility policy, so they are plaintext in public sessions.

Without provider configuration, Fite uses a deterministic local fake so the complete session and tool flow can be tested without an account.

For scripts and CI, fite run owns the complete daemon/session lifecycle and exits after one durable answer:

fite run "summarize this repository"
fite run --cwd ./project --provider openrouter --model MODEL_ID --format jsonl < prompt.txt

Each invocation creates a replayable session. --mode, --max-turns, --reasoning, --visibility, and --prompt-file provide per-run overrides; jsonl emits a session record followed by the completed answer as newline-delimited JSON.

Sessions can carry signed titles without changing their stable UUIDs. Use /rename TITLE, search with /sessions QUERY, and select an exact title or UUID prefix with /session MATCH. The CLI equivalents are fite rename and fite list --search. fite fork SESSION --head CANONICAL_HEAD (or /fork HEAD) branches from an exact verified point in the conversation; omitting the head forks the latest state.

Configure a model provider

fite models or /models lists the built-in fake and the default model for each locally configured profile without contacting a remote catalog. Select a model with /model MODEL, and set a provider-neutral signed reasoning level with /reasoning off|low|medium|high|max (CLI: fite reasoning SESSION LEVEL). The Goose provider layer maps that level to each supported model's request format.

Every completed provider response retains its reported token and cost fields in the canonical journal. /usage, fite usage SESSION, and fite status SESSION show cumulative input/output/total tokens, cost, and the most recent prompt's percentage of the resolved model context window.

To use the Codex models included with a ChatGPT subscription:

fite login codex
fite

You can instead run /login inside the TUI. Fite opens the ChatGPT browser flow and listens on 127.0.0.1:1455 for the callback. If the browser cannot reach that callback (for example, across SSH), copy its final redirect URL, paste it into the Fite prompt, and press Enter. /logout or fite logout codex removes the locally cached tokens. Login creates a local codex profile using chatgpt_codex and makes it the default.

OpenRouter is the simplest way to use models from multiple vendors:

fite config set-profile router \
  --provider openrouter \
  --model <OPENROUTER_MODEL_SLUG> \
  --credential
fite config set-default router
fite

API credentials are read from a secure prompt and written to the owner-only local provider registry. ChatGPT access and refresh tokens use a separate owner-only OAuth file and refresh automatically. No credential or token is ever placed in a Nostr event. OpenAI API-key profiles use --provider openai; for Ollama, use --provider ollama and omit --credential.

Provider profile selection and model name are canonical session configuration. Endpoints and secrets remain local. Environment variables are available as explicit CI/container overrides, not as the primary interactive configuration path. Run fite config to see the redacted effective configuration.

Agent turns default to at most 1000 consecutive tool-calling iterations. The limit is canonical session configuration, so it follows exported, replayed, and forked conversations:

fite config set-session-max-turns <SESSION_UUID> 100

Inside the TUI, /max-turns shows the effective value and /max-turns 100 changes it. Before the hard ceiling, Fite detects repeated identical calls, alternating call cycles, and consecutive tool failures. It gives the model one explicit chance to change course, then requests a tool-free final answer if the same loop continues. Reaching a guard produces a normal, replayable assistant response rather than failing the turn.

Fite automatically compacts provider context at 80% of the model context window or the configured message bound. /compact (or fite compact <SESSION_UUID>) requests it explicitly. The model-generated summary and its verified source head are journaled, while the complete conversation remains canonical and visible for replay or forks.

Coding tools and approvals

The model receives structured file tools scoped to the directory where its session was created, plus shell tools that use that directory as their working directory:

  • fite_read_file, fite_list_files, and fite_search_text are bounded, read-only operations.
  • fite_write_file and fite_edit_file make atomic changes after explicit approval.
  • fite_apply_patch validates and applies all-or-rollback batches of file additions, exact edits, deletions, and moves with a combined diff preview.
  • fite_shell runs Bash with the Fite process's environment and access, bounded output, and an approval gate.
  • fite_shell_start, fite_shell_poll, fite_shell_input, and fite_shell_cancel manage bounded background commands for builds, servers, and other long-running jobs.
  • fite_skill discovers Agent Skills by metadata and loads bodies only on demand.
  • fite_delegate gives a self-contained task to a child coding agent and returns its final answer. Passing tasks instead runs up to four independent child sessions concurrently and returns every signed child session ID.

When a provider requests multiple independent file reads, listings, or text searches in one response, Fite executes them concurrently. Request/start facts and terminal outcomes are still committed in provider order, keeping replay and projection hashes deterministic. Mutations, shell commands, approvals, and MCP calls remain serialized.

Fite records tool request, review, approval, start, and outcome facts in the session journal. The TUI clearly separates assistant text, tool activity, compact tool results, approval prompts, and final answers. Enter /approve, /approve turn, or /deny after reviewing a pending mutation. While automatic review is running, the status says reviewer checking approval; waiting for your approval means the next decision is yours. Reviewer failures and timeouts are named when control passes to you, and submitting /approve or /deny produces an immediate local acknowledgement while the signed decision is recorded. Active status also shows minute-bucket elapsed time and the latest context usage/window (for example, ctx 24k/200k) as a Tau-style right prompt. The editable input always begins at the same column, and the right prompt disappears rather than colliding with a long or multiline draft. Assistant text streams through signed ephemeral frames while the completed provider message remains the only durable response.

After upgrading Fite, restart both the daemon and terminal client from the same build. Runtime discovery rejects incompatible daemon/client generations before the client can misread newer canonical event types.

Each session has a signed chat, review, approve, or auto tool mode. review is the default: confined reads and structured workspace edits run directly, while shell, MCP, and delegation calls are reviewed or shown to the operator. Fite deliberately does not create a process, filesystem, or network sandbox. Shell commands inherit the Fite process's effective access; run Fite inside a container or sandbox at the launcher/deployment layer when confinement is required. approve asks the operator before every side effect, chat exposes no tools, and auto skips prompts. Use /mode, or configure the CLI explicitly:

fite config set-mode review
fite config set-reviewer openrouter --model MODEL_ID
fite config set-session-mode <SESSION_UUID> auto
fite config set-tool-permission fite_shell ask

The selected session mode is replayable Nostr state. Per-tool overrides and the default for new sessions stay owner-local in permissions.json. Reviewer credentials also remain owner-local. Every completed review is signed by a separate reviewer Nostr identity and embedded as verifiable evidence in the canonical approval history; the reviewer can recommend one exact call or escalate, but it never receives session-authority keys.

Delegation is Nostr-native rather than an invisible in-process prompt. Each child receives a distinct session authority and signed subagent relationship to the exact parent head, an isolated conversation, the parent's provider/tool/ MCP selections, and the same working directory. Its complete journal can be listed, inspected, replayed, shared, or forked like any other session. Nesting is capped at four, and a daemon needs at least one spare active-session slot.

For analysis before implementation, /plan on switches the current session to read-only plan mode. The model can inspect the workspace, maintain a structured plan with signed plan.updated facts, and pause for a bounded multiple-choice question. The TUI displays the choices and resumes after /answer TEXT; headless operators can use fite tools <SESSION_UUID> followed by fite answer <SESSION_UUID> <QUESTION_ID> <ANSWER>. /plan displays the latest folded plan and /plan off returns the session to review mode.

Project skills live at .agents/skills/<name>/SKILL.md or .agents.local/skills/<name>/SKILL.md. Use /skills and /skill <name> [arguments] interactively. See Agent Skills for discovery precedence, frontmatter, bounds, and durability behavior.

Sessions, sharing, and recovery

# Create explicitly; public is the default.
fite create --visibility public
fite create --visibility private
fite create --visibility shared

# Resume the latest session or a specific session.
fite tui
fite tui <SESSION_UUID>

# Verify and inspect signed history.
fite verify <SESSION_UUID>
fite inspect <SESSION_UUID>

# Share replay/fork access without write authority.
fite share <SESSION_UUID> conversation.fite-bundle.json

# Export continuation authority, restore, and fork.
fite export <SESSION_UUID> continuation.fite-bundle.json
fite import conversation.fite-bundle.json
fite fork <SESSION_UUID> --visibility private

Exports are password-encrypted and never include the installation owner key. share omits session write authority. Import verifies signatures, sequence, predecessor links, object completeness, and the projection hash before making a session available. NIP-77 relay synchronization is opt-in and uses the same verification rules.

Architecture

Layer Responsibility
fite-protocol Versioned event schemas, limits, encryption envelopes, golden fixtures
fite-core Deterministic fold, lifecycle invariants, recovery actions, repository traits
fite-store NDK/SQLite adapter, pagination, snapshots, backup, verified NIP-77 sync
fite-gdk Goose state-machine adapter and atomic conversation-effect batches
fite-app Daemon composition, TUI/CLI, coding tools, skills, ACP, MCP, and Buzz

The default data directory follows the Linux platform convention and contains:

events.sqlite       authoritative signed Nostr event store
projections.sqlite  disposable indexes and session heads
keys.json           owner, agent, interface, reviewer, and session keys (mode 0600)
providers.json      provider profiles and credentials (mode 0600)
permissions.json    owner-local tool defaults and overrides (mode 0600)
mcp.json            owner-local named MCP server profiles (mode 0600)
chatgpt-codex.json  local ChatGPT OAuth tokens (mode 0600, when logged in)
prompt-history.json owner-only terminal prompt history
runtime.json        loopback relay discovery; present only while a daemon runs

Canonical ordering is sequence plus previous, never relay arrival time or a Nostr timestamp. Token deltas and progress frames are ephemeral. Snapshots and projections are derived and can be rebuilt from verified canonical facts.

Interfaces

  • fite is the default interactive entrypoint and also provides headless create, submit, inspect, verify, status, configuration, export/import, fork, and sync commands.
  • fite-daemon runs the local relay and agent lifecycle as a persistent process for headless or multi-interface deployments.
  • fite-acp exposes ACP v1 over stdio for compatible editors and clients.
  • fite-buzz is an opt-in native Buzz adapter with fail-closed access gates and a signed durable reply outbox. See Buzz integration.

Configure any number of named stdio MCP extensions locally, then enable up to eight of them in a session's signed configuration:

fite mcp add filesystem npx -- -y @modelcontextprotocol/server-filesystem .
# Secret environment values are prompted securely and never enter shell history.
fite mcp add hosted tool-command --env API_TOKEN -- --stdio
fite mcp enable <SESSION_UUID> filesystem
fite mcp list

The command and arguments remain machine-local; only stable profile names enter Nostr events. /mcp manages the selected session from the TUI. ACP clients may also supply per-session MCP servers. The legacy FITE_MCP_COMMAND and FITE_MCP_ARGS_JSON override remains for CI compatibility, and all side-effecting extension calls still pass through Fite's permission policy.

Security model

  • The default relay is IPv4 loopback-only and requires NIP-42.
  • Session authorities limit a leaked writer key to one journal.
  • Private/shared canonical content uses NIP-44; public content remains signed plaintext by design.
  • Provider credentials, raw keys, MCP environments, and authorization headers must never enter canonical payloads or logs.
  • Network synchronization and native Buzz access are disabled until explicitly configured.
  • A data-directory lease prevents two daemons from advancing one installation.

Read SECURITY.md before exposing external relays or tools. Please report vulnerabilities privately rather than opening a public exploit report.

Development

nix develop path:.
just quick-check

Run just final-check before tagging a release or changing dependency pins. It adds the full Crane/Nix build and check graph. The workspace pins Rust 1.96.1, Nostr Dev Kit, Goose, and the ACP SDK for reproducible protocol behavior.

Contributor setup is in DEVELOPMENT.md. Delivered alpha capabilities are summarized in the implementation record.

License

Fite is available under the MIT License.