···4242 pull_request:
4343 push:
4444 tags:
4545- - '**[0-9]+.[0-9]+.[0-9]+*'
4545+ - "**[0-9]+.[0-9]+.[0-9]+*"
46464747jobs:
4848 # Run 'dist plan' (or host) to determine what tasks we need to do
···218218 name: artifacts-build-plugins
219219 path: ${{ env.ARCHIVE }}
220220221221- # Build and package ein-server for each platform so bootstrap.rs can download it
221221+ # Build and package eind for each platform so bootstrap.rs can download it
222222 build-server-artifacts:
223223 name: build-server-artifacts (${{ matrix.target }})
224224 needs:
···249249 uses: arduino/setup-protoc@v3
250250 with:
251251 repo-token: ${{ secrets.GITHUB_TOKEN }}
252252- - name: Build ein-server
253253- run: cargo build --release -p ein-server --target ${{ matrix.target }}
252252+ - name: Build eind
253253+ run: cargo build --release -p eind --target ${{ matrix.target }}
254254 - name: Package archive
255255 shell: bash
256256 run: |
257257 TARGET="${{ matrix.target }}"
258258- ARCHIVE="ein-server-${TARGET}.tar.xz"
258258+ ARCHIVE="eind-${TARGET}.tar.xz"
259259 mkdir -p dist-server
260260- cp "target/${TARGET}/release/ein-server" dist-server/
261261- tar -cJf "${ARCHIVE}" -C dist-server ein-server
260260+ cp "target/${TARGET}/release/eind" dist-server/
261261+ tar -cJf "${ARCHIVE}" -C dist-server eind
262262 echo "ARCHIVE=${ARCHIVE}" >> "$GITHUB_ENV"
263263 - name: Upload server archive artifact
264264 uses: actions/upload-artifact@v6
+5-5
CLAUDE.md
···1212rustup target add wasm32-wasip2
1313cargo build # Build all crates
1414cargo build -p ein-tui # Build just the TUI client
1515-cargo build -p ein-server # Build just the server
1515+cargo build -p eind # Build just the server
1616```
17171818Plugins (tool plugins and model client plugins) are WASM components compiled separately:
···42424343```bash
4444# Terminal 1 — start the server (no env vars needed)
4545-cargo run --bin ein-server
4545+cargo run --bin eind
46464747# Terminal 2 — start the TUI (connects to localhost:50051 by default)
4848cargo run --bin ein-tui
···59596060```
6161┌─────────────────────────────┐ ┌──────────────────────────────┐
6262-│ ein-tui │ gRPC │ ein-server │
6262+│ ein-tui │ gRPC │ eind │
6363│ │ (proto) │ │
6464│ Ratatui terminal UI │◄────────►│ Agent loop + tool executor │
6565│ Keyboard / render loop │ │ WASM plugin host │
···103103- **Save** — after each agent turn, the full message history is serialised and written (`save_messages`).
104104- **Resume** — when a client supplies a known `session_id`, `load_messages` restores the conversation so the agent picks up where it left off.
105105106106-Database migrations live in `crates/ein-server/migrations/`.
106106+Database migrations live in `eind/migrations/`.
107107108108### Client config (`crates/ein-tui/src/config.rs`)
109109···113113114114Legacy flat config files (with top-level `api_key`, `base_url`, `model`, `max_tokens`) are automatically migrated to the nested format on load.
115115116116-### Server (`crates/ein-server/`)
116116+### Server (`eind/`)
117117118118| File | Role |
119119|------|------|
···4455```
66┌─────────────────────────────┐ ┌──────────────────────────────┐
77-│ ein-tui │ gRPC │ ein-server │
77+│ ein-tui │ gRPC │ eind │
88│ │ (proto) │ │
99│ Ratatui terminal UI │◄────────►│ Agent loop + tool executor │
1010│ Session picker on startup │ │ WASM plugin host │
···2323cargo binstall --git https://github.com/mstallmo/ein ein
2424```
25252626-This installs both `ein-tui` (terminal UI) and `ein-server` (gRPC agent server). You can also install them individually:
2626+This installs both `ein-tui` (terminal UI) and `eind` (gRPC agent server). You can also install them individually:
27272828```bash
2929cargo binstall --git https://github.com/mstallmo/ein ein-tui
3030-cargo binstall --git https://github.com/mstallmo/ein ein-server
3030+cargo binstall --git https://github.com/mstallmo/ein eind
3131```
32323333Or download archives directly from [GitHub Releases](https://github.com/mstallmo/ein/releases).
···128128Start the server in one terminal:
129129130130```bash
131131-cargo run --bin ein-server
131131+cargo run --bin eind
132132```
133133134134Start the TUI client in another:
···270270```
271271crates/
272272 ein-proto/ Protocol Buffer definitions (gRPC service + message types)
273273- ein-server/ gRPC server — agent loop, WASM plugin host, session persistence
273273+ eind/ gRPC server — agent loop, WASM plugin host, session persistence
274274 ein-tui/ Terminal UI client
275275packages/
276276 ein_tool/ WASM tool plugin interface (ToolPlugin trait, ToolDef, syscalls)
···315315316316Uses **Ratatui** (v0.29) for rendering and **crossterm** for keyboard events. The conversation pane renders a corgi pixel-art header on startup. Edit diffs are syntax-highlighted using `syntect` with the `base16-ocean.dark` theme.
317317318318-### Server modules (`crates/ein-server/src/`)
318318+### Server modules (`eind/src/`)
319319320320| File | Role |
321321|------|------|
···329329330330## Releasing
331331332332-Releases are fully automated via CI using [cargo-dist](https://axodotdev.github.io/cargo-dist/). Only the `crates/ein` meta-package is distributed — it includes both the `ein-tui` and `ein-server` binaries.
332332+Releases are fully automated via CI using [cargo-dist](https://axodotdev.github.io/cargo-dist/). Only the `crates/ein` meta-package is distributed — it includes both the `ein-tui` and `eind` binaries.
333333334334**1. Bump the version**
335335
···3344//! Ein server library.
55//!
66-//! Exposes [`run`] so both the standalone `ein-server` binary and the `ein`
66+//! Exposes [`run`] so both the standalone `eind` binary and the `ein`
77//! meta-package binary can share the same entry-point without duplicating code.
8899mod grpc;
···72727373 let server = AgentServer::new().await?;
74747575- println!("ein-server listening on {addr}");
7575+ println!("eind listening on {addr}");
76767777 Server::builder()
7878 .add_service(AgentServiceServer::new(server))
+2-2
crates/ein-server/src/main.rs
eind/src/main.rs
···2929 let args = Args::parse();
30303131 match args.command {
3232- Some(Commands::InstallPlugins { version }) => ein_server::install_plugins(version).await,
3333- None => ein_server::run(args.port).await,
3232+ Some(Commands::InstallPlugins { version }) => eind::install_plugins(version).await,
3333+ None => eind::run(args.port).await,
3434 }
3535}
···242242243243 // Mount each allowed path at its absolute guest path so plugins can
244244 // open files by absolute path. Additionally mount the first path as
245245- // "." so that relative paths (e.g. "crates/ein-server/Cargo.toml")
245245+ // "." so that relative paths (e.g. "crates/eind/Cargo.toml")
246246 // resolve correctly — WASI resolves relative paths against the guest
247247 // current directory, which must be explicitly preopened.
248248 let mut first = true;
···11// SPDX-License-Identifier: Apache-2.0
22// Copyright 2026 Mason Stallmo
3344-//! Bootstrap logic: downloads `ein-server` on first run and registers it as a
44+//! Bootstrap logic: downloads `eind` on first run and registers it as a
55//! system service (macOS LaunchAgent or Linux systemd user service).
6677// These items are only called from the #[cfg(not(debug_assertions))] block in
···1414 os::unix::fs::PermissionsExt,
1515 path::{Path, PathBuf},
1616};
1717-use xz2::read::XzDecoder;
1817use tar::Archive;
1918use tokio::{fs, process::Command, task};
1919+use xz2::read::XzDecoder;
20202121const GITHUB_REPO: &str = "mstallmo/ein";
22222323-/// Path where `ein` installs the server binary: `~/.ein/bin/ein-server`.
2323+/// Path where `ein` installs the server binary: `~/.ein/bin/eind`.
2424pub fn server_bin_path() -> PathBuf {
2525 dirs::home_dir()
2626 .expect("home directory not found")
2727 .join(".ein")
2828 .join("bin")
2929- .join("ein-server")
2929+ .join("eind")
3030}
31313232/// Compile-time target triple used to select the right GitHub release asset.
···4343 ""
4444}
45454646-/// Downloads the `ein-server` binary for the current platform from GitHub
4747-/// releases and writes it to `~/.ein/bin/ein-server` with executable permissions.
4646+/// Downloads the `eind` binary for the current platform from GitHub
4747+/// releases and writes it to `~/.ein/bin/eind` with executable permissions.
4848pub async fn download_server(version: &str) -> Result<()> {
4949 let ver = version.trim_start_matches('v');
5050 let tag = format!("v{ver}");
5151 let triple = target_triple();
5252 // cargo-dist names archives as "{package}-{triple}.tar.xz" (no version in filename).
5353- let archive_name = format!("ein-server-{triple}.tar.xz");
5353+ let archive_name = format!("eind-{triple}.tar.xz");
5454 let url = format!("https://github.com/{GITHUB_REPO}/releases/download/{tag}/{archive_name}");
55555656 let dest = server_bin_path();
···8383 perms.set_mode(0o755);
8484 fs::set_permissions(&dest, perms).await?;
85858686- println!("ein-server installed to {}", dest.display());
8686+ println!("eind installed to {}", dest.display());
8787 Ok(())
8888}
89899090-/// Extracts the `ein-server` binary from a tar.xz archive into `dest`.
9090+/// Extracts the `eind` binary from a tar.xz archive into `dest`.
9191fn extract_server(bytes: &[u8], dest: &Path) -> Result<()> {
9292 let xz = XzDecoder::new(io::Cursor::new(bytes));
9393 let mut archive = Archive::new(xz);
···9999 let mut entry = entry.context("corrupt archive entry")?;
100100 let entry_path = entry.path().context("entry has no path")?;
101101102102- // The archive contains exactly one file: the `ein-server` binary.
102102+ // The archive contains exactly one file: the `eind` binary.
103103 // Accept it regardless of any leading directory component.
104104 let file_name = entry_path
105105 .file_name()
106106 .and_then(|n| n.to_str())
107107 .unwrap_or("");
108108109109- if file_name == "ein-server" {
109109+ if file_name == "eind" {
110110 let mut file = std::fs::File::create(dest)
111111 .with_context(|| format!("failed to create {}", dest.display()))?;
112112- io::copy(&mut entry, &mut file).context("failed to write ein-server")?;
112112+ io::copy(&mut entry, &mut file).context("failed to write eind")?;
113113 return Ok(());
114114 }
115115 }
116116117117- anyhow::bail!("ein-server binary not found in archive")
117117+ anyhow::bail!("eind binary not found in archive")
118118}
119119120120// ---------------------------------------------------------------------------
121121// Service registration
122122// ---------------------------------------------------------------------------
123123124124-/// Ensures `ein-server` is registered as a system service.
124124+/// Ensures `eind` is registered as a system service.
125125///
126126/// On macOS, installs a LaunchAgent plist and loads it.
127127/// On Linux, writes a systemd user unit and enables it.
···141141// Uninstall
142142// ---------------------------------------------------------------------------
143143144144-/// Stops and removes the `ein-server` service and binary installed by
144144+/// Stops and removes the `eind` service and binary installed by
145145/// [`ensure_service_installed`] and [`download_server`].
146146///
147147/// Returns a list of completed step descriptions for display in the TUI.
···292292 anyhow::bail!("launchctl load failed: {stderr}");
293293 }
294294295295- println!("ein-server registered as LaunchAgent ({LAUNCH_AGENT_LABEL})");
295295+ println!("eind registered as LaunchAgent ({LAUNCH_AGENT_LABEL})");
296296 Ok(())
297297}
298298···301301// ---------------------------------------------------------------------------
302302303303#[cfg(target_os = "linux")]
304304-const SYSTEMD_SERVICE_NAME: &str = "ein-server";
304304+const SYSTEMD_SERVICE_NAME: &str = "eind";
305305306306#[cfg(target_os = "linux")]
307307fn systemd_unit_path() -> PathBuf {
···363363 anyhow::bail!("systemctl enable failed: {stderr}");
364364 }
365365366366- println!("ein-server registered as systemd user service ({SYSTEMD_SERVICE_NAME})");
366366+ println!("eind registered as systemd user service ({SYSTEMD_SERVICE_NAME})");
367367 Ok(())
368368}
+3-3
crates/ein-tui/src/input.rs
···66use tracing::{debug, info, warn};
7788use crate::app::{
99- App, CwdState, DisplayMessage, Modal, SessionPickerState, SetupWizardState, UninstallModalState,
1010- UninstallPhase, WizardStep,
99+ App, CwdState, DisplayMessage, Modal, SessionPickerState, SetupWizardState,
1010+ UninstallModalState, UninstallPhase, WizardStep,
1111};
1212use crate::connection::to_proto_session_config;
1313···5959 },
6060 CommandDef {
6161 name: "/uninstall",
6262- description: "Stop and remove the ein-server service and binary",
6262+ description: "Stop and remove the eind service and binary",
6363 },
6464];
6565
+2-2
crates/ein-tui/src/lib.rs
···98989999 info!(server_addr = %args.server_addr, "ein-tui starting");
100100101101- // In release builds: download ein-server if absent, then register it as a
101101+ // In release builds: download eind if absent, then register it as a
102102 // system service. Runs before raw mode so stdout is visible for progress.
103103 #[cfg(not(debug_assertions))]
104104 {
105105 let bin = bootstrap::server_bin_path();
106106 if !bin.exists() {
107107- println!("Downloading ein-server {}...", env!("CARGO_PKG_VERSION"));
107107+ println!("Downloading eind {}...", env!("CARGO_PKG_VERSION"));
108108 bootstrap::download_server(env!("CARGO_PKG_VERSION")).await?;
109109 }
110110 bootstrap::ensure_service_installed().await?;
···11-# ein-server Code Review Report
11+# eind Code Review Report
2233_Evaluation Date: 2026-04-03_
44···6677## Executive Summary
8899-The `ein-server` (located in `crates/ein-server`) is a sophisticated gRPC server that implements a secure WASM-based agent hosting model. It successfully implements the core architecture of:
99+The `eind` (located in `eind`) is a sophisticated gRPC server that implements a secure WASM-based agent hosting model. It successfully implements the core architecture of:
1010- Loading and orchestrating WASM WASI plugins with **per-session isolation**
1111- Fine-grained **access controls** (filesystem paths, network hosts)
1212- Graceful **error handling** (preserving sessions after errors)
···807807808808## Conclusion
809809810810-**The `ein-server` is a well-architected and innovative implementation** of a secure WASM-based agent hosting model. Its design prioritizes:
810810+**The `eind` is a well-architected and innovative implementation** of a secure WASM-based agent hosting model. Its design prioritizes:
811811- ✅ Session isolation
812812- ✅ Fine-grained access control
813813- ✅ Graceful error handling
+3-3
specs/EIN-SERVER-CODE-REVIEW-v2.md
···11-# ein-server Code Review Report
11+# eind Code Review Report
2233_Evaluation Date: 2026-04-04_
44_Rated: v0.1.0_
···7788## Executive Summary
991010-The `ein-server` (located in `crates/ein-server`) is a sophisticated gRPC server that implements a secure WASM-based agent hosting model. It successfully implements the core architecture of:
1010+The `eind` (located in `eind`) is a sophisticated gRPC server that implements a secure WASM-based agent hosting model. It successfully implements the core architecture of:
11111212- Loading and orchestrating WASM WASI plugins with **per-session isolation**
1313- Fine-grained **access controls** (filesystem paths, network hosts)
···461461462462## Conclusion
463463464464-The `ein-server` is well-architected for Wasm plugin execution, with a clear separation of concerns. Security concerns around shell command execution must be addressed first. Error handling and metric instrumentation would significantly improve user experience and debugging. With the fixes above, `ein-server` will be a robust, observable, and secure component of the `ein` ecosystem.
464464+The `eind` is well-architected for Wasm plugin execution, with a clear separation of concerns. Security concerns around shell command execution must be addressed first. Error handling and metric instrumentation would significantly improve user experience and debugging. With the fixes above, `eind` will be a robust, observable, and secure component of the `ein` ecosystem.
465465466466---
467467