Skip to content

Repository files navigation

Hexa

A session-based terminal workspace where the frontend is disposable and your shells are not.

Crash the terminal frontend, restart it, reattach, and your terminals keep running exactly where you left them.


How it works

Hexa splits into four layers:

  • hexe terminal — the terminal UI frontend (aliases: hexe mux, hexe multiplexer).
  • shared frontend runtime — attach lifecycle, transport, and the frontend-side session projection.
  • hexe session / hexe ses — the session authority that owns canonical session state.
  • hexe pod — one per pane. Owns the PTY, holds the shell, buffers output even while detached.

See architecture for the full picture.


Palette namespaces

A program can claim its own 256-colour table — one of 32 numbered slots — for the output it writes. Recolour that table and only its cells change — the rest of the pane stays exactly as it was, on screen and in scrollback, with no redraw from the application.

hexe palette set --ns 4 33=#ff00aa bg=#1a1020
hexe palette get # what is actually set

An application drives it directly, with nothing to negotiate first — claim a slot, print, release it:

printf'\033]1330;set;4;33=#ff00aa\033\\'printf'\033]1330;use;4\033\\'printf'this line resolves colour 33 through slot 4\n'printf'\033]1330;end\033\\'

hexe holds the colours and resolves the indexes; it never decides which cells belong to which slot. Every cell records the slot that was current when it was written — the number itself, so there is no mapping to lose — and two slots are correct on screen at once, with a repaint reaching scrollback. Slot 0 is what unclaimed output resolves against, so setting it recolours the ordinary palette. Anything hexe does not recognise — an unknown name, a program that claims nothing, another terminal entirely — falls back to your own palette, so a default install looks exactly as it did before.

See the palette protocol for the sequences to emit.


Docs

One document per feature in docs/, each opening with a recording of it running: how it works, how it differs from tmux, what it cannot do, and where it lives in the tree. Every claim in them was checked against the source or a running build, and the recordings are made by hexe itself — see recording.

Four processesfrontend, runtime, SES, pod — and why only one is disposable
Sessionsdetach, reattach, replay, adoption, and what is written down
Podsone daemon per pane: the PTY, the backlog ring, exactly-once input
Instancestwo whole stacks on one machine that cannot see each other
Panes and tabsthe split tree, geometric focus, zoom, broadcast, select-and-swap
Floatsoverlay panes with lifetimes: per-directory, sticky, exclusive, sandboxed
Keybindingsno prefix: chords, conditions, and what happens to the key afterwards
Reading what happenedscrollback search, copy-mode, OSC 133 prompt marks
Overlays and popupsnotifications, questions, pickers, keycast, pane labels
Paintingthe bar, titles, sprites and popups are drawn by an external painter
Shell integrationwhat a shell reports to the mux, and how the prompt is drawn
Palette protocola program claims its own 256-colour table for the output it writes
Configurationone Lua file, a schema that refuses typos, reload without losing panes
Project sessions.hexe.lua, freezing a session, and the trust ledger
Isolationnamespaces and cgroups per pane — and what it needs from the kernel
The command lineaddressing sessions, panes and pods from a script
Recordinghexe writes asciicasts of itself; every film in the docs was made that way

Quick start

Build (requires Zig). A static musl binary, which is what make build gives you:

make build # zig build -Doptimize=ReleaseFast, static musl, stripped
make install # …and copy it to ~/.local/bin/hexe

Run:

hexe terminal new # a new session, named after a pokemon
hexe terminal new --name work # or named by you
hexe # bare: attach to a session rooted here, or load ./.hexe.lua

Detach and come back. Detach is a keybinding, so it is whatever your config says — there is no built-in chord:

hexe.key({ hexe.key.ctrl, hexe.key.alt, hexe.key.d }, hexe.action.detach()),
hexe session list # what is running, attached or not
hexe terminal attach work # by name, or by uuid prefix

Config lives at ~/.config/hexe/init.lua and is Lua. See configuration, and keybindings for the binding language.


History

Started as bash and Python hacks wrapped around tmux. Absolutely cursed code. Shell scripts spawning tmux sessions, Python daemons talking to tmux through send-keys, config files that were basically more shell scripts. It was wild. But it worked, and it was the workflow I wanted.

Rewrote it properly in Rust on top of tmux-rs, got far, learned a lot about terminal internals. But that crate is mostly unsafe and you're still building on top of tmux's architecture rather than escaping it.

Then Ghostty came out. Saw what Mitchell was doing with Zig and decided to start from scratch. Zero regrets. Zig is a joy, Ghostty's VT implementation is solid, and the architecture finally matches what I actually wanted to build.


Credits

About

terminal multiplexer based on libghostty

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages