A safe, simple, extensible, and fast agent harness

Files
README.md

Ein #

Ein corgi logo

An AI agent for your terminal — self-hosted, plugin-driven, and yours.


┌─────────────────────────────┐          ┌──────────────────────────────┐
│          ein                │  gRPC    │          eind                │
│                             │ (proto)  │                              │
│  Ratatui terminal UI        │◄────────►│  Agent loop + tool executor  │
│  Session picker on startup  │          │  WASM plugin host            │
│  Slash command autocomplete │          │  Pluggable model clients     │
│  Animated thinking spinner  │          │  Per-session sandboxing      │
│  Syntax-highlighted diffs   │          │  SQLite session persistence  │
└─────────────────────────────┘          └──────────────────────────────┘

ein is the terminal UI you interact with; eind is the agent server that runs as a background service. On first launch, ein downloads eind automatically and registers it as a system service — no separate server setup required. Tools and model clients are sandboxed WASM plugins: choose from OpenRouter, Anthropic, OpenAI, or Ollama, or write your own.

Features #

  • Works with OpenRouter, Anthropic, OpenAI, and Ollama — bring your own key or run locally with no API key
  • Sessions persist to SQLite; resume any conversation where you left off
  • Tools run as sandboxed WASM components with client-supplied workspace roots and server-owned network controls
  • Extend with custom tools or model client backends
  • Syntax-highlighted edit diffs shown inline as the agent works
  • Server-owned provider profiles keep credentials out of clients and session records

Installation #

Install with cargo binstall:

cargo install cargo-binstall
cargo binstall --git https://github.com/mstallmo/ein ein

Or download pre-built archives directly from GitHub Releases.

Getting Started #

Before the first launch, create ~/.ein/eind.json with at least one provider profile (see Configuration). Then run ein:

  1. Server setup — eind is downloaded from GitHub Releases and registered as a background service automatically
  2. Session picker — create a new session or resume an existing one
  3. Profile picker — choose one of the server's configured profile/model pairs for a new session
  4. Directory prompt — choose whether that session may access the current working directory

ein manages the local service. A separately managed or remote eind can use the same protocol, though the service is unauthenticated and must not be exposed to an untrusted network.

Configuration #

Configuration is split by owner:

  • ~/.ein/eind.json belongs to the server and contains provider profiles, credentials, embedding, and tool network policy. Edit it on the server and restart eind after changes.
  • ~/.ein/ein.json belongs to the TUI and contains reusable workspace roots. /config opens this client file.

A minimal OpenRouter server configuration is:

{
  "version": 1,
  "default_profile": "personal",
  "profiles": {
    "personal": {
      "display_name": "Personal models",
      "plugin": "ein_openrouter",
      "params": {
        "base_url": "https://openrouter.ai/api/v1"
      },
      "secret_params": {
        "api_key": "sk-or-..."
      },
      "default_model": "sonnet",
      "models": {
        "sonnet": {
          "display_name": "Claude Sonnet",
          "model": "anthropic/claude-sonnet-4-6",
          "context_window": 200000,
          "params": {
            "max_tokens": 32000
          }
        }
      }
    }
  },
  "tool_policy": {
    "plugins": {
      "ein_webfetch": {
        "allowed_hosts": ["docs.rs"]
      }
    }
  }
}

Provider secrets remain on eind; profile listing reveals only stable profile and model IDs plus display names. A session persists its selected IDs and non-secret model settings, so changing defaults does not silently change an existing session. Use /profiles for an explicit model change.

On upgrade, eind and ein independently import their fields from the legacy ~/.ein/config.json and leave that file untouched.

Usage #

Type a message and press Enter to send. Type / to see available slash commands with autocomplete hints.

Key / Command Action
Enter Send message / run slash command
↑ / ↓ Scroll conversation history
Ctrl-C Cancel the active turn; quit when idle or on a second press
/exit Exit the TUI
/config Open ~/.ein/ein.json in $EDITOR
/clear Wipe the agent's in-memory context (SQLite history preserved)
/new Drop current session and start a fresh one
/sessions Re-open the session picker to switch sessions
/compact Summarise the conversation via the LLM and replace history with the summary
/plugins Manage installed plugins
/profiles Browse server profiles or change this session's model
/uninstall Stop and remove the eind service and binary

While the agent is working, an animated thinking spinner appears in the conversation pane. Tool invocations are shown inline as the agent uses them:

 ▸ Bash  ls -la
 ▸ Read  src/main.rs
 ▸ Write  src/main.rs
 ▸ Edit  src/main.rs

Edit calls display a syntax-highlighted diff showing removed and added lines (up to 5 each), with line numbers.

The status bar shows the active model and cumulative token usage. Use Shift+D on a session in the picker to permanently delete it.

Connection behaviour #

The TUI reconnects automatically every 3 seconds if the server is unavailable. Running /new or /sessions bypasses the retry delay and triggers an immediate reconnect attempt.

Tools #

Tools are WASM components loaded at startup. Six are included:

Tool Description
Bash Execute shell commands (streams stdout in real time)
Read Read a file from the filesystem
Write Write content to a file
Edit Replace a specific string in a file with new content
Grep Search file contents by regex across a directory tree
WebFetch Fetch a URL and return its content (HTML converted to markdown)

Adding a tool #

  1. Create a new package under plugins/ implementing the ToolPlugin trait from plugins/ein_tool/
  2. Add it to scripts/build_install_plugins.sh
  3. Rebuild — the server picks it up automatically on next start

Architecture #

crates/
  ein_agent/        Agent component (agent loop, tool call logic, extension interface)
  ein_core/         Shared types and utilities between plugins and the harness
  ein_http/         WASM-native HTTP client, shared by model client and tool plugins
  ein_plugin/       WASM plugin interface
  ein_proto/        Protocol Buffer definitions (gRPC service + message types)
ein/                Terminal UI client
eind/               gRPC server — WASM plugin host, session persistence
plugins/
  ein_anthropic/    Anthropic model client plugin
  ein_bash/         Bash tool plugin
  ein_edit/         Edit tool plugin
  ein_grep/         Grep tool plugin
  ein_ollama/       Ollama model client plugin
  ein_openai/       OpenAI model client plugin
  ein_openrouter/   OpenRouter model client plugin
  ein_read/         Read tool plugin
  ein_webfetch/     WebFetch tool plugin
  ein_write/        Write tool plugin

Protocol #

The protocol (crates/ein_proto/proto/ein.proto) defines a bidirectional streaming AgentSession RPC plus profile and session discovery. A stream opens with SessionInit, which either requests a server-defined profile/model for a new session or names an existing session to resume, and always supplies the current connection's SessionEnvironment.

Session persistence #

Sessions are persisted to ~/.ein/sessions.db. Resuming by session ID restores the durable model snapshot and full transcript while using the workspace environment supplied by the current connection.

An agent's memory lives beside it: core blocks in the same database, and the searchable tiers — facts it wrote down, and a log of what was said — in ~/.ein/memory.lance/. Those are searched by keyword and, when an embedding model is configured, by meaning as well.

Contributing #

Contributions are welcome. See CONTRIBUTING.md for how to build from source, run in development, and cut a release.

License #

Ein is licensed under the Apache License, Version 2.0.

Copyright 2026 Mason Stallmo.

By submitting a pull request, you agree that your contribution is licensed under the Apache License, Version 2.0.