We’re pushing PentLog beyond simple recording. The next frontier is Real-Time Collaboration.

Penetration testing is often a team sport. Yet, sharing a terminal session usually involves clunky solutions like tmux over shared SSH keys (security nightmare) or third-party services like tmate (privacy risk).

We need something native, secure, and lightweight.

The Challenge: “Don’t Break the Shell”

The cardinal rule of PentLog is Evidence Integrity. If the logging tool crashes, the shell must survive. If the network lags, the typing must not delay.

Connecting a live viewer directly to the PTY Master (the pseudo-terminal process) is risky. A slow WebSocket client could block the Write() call, freezing the attacker’s shell. Unacceptable.

The Solution: Tail-Based Sync

Instead of intercepting the PTY stream, we chose a Decoupled Architecture:

flowchart LR subgraph "Recording Process" ttyrec["ttyrec -a -f session.tty"] end subgraph "pentlog share" tailer["Tailer
(poll .tty file)"] parser["TtyrecParser
(extract payloads)"] hub["Hub
(fan-out broadcaster)"] srv["HTTP Server
(/watch + /ws)"] end subgraph "Viewers (Browser)" c1["Client 1
xterm.js"] c2["Client 2
xterm.js"] end ttyrec -->|writes frames| tailer tailer -->|raw bytes| parser parser -->|payload only| hub hub -->|WebSocket binary| c1 hub -->|WebSocket binary| c2 srv -.->|upgrade| c1 srv -.->|upgrade| c2
  1. The Shell Process (Producer):

    • Writes standard ttyrec frames to disk (session.tty).
    • Does not know about any network viewers.
    • Zero overhead, zero blocking.
  2. The Share Process (Consumer):

    • Runs as a separate goroutine/process (pentlog share).
    • Tails the session.tty file in real-time (100ms polling).
    • Parses new frames and broadcasts them via WebSocket.

The Viewer: xterm.js

On the client side (browser), we use xterm.js. It receives the raw PTY stream and renders the state locally.

This means the server (PentLog) is dumb. It just shovels bytes. The client handles the complex terminal emulation (colors, cursor movements, ncurses apps).

Security First

  • Read-Only: Viewers can watch, but never type.
  • Tokenized Access: Every shared session generates a random 128-bit token in the URL.
  • Local Network: By default, it binds to 0.0.0.0 but is intended for VPN/LAN usage, keeping sensitive engagement data off the public internet.

Conclusion

This architecture gives us the best of both worlds: Real-Time Visibility for the team, with Zero Risk to the active operator.

Code implementation is underway.