bastion
Block-aware web terminal. Persistent shells, OSC 133 command segmentation,
{command, output, exit}sidebar blocks, xterm.js front-end. Mount it in any axum app, orcargo installand run standalone.
Part of DevOps Defender's client-first attested-terminal story. Runs anywhere axum runs; the DD stack is one deployment target among others.
Status
Early — v0.1 ships the local block-terminal module. Noise E2E,
multi-node aggregation, and Tauri desktop/mobile apps land in later
releases. See the plan at
~/.claude/plans/more-and-more-i-modular-pearl.md if you have it, or
the GitHub project board.
Standalone use
Then open http://127.0.0.1:7681/.
Embed in an axum app
use Router;
async
Custom HTML chrome:
let mgr = new.with_shell;
See examples/standalone.rs for a runnable demo.
Routes
Relative to wherever you mount the router:
| Method | Path | Purpose |
|---|---|---|
| GET | / |
SPA HTML |
| GET | /api/sessions |
list sessions |
| POST | /api/sessions |
create a session |
| DELETE | /api/sessions/{id} |
kill a session |
| GET | /ws/{id} |
WebSocket for a session |
WebSocket protocol
Client → server:
- binary frames = stdin bytes (forwarded to PTY)
- text JSON
{"type":"resize","cols":N,"rows":N}|{"type":"hello","have_up_to":N}
Server → client:
- binary frames = raw PTY bytes (feed to xterm.js)
- text JSON
{"type":"block",...record}|{"type":"exit","code":N}|{"type":"gap","from":N,"to":N}|{"type":"ready","seq":N}
How segmentation works
bastion spawns bash (or sh) with
WezTerm's shell integration
injected via --rcfile. The shell emits
OSC 133
sequences (A, B, C, D) around prompt / input / command / exit
boundaries. bastion's vte-based
parser cuts the byte stream into one BlockRecord per command and
emits it to subscribed WebSocket clients alongside the raw PTY bytes.
License
MIT — see LICENSE-MIT.