Terminal session recorder for CTF competitions and authorized penetration testing.
Designed for operators who want replayable evidence, structured reporting, and less manual note-taking during real lab work.
Guild Scroll wraps your terminal with script and zsh hooks to capture every command, output, and asset into structured JSONL logs — so you can replay, search, export, and report without manual note-taking.
Installation · Quick Start · Deployment Modes · Codebase Guide · Roadmap · Contributing
Why it matters: Guild Scroll captures the evidence trail that usually gets lost in fast-moving CTF or pentest work, making sessions easier to replay, export, and turn into clean reports.
You start a session, work normally, and Guild Scroll captures everything in the background. When you're done, you have a structured, searchable record you can export as Markdown, HTML, or an asciicast playback — and eventually feed to an AI to generate a writeup.
gscroll start htb-machine # begin recording
# ... do your work ...
gscroll export --format md # structured report, ready to share
| Category | Capability |
|---|---|
| Recording | Raw I/O + timing via script; [REC] colored prompt indicator |
| Command logging | Per-command metadata via zsh hooks: timestamp, exit code, working dir |
| Asset detection | Automatic capture of wget, curl, git clone, tar, unzip, and 40+ patterns |
| Tool tagging | 40+ security tools auto-classified as recon / exploit / post-exploit |
| MITRE ATT&CK | Each tool mapped to a MITRE technique ID |
| Annotations | Timestamped notes and tags, mid-session or post-session |
| Export | Markdown report, self-contained HTML, live web previews/downloads, asciicast v2 (.cast) |
| Search | gscroll search --tool nmap --phase recon --exit-code 0 |
| Validation | gscroll validate [SESSION] --repair checks JSONL/assets/parts and patches repairable metadata |
| Replay | gscroll replay via scriptreplay with speed control |
| TUI | Interactive Textual dashboard — session sidebar, phase timeline, command table |
| Web preview | gscroll serve hosts an HTML viewer + JSON API with full session CRUD (POST /api/sessions, DELETE, continue, validate) |
| Session auto-detect | All sub-commands pick up GUILD_SCROLL_SESSION automatically |
| Self-update | gscroll update checks GitHub and reinstalls |
graph TD
A["gscroll start"] -->|"launches script + injects zsh hooks"| B["script process\n(raw_io.log + timing.log)"]
A --> C["JSONL log\n(session.jsonl)"]
subgraph "Per-command (zsh preexec/precmd)"
D["CommandEvent"] --> C
E["AssetEvent"] --> C
F["NoteEvent"] --> C
end
B --> G["gscroll replay\n(scriptreplay)"]
C --> H["session_loader.py\n(LoadedSession)"]
H --> I["gscroll export\n(md / html / cast)"]
H --> J["gscroll search\n(SearchFilter)"]
H --> K["gscroll tui\n(Textual TUI)"]
H --> L["gscroll writeup\n(Claude SDK — M5)"]
sequenceDiagram
participant U as User
participant G as gscroll
participant S as script
participant Z as zsh hooks
participant L as session.jsonl
U->>G: gscroll start htb-machine
G->>S: launch script --log-out raw_io.log
G->>Z: inject preexec/precmd hooks
G->>L: write SessionMeta
loop Each command
U->>Z: runs a command
Z->>L: CommandEvent (cmd, exit_code, cwd, timestamps)
Z->>L: AssetEvent (if download/extract detected)
end
U->>G: gscroll note "got shell" --tag exploit
G->>L: NoteEvent
U->>G: gscroll export --format md
G->>L: read all events
G-->>U: session_report.md
For scenarios with multiple concurrent terminals (e.g. attacker shell + reverse shell listener):
graph LR
T1["Terminal 1\n(attacker shell)"] -->|"part 1"| S["Session: htb-machine"]
T2["Terminal 2\n(reverse shell)"] -->|"part 2"| S
T3["Terminal 3\n(listener)"] -->|"part 3"| S
S -->|"gscroll join"| M["Merged timeline\n(by timestamp)"]
M --> E["Export / TUI / Writeup"]
Guild Scroll runs in three ways — pick the one that matches your setup:
| Mode | Best for | Requires |
|---|---|---|
| Option A: Local install | Your own Linux/macOS machine | Python 3.11+, script (util-linux), zsh/bash |
| Option B: Existing container | Already inside Exegol, Kali, or another pentest image | Nothing extra — install Guild Scroll inside the container you already have |
| Option C: Managed Docker / Kubernetes | Fresh isolated environment, team lab, or CI | Docker 20.10+ or a Kubernetes cluster |
Already inside Exegol? Skip to Option B — no new container needed.
pipx install git+https://github.com/Panacota96/Guild-Scroll.gitpipx install "git+https://github.com/Panacota96/Guild-Scroll.git[tui]"git clone https://github.com/Panacota96/Guild-Scroll.git
cd Guild-Scroll
python3 -m venv .venv
source .venv/bin/activate
pip install -e '.[tui]'System prerequisites (usually pre-installed on Kali/Ubuntu):
# Debian / Kali
sudo apt install util-linux zsh
# Arch
sudo pacman -S util-linux zshNote for managed Python environments (Kali, Parrot): use
pipxor a venv — avoidpip install --break-system-packages.
See docs/docker/deployment-modes.md for full platform support matrix.
If you are already working inside a container that has Python 3.11+, script, and zsh/bash — you do not need to spin up a new container. Just install Guild Scroll directly inside the running environment:
# 1. Verify prerequisites (usually already present in Exegol/Kali)
python3 --version # must be ≥ 3.11
script --version # from util-linux ≥ 2.35
which zsh || which bash
# 2. Install Guild Scroll
pipx install git+https://github.com/Panacota96/Guild-Scroll.git
# 3. Point sessions at a bind-mounted volume so data survives container restarts
export GUILD_SCROLL_DIR=/recordings # or any volume-mounted path
# 4. Start recording
gscroll start htb-machinePersist sessions across container restarts by mounting a host path:
# When starting Exegol (example — adapt to your launcher)
exegol start htb --volume /host/pentest-sessions:/recordings
# Then inside the container
export GUILD_SCROLL_DIR=/recordings
gscroll start htb-machineSee the full Existing Container Guide for Exegol-specific steps, troubleshooting, and hook compatibility notes.
Spin up a clean, fully-isolated environment with Guild Scroll pre-configured — no host dependencies required. This starts two containers: a recording shell (Debian + pentest tools) and a Guild Scroll app (web UI + CLI):
# Clone and start
git clone https://github.com/Panacota96/Guild-Scroll.git
cd Guild-Scroll
docker-compose up -d
# Access web UI
open http://localhost:8080 # or curl http://localhost:8080/api/sessions
# Access recording shell
docker-compose exec kali-recorder zsh
# Work normally — all commands auto-recorded
nmap -sV target.com
exitFor Kubernetes deployments:
kubectl apply -k k8s/
kubectl port-forward -n guild-scroll svc/guild-scroll-app 8080:8080
kubectl exec -it -n guild-scroll kali-recorder-0 -- zshSee DOCKER.md for full Docker + Kubernetes guide, including troubleshooting, custom tools, and volume configuration.
# Start a new session
gscroll start htb-machine
# Add a note (auto-detects active session inside a recording)
gscroll note "found open port 80 — Apache 2.4" --tag recon
# Search commands
gscroll search --phase recon
gscroll search --tool nmap --exit-code 0
# Validate integrity and repair session metadata
gscroll validate htb-machine --repair
# Export
gscroll export --format md
gscroll export --format html -o report.html
gscroll export --format cast # asciicast (asciinema-compatible)
# Replay
gscroll replay
gscroll replay --speed 2.0
# Interactive TUI dashboard
gscroll tui htb-machine
# Local web preview
gscroll serve
# List all sessions
gscroll list
# Update to latest
gscroll updategscroll start <name>creates./guild_scroll/sessions/<name>/and launchesscriptto record raw I/O.- Zsh hooks (
preexec/precmd) write aCommandEventfor every command — with timestamp, exit code, and working directory. - The hook parser scans each command for download/extract patterns and writes
AssetEvententries automatically. gscroll noteappends aNoteEventto the log at any point.gscroll exportloads all events, auto-tags each command by security phase, and renders the chosen format.
| Area | Implementation |
|---|---|
| Language/runtime | Python 3.11+ |
| CLI | Click |
| Terminal recording | script / scriptreplay from util-linux |
| Shell integration | zsh preexec / precmd hooks |
| Storage format | JSONL event log + raw terminal I/O logs |
| Export targets | Markdown, HTML, asciicast v2, Obsidian |
| Optional UI | Textual TUI |
| Dependency policy | stdlib-first; core runtime only depends on click |
| Path | Purpose |
|---|---|
src/guild_scroll/ |
Main package: CLI, session management, log schema, exporters, validation, replay, sharing, and web/TUI entrypoints |
src/guild_scroll/exporters/ |
Format-specific exporters for Markdown, HTML, asciicast, and Obsidian |
src/guild_scroll/tui/ |
Optional Textual dashboard components |
src/guild_scroll/web/ |
Local preview server package (app.py) and related web helpers |
tests/ |
Pytest suite covering CLI flows, schema compatibility, exporters, merge logic, hooks, and validation |
docs/context-engineering/ |
Project-specific design notes for tool/agent workflows |
.github/instructions/ |
Shared contributor rules for Python, CLI implementation, and release prep |
.github/skills/ |
Reusable workflows such as /issue and /release |
cli.pyis the entrypoint and keeps command imports lazy so optional features do not slow startup or create circular imports.session.py,session_loader.py,recorder.py, andhooks.pyown the recording lifecycle: start a session, attach shell hooks, and resolve session data later.log_schema.pyandlog_writer.pydefine the JSONL event model used across recording, export, replay, and validation.asset_detector.py,tool_tagger.py,analysis.py, andsearch.pyenrich command history with security-specific metadata.exporters/turns a loaded session into shareable outputs;validator.pyandmerge.pykeep session data consistent across repairs and multi-terminal workflows.sharing.py,web/app.py,updater.py, andtui/are feature layers built on top of the same loaded-session primitives.
session_metastores high-level session state such as name, timestamps, hostname, platform, and part count.commandrecords the executed command, timing, exit code, working directory, and terminal part number.assetcaptures downloads, extracts, clones, and other artifacts detected from command history.notestores manual annotations added during or after a session.screenshotis reserved for automation workflows that attach captured images to the session log.
Sessions are stored under ./guild_scroll/sessions/<name>/ (CWD-local, like .git/):
<name>/
├── logs/
│ ├── session.jsonl # all structured events (JSONL, one per line)
│ ├── raw_io.log # raw terminal I/O (scriptreplay source)
│ └── timing.log # timing data for scriptreplay
└── assets/ # captured files
Override the base path with GUILD_SCROLL_DIR.
Set GUILD_SCROLL_ALLOW_REMOTE=1 to allow the report server to bind to non-localhost addresses (required for Docker/container deployments that use --host 0.0.0.0). Localhost-only is the default.
gscroll serve exposes a JSON API under /api/. Key endpoints:
| Method | Path | Description |
|---|---|---|
GET |
/api/sessions |
List all sessions |
GET |
/api/session/{name} |
Fetch session detail (commands, notes, assets) |
POST |
/api/sessions |
Create a session scaffold ({"name": "..."}) → 201/409/422 |
DELETE |
/api/session/{name} |
Delete a session directory → 204/404/400 |
POST |
/api/session/{name}/continue |
Scaffold a new session part → {"part": N} |
POST |
/api/session/{name}/validate |
Validate (and optionally repair with ?repair=true) → {valid, errors, warnings, repaired} |
POST |
/api/session/{name}/report |
Render a filtered export (body: {"format": "md|html", ...}) |
GET |
/api/session/{name}/download |
Download session export (?format=md|html) |
GET |
/api/session/{name}/discoveries |
Fetch recent notes/assets timeline |
| Type | Key Fields |
|---|---|
session_meta |
session_name, session_id, start_time, hostname, end_time, command_count |
command |
seq, command, timestamp_start, timestamp_end, exit_code, working_directory |
asset |
seq, trigger_command, asset_type, captured_path, original_path, timestamp |
note |
text, timestamp, tags |
- Terminal session recording via
script - Zsh hook injection (preexec/precmd) for command logging
- JSONL structured logs (SessionMeta, CommandEvent, AssetEvent)
- Automatic asset detection
- Session management (start, list, status)
- Self-update command
-
NoteEvent— timestamped annotations with tags - Security tool auto-tagger (recon / exploit / post-exploit, 40+ tools)
-
gscroll export— Markdown, self-contained HTML, asciicast v2 -
gscroll replay— terminal replay with speed control - CWD-local session storage
- Attack phase timeline (recon → exploit → post-exploit)
- MITRE ATT&CK mapping
-
gscroll tui— Textual TUI dashboard -
gscroll search— filter by tool, phase, exit code, cwd -
[REC]colored prompt indicator - Auto-detect active session
- Multi-session parts — join multiple terminals (reverse shell, listener, attacker) into one session as timestamped parts;
gscroll joinmerges for export - Obsidian vault export with wikilinks and tags
- CTF platform detection (HTB/THM network auto-detect)
- Auto-screenshot on key events (flags, root shells) (pending: capture strategy)
- Session sharing/import (archive + restore)
- Bash hook support (PROMPT_COMMAND + trap DEBUG)
- Claude SDK writeup generation —
gscroll writeup <session> --ai claudesends session history to Claude API and outputs a structured CTF/pentest writeup (pending: API integration design) - Screenshot attachment — embed captured screenshots into Markdown/HTML exports (pending: screenshot storage format)
- Attack graph visualization (Graphviz/Mermaid)
- Web dashboard (
gscroll web)
- VS Code extension
- PyPI publication
- Kali Linux / BlackArch package submission
- Sub-agent and skill integration for automated note creation during sessions
- Claude Code hooks that trigger agents on session events (flag captured, root shell detected)
- Scheduled session summarization via remote agents
- Skills for developer documentation and architecture diagram generation (pending: design)
Contributions, bug reports, and feature requests are welcome.
To contribute:
- Fork the repository
- Create a feature branch:
git checkout -b feat/your-feature - Write tests first (TDD), then implementation
- Run the test suite:
PYTHONPATH=src python3 -m pytest tests/ -v - Open a pull request — PRs are reviewed before merging to
main
Guidelines:
- No external dependencies beyond
clickin core code - Follow the existing dataclass patterns (
to_dict()/from_dict()withtype-first serialization) - Keep CLI lazy imports (all imports inside command function bodies)
Developer references:
- Quick project overview:
CLAUDE.md - Shared repository rules:
.github/copilot-instructions.md - Auto-loaded implementation guidance:
.github/instructions/ - Design/context notes:
docs/context-engineering/ - Deployment mode docs:
docs/docker/andDOCKER.md
High-value documentation issues:
- Architecture deep-dive for the recording pipeline, JSONL schema, and multi-session merge flow
- Infrastructure/release guide covering version sync, changelog expectations, and contributor release workflow
- Exporter extension guide for adding or maintaining output formats
- Testing guide for fixtures, CLI coverage patterns, and integration-style session tests
Shared Copilot customizations:
- Workspace guidance:
.github/copilot-instructions.md - Auto-loaded instructions:
.github/instructions/ - Shared agents:
.github/agents/tdd-enforcer.agent.md,.github/agents/release-manager.agent.md,.github/agents/docs-maintainer.agent.md - Shared skills:
.github/skills/issue-from-template/SKILL.md(/issue),.github/skills/release-cycle/SKILL.md(/release), and.github/skills/doc-sync/SKILL.md(/doc-sync) - Version-check hook:
.github/hooks/version-check.jsondocuments the pre-commit version sync check
Found a bug or have an idea? Open an issue.
If Guild Scroll saved you time on a CTF or pentest engagement, consider buying me a coffee:
Guild Scroll is intended for authorized security testing, CTF competitions, and educational purposes only. Always ensure you have explicit authorization before conducting security assessments.
MIT