terminal session audit for linux
Every shell session. Recorded. Encrypted. Replayable.
Ghost Shell is a terminal session recorder and root audit daemon. It records every shell session on your Linux hosts — including SSH — encrypts it at rest with AES-256-GCM, and lets root replay, search, and live-tail from a root-only store.
deb · rpm · static binary · source — install in under a minute ↓$ ghostshell rec ghostshell: recording to ghostshell-daemon (central) — type 'exit' or Ctrl-D to stop $ sudo systemctl restart nginx $ exit $ sudo ghostshell play 20260711T142031-4471 > 00:04 / 00:12 [### ] 33% 1x spc play q quit $ sudo strings /var/lib/ghostshell/alice/20260711T142031-4471.cast | head -1 TTEC2 # AES-256-GCM ciphertext — not alice's commands
Recorded as alice types it. On disk: root-owned ciphertext.
Binary
~3.5 MB static, CGO_ENABLED=0
Encryption
AES-256-GCM at rest
Store
/var/lib/ghostshell · root:root 0700
Availability
fail-open — never blocks a login
License
GPL-2.0 · open source
the problem
Your current audit trail is a polite fiction.
Three tools people actually use to answer “what happened?” — and where each one stops.
~/.bash_history
It lies.
- User-writable — one
history -cand it's gone - Drops pipes
- Never records output — you see the command, never what it printed
script(1)
Plaintext, readable in practice.
- Anyone who can read the typescript file reads every keystroke and secret it captured
- No encryption, no central store, no access control
- No way to search last month's typescripts across a host
asciinema
Built for demos, not audit.
- Great for sharing a terminal clip
- No central store, no encryption, no per-host retention story
- Nothing ties a cast to a user on your box
Ghost Shell exists for the case where you actually need to answer “what happened on that box?”
see it run
Record it. Replay it. Exactly as it happened.
A scripted tour of one session's life — or take over and drive the player yourself.
Users just work. The session records itself — to the central store when the daemon is up, to ~/.local/share/ghostshell when it isn't.
capabilities
Built for the 2 a.m. question.
Everything below ships in the one binary — no plugins, no server product.
Full-screen replay
Seek, pause, 1/64×–64× speed, jump-to-command, click-to-seek. Original timing preserved; TUI apps like vim and htop redraw exactly.
$ ghostshell play 20260711T142031-4471
Search everything
Grep across every recording on the host, with natural-language date filters and per-user scoping.
$ sudo ghostshell search --from "2 days ago" nginx
Live tail
Stream an in-progress session as it happens — or print the last N lines of any finished one.
$ sudo ghostshell tail -f <id>
Process tree
Which command ran which, with exit codes and durations, captured by a bash trace shim.
$ sudo ghostshell tree <id>
Local AI analysis
A deterministic pass — failures, retry loops, slowest steps — plus an optional Ollama summary. Loopback-only unless --allow-remote.
$ sudo ghostshell analyze <id>
Ansible run tracking
Playbook runs recorded on the controller: plays, tasks, per-host status, output.
$ sudo ghostshell ansible list
Auto-record on login
An optional profile.d hook records every interactive login. Skips nested sudo su -. Fail-open.
/etc/profile.d/ghostshell-autorec.sh
Export to plain asciinema
Decrypt a recording to a standard v2 cast that plays with asciinema play.
$ sudo ghostshell export -o session.cast <id>
Also in the box: bash tab-completion · ghostshell --check config validation · interactive prune retention · backup of the central store · optional playback password.
architecture
One binary, one daemon, one root-only directory.
The full path of a keystroke, from PTY to encrypted cast.
The live architecture diagram is desktop-only.
Open this page on a bigger screen to watch a session travel from PTY capture to the encrypted root-only store.
security model
Read this before relying on it.
What Ghost Shell guarantees, how the key works, and the one thing it is not.
The trust boundary
- Central store /var/lib/ghostshell is root:root 0700; recordings 0600. Only root reads recordings — users can't read even their own.
- The socket is 0666 so any user can submit a session — but the daemon attributes it to the kernel-reported UID via SO_PEERCRED. Identity can't be forged, and nothing can be read back over the socket.
- Every read command — play, tail, tree, search, export, ls --all — requires root.
- An optional playback password (hashed, root:root 0600) gates every content-revealing command — a second factor on top of root, not a replacement.
- Ingest rejects symlinks and irregular entries — a crafted home directory can't redirect the root ingest.
Encryption and the key
- Recordings are AES-256-GCM encrypted before they touch disk. On disk a .cast opens with the magic prefix TTEC1; the rest is ciphertext.
- The key /var/lib/ghostshell/.ghostshell.key (root:root 0600) is unique per server and set immutable with chattr +i — rm, vi, sed, >, and tee fail, even as root, until someone runs chattr -i.
- Back up the key. Lose it and every recording is permanently unreadable; the daemon refuses to start if the key is missing while encrypted recordings exist.
Filesystem caveat: ext4 and xfs honor +i; overlayfs, tmpfs and many network mounts silently ignore it. The daemon reads the flag back and logs:
WARNING: … the at-rest key is NOT protected from deletion/modification
Hardening, in passing
- NoNewPrivileges
- ProtectSystem=full
- SystemCallFilter=@system-service
- RestrictAddressFamilies=AF_UNIX
- UMask=0077
- race-tested
- staticcheck + golangci-lint clean
- systemd-analyze security: 3.9 OK
- security-audited — AUDIT.md
install
On the box in under a minute.
Every vX.Y.Z tag publishes a .deb, an .rpm, and static binaries — plus SHA256SUMS.
VER=$(curl -fsSL https://api.github.com/repos/Karannnnn614/Ghost-Shell/releases/latest | grep -oP '"tag_name":\s*"v\K[^"]+')
curl -fLO "https://github.com/Karannnnn614/Ghost-Shell/releases/download/v${VER}/ghostshell_${VER}_amd64.deb"
sudo apt install "./ghostshell_${VER}_amd64.deb"Fetches the latest release; releases are published on every vX.Y.Z tag.
Packages create /var/lib/ghostshell (root-only), write /etc/ghostshell/ghostshell.conf with all defaults visible, and enable ghostshell-daemon.
the cli
Five commands you'll actually run.
All audit commands read the root-only store, so all of them require root.
Full reference: 10 personal + 12 audit commands. /docs/commands
positioning
The right tool depends on the question you're asking.
script(1) and asciinema answer “what did I do?”; auditd answers “what did the kernel see?”; Ghost Shell answers “what happened on that box — show me.”
| Capability | Ghost Shell | script(1) | asciinema | auditd |
|---|---|---|---|---|
| Captures full terminal output | yes | yes | yes | no — syscalls and execs, not terminal output |
| Encrypted at rest | AES-256-GCM | plaintext | plaintext | plaintext logs |
| Central root-only store | /var/lib/ghostshell, 0700 | no — file in user's cwd | no — local files or upload | /var/log/audit (root) |
| Replay with original timing | yes — seek, 1/64×–64×, click-to-seek | with scriptreplay | yes | no replay |
| Live tail of a session | yes — tail -f <id> | no | no | events, not the terminal |
| Search across sessions | yes — with date filters | no | no | ausearch (event fields) |
| Per-command exit codes & timing | yes — tree (bash shim) | no | no | yes (execve records) |
| Survives a user who avoids the wrapper | no — fail-open, by design (see security model) | no | no | yes — kernel-level |
| Designed for | host audit + replay | quick local capture | demos & sharing | kernel/syscall audit |
auditd records syscalls and events, not terminal output. It answers “what syscalls ran” — not “what did the operator see on screen.”
Does Ghost Shell replace auditd? No — they answer different questions. Run both.
Know what actually happened on your box.
One static binary. Root-only store. Encrypted at rest. Fail-open.
$ sudo apt install "./ghostshell_${VER}_amd64.deb"