Daemon-backed terminal emulator for persistent sessions and headless control
  • Rust 90.5%
  • Python 7.6%
  • Shell 0.7%
  • Nix 0.7%
  • WGSL 0.2%
  • Other 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
natsukium 0661ac44f5
All checks were successful
fuzz / cargo fuzz nightly long-run (push) Has been skipped
darwin / detect relevant changes (push) Successful in 9s
darwin / darwin docs-only no-op (push) Has been skipped
windows / detect relevant changes (push) Successful in 10s
windows / windows docs-only no-op (push) Has been skipped
fuzz / cargo fuzz smoke (per target) (push) Successful in 59s
darwin / build felis (aarch64-darwin) (push) Successful in 1m40s
pr / nix flake check (push) Successful in 1m23s
windows / cargo nextest (Windows) (push) Successful in 2m39s
windows / frontend smoke (Windows) (push) Successful in 2m37s
pr / cargo build / clippy / test / deny (push) Successful in 3m15s
pr / wire schema is compatible with the base (push) Successful in 4s
pr / frontend smoke (x86_64-linux) (push) Successful in 1m53s
windows / cargo clippy (Windows cross) (push) Successful in 15s
Merge pull request 'render: shader contract v2 (chained stages, history, params, event animation)' (#239) from render/shader-contract-ext into main
Reviewed-on: #239
2026-10-24 12:45:15 +09:00
.agents/skills tools: run every tool's tests under pytest 2026-10-21 18:22:45 +09:00
.cargo mutants: build the state-dump feature so its validators are graded 2026-10-13 23:08:20 +09:00
.claude Import from the private repository 2026-09-26 17:51:59 +09:00
.config Import from the private repository 2026-09-26 17:51:59 +09:00
.forgejo tools: run every tool's tests under pytest 2026-10-21 18:22:45 +09:00
.github tools: run every tool's tests under pytest 2026-10-21 18:22:45 +09:00
crates client-core, client, render: event-bounded post-stage animations 2026-10-24 11:42:01 +09:00
dev nix: source the ssh function from the home-manager module 2026-10-21 23:10:50 +09:00
docs docs: shader contract v2 2026-10-24 11:42:33 +09:00
fuzz workspace, ci, docs: make every crate publishable and package them in CI 2026-10-19 23:08:32 +09:00
nix nix: source the ssh function from the home-manager module 2026-10-21 23:10:50 +09:00
share nix, docs, tools: ship an ssh function that installs xterm-felis remotely 2026-10-21 23:10:50 +09:00
skills/felis docs, skills: state each limit once and cite its owner 2026-10-20 13:40:04 +09:00
tests protocol, tests: check the minor ledger from the workspace tests member 2026-10-20 00:25:48 +09:00
tools release: serialize publish dry runs that share a CARGO_HOME 2026-10-22 08:46:35 +09:00
.gitattributes Import from the private repository 2026-09-26 17:51:59 +09:00
.gitignore tools: run every tool's tests under pytest 2026-10-21 18:22:45 +09:00
AGENTS.md docs: name only the area whose behavior changes in a commit scope 2026-10-20 12:36:03 +09:00
Cargo.lock release: 0.3.1 2026-10-24 00:03:58 +09:00
Cargo.toml release: 0.3.1 2026-10-24 00:03:58 +09:00
CHANGELOG.md docs: shader contract v2 2026-10-24 11:42:33 +09:00
clippy.toml Import from the private repository 2026-09-26 17:51:59 +09:00
CONTRIBUTING.md tools: run every tool's tests under pytest 2026-10-21 18:22:45 +09:00
deny.toml docs, skills: state contracts in the testing and workspace references, not inventories 2026-10-20 12:44:51 +09:00
flake.lock deps: Lock file maintenance 2026-10-19 00:34:33 +09:00
flake.nix nix: bump the pinned Kani with one recipe 2026-10-20 12:50:05 +09:00
justfile release: dry-run the publish from a fresh unpack of the packages 2026-10-22 08:41:10 +09:00
LICENSE Import from the private repository 2026-09-26 17:51:59 +09:00
README.md docs, nix: lead the README and package metadata with what felis does 2026-10-19 19:53:35 +09:00
release-plz.toml release, ci, docs, skills: bump, check and publish the crates with the app release 2026-10-19 23:51:15 +09:00

felis

A daemon-backed terminal emulator for persistent sessions and headless control.

The daemon holds live sessions, terminal state, scrollback, and images in memory; GUI windows and CLI tools connect as clients over a public, versioned IPC.

What felis owns

  • Persistent sessions, local and remote. Close a window without killing the shell, a long build, or an agent. Reattach locally with felis attach, or put a local GPU window onto a remote daemon with felis --host user@remote. Because the daemon transfers live terminal state instead of stacking a second emulator, sessions survive disconnects without tmux's protocol degradation or keybind collisions (attach over SSH, tmux workflows without tmux).
  • Headless control and typed IPC. Every session is reachable without a window. felis sessions and felis bridge let scripts, editor integrations, and AI agents spawn sessions, inject input, wait for commands to finish, and inspect screen state with --format json or jsonl (driving a session without a window, CLI reference, IPC protocol).
  • Modern protocol fidelity. felis implements Kitty graphics, Kitty text sizing, Kitty keyboard protocol, OSC 8 hyperlinks, OSC 133 prompt marks, OSC 7501 program status, East Asian width, and ligature font shaping (protocol support matrix). Because the daemon retains protocol state in memory, rich TUIs keep their fidelity across detach and reattach (correct by construction).

Quick start

With Nix and flakes enabled:

nix run github:felis-terminal/felis

A window opens on a fresh session, and the shell inside it keeps running once you close the window.

The install guide covers the profile, Home Manager, Homebrew, standalone release archives (Linux, macOS, Windows), and from-source paths. Your first session walks the close-and-reattach loop. One TOML file controls fonts, colors, keybindings, and window appearance (configuration, keybindings).

Performance

felis is measured against Kitty, Alacritty, WezTerm, Ghostty and foot with the in-tree cross-terminal harness (just bench-vs). On an Apple M4 Max it has the best throughput of the field on every benchmark. On Linux it is faster than Kitty, WezTerm and Ghostty on every throughput benchmark and splits them with Alacritty, Ghostty's tip build and foot, while foot and Alacritty keep the lead on keystroke latency, startup, and memory. Benchmarks summarizes the standing per suite and has every figure.

Scope and design

Each felis window displays one session. Layout (tiling, tabs, workspaces) belongs to your window manager, and workflow belongs to your shell and external programs. There are no in-terminal tabs or splits, and no embedded scripting runtime; session pickers, notification consumers, and editor integrations run as separate tools over the same public CLI and IPC as your own scripts.

Read the vision for where felis draws the line, the design values and testable principles for why, and non-goals for what stays outside the boundary.

Status and compatibility

felis is at 0.x, an early release distributed as a Nix flake and as an archive attached to the release for each supported target. The CLI and IPC are versioned, but expect edges.

Three targets are supported: x86_64-linux and aarch64-darwin are published to the project's binary cache and also ship as tarballs that run on a host without Nix, and x86_64-pc-windows-msvc ships as a zip. aarch64-linux is committed and builds from source, but carries no claim. See the platform matrix for details and the protocol support matrix for terminal compatibility.

Open work is tracked in Forgejo issues and mirrored on GitHub.

Documentation and development

The manual and its full index live in docs/. For architecture and comparisons with other terminals, follow the explanation index.

felis is written in Rust with wgpu and winit. The Nix flake owns the development toolchain:

nix develop
cargo build

See CONTRIBUTING.md for the development workflow and verification gates, or the install guide to build and run from source.

License

Apache-2.0. See LICENSE.

One file is excepted: tools/bench/doom-fire-bench.patch is GPL-3.0-or-later, since a patch carries the upstream lines it modifies and DOOM-fire-zig is GPLv3. It is applied to that project's source at build time by the benchmark dev shell and reaches no felis binary.