Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,10 @@ The first engagement, in the order that proves capture works:
to open a **new** terminal and run the `echo redlog-ok-…` it shows, and
confirms only when that command arrives — shells that were already open are
not hooked. (Later: **Capture Health ▸ Install shell hook** does the same.) From then on every command you type
there is recorded as metadata: command, exit code, duration and working
directory. Output is not; for that use the built-in terminal,
`redlog-run <cmd>`, or a `redlog-session` recorded shell.
there is recorded — **in zsh, with its output**, and with nothing typed in
front of it. `redlog status` says whether this terminal is recording,
`redlog stop` stops it until you start it again, and `redlog class list`
shows what is deliberately left alone.
4. **HTTP capture.** **Start HTTP capture** on the Dashboard or the
first-run screen (or Settings ▸ Browser & HTTP capture) runs a local
mitmproxy with RedLog's addon; the one-click proxied browser goes through
Expand Down Expand Up @@ -121,7 +122,8 @@ Windows terminal output. The full walkthrough, in Traditional Chinese, is the

Penetration testers need a tamper-evident record of what they did during an engagement. RedLog runs in the background and records it into a per-project SQLite database, timestamped and hash-chained. What it records, and what it does not, is stated rather than implied:

- **Commands** from any shell with the hook installed: the command, exit code, duration and working directory. **Output** is recorded only by the built-in terminal, `redlog-run <cmd>`, or a `redlog-session` shell.
- **Commands** from any shell with the hook installed: the command, exit code, duration and working directory. In **zsh** the output comes too, automatically, with no prefix to remember. In bash it does not — use `redlog-run <cmd>`, the built-in terminal, or a `redlog-session` shell.
- **What zsh deliberately leaves alone**, because capturing it would cost more than it is worth: `nc` and `ncat` keep local suspension, so the Ctrl-Z → `stty raw -echo` → `fg` upgrade still works; editors and pagers would record redraws and none of the file. `ssh`, `socat` and `pwncat-cs` get a real PTY recorder instead. Each such command is still recorded, marked `metadata-only` with the reason — never an empty `stdout` that reads as "this printed nothing". `redlog class list` shows the lists; `redlog class add|remove` changes them.
- **HTTP(S)** that goes through RedLog's proxy — the capture browser, or a tool pointed at the proxy. Traffic that does not use the proxy (raw TCP, SMB, nmap SYN scans) is not recorded as HTTP.
- **Optional sources**, off until you turn them on: process, connection and file monitoring; the clipboard (its own opt-in); AI agent transcripts; periodic screenshots.

Expand Down Expand Up @@ -362,9 +364,11 @@ Overlay Window
└── IP status always-on-top widget (click-through + draggable)

Hooks
├── shell-zsh-hook.zsh zsh preexec/precmd integration
├── shell-zsh-hook.zsh zsh preexec/precmd, output relay, the `redlog` command
├── shell-bash-hook.sh bash DEBUG/PROMPT_COMMAND integration
└── shell-common.sh shared transport, spool and redlog-run
├── shell-common.sh shared transport, spool and redlog-run
├── redlog-relay.py the output relay, the classifier, per-terminal state
└── command-class.json which commands are relayed, PTY-recorded, or left alone

Plugin hooks
└── pack-ai-agents/hooks/codex-wrapper.sh Shell wrapper for Codex/GPT
Expand Down Expand Up @@ -407,9 +411,11 @@ src/
styles/index.css Tailwind + custom scrollbar
env.d.ts TypeScript declarations for preload API
hooks/
shell-zsh-hook.zsh zsh preexec/precmd → RedLog
shell-zsh-hook.zsh zsh preexec/precmd → RedLog, with output
shell-bash-hook.sh bash DEBUG/PROMPT_COMMAND → RedLog
shell-common.sh shared POSIX transport and redlog-run
redlog-relay.py output relay, classifier, per-terminal state
command-class.json relayed / pty / native, one list for both readers
plugins/
pack-ai-agents/
hooks/codex-wrapper.sh shell wrapper for any agent
Expand Down
43 changes: 39 additions & 4 deletions docs/USER-GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ RedLog 裝在**你發動攻擊的那台機器**(Kali、筆電),不是裝

- **指令**:最快的驗證是在內建終端機打一道指令(例如 `echo redlog-ok`),收到就算
驗證通過。內建終端機連輸出一起錄(asciinema `.cast`)。也可以直接接上你自己的終端
(見下一節),那裡的指令同樣算數。
(見下一節),那裡的指令同樣算數——zsh 的話連輸出一起。
「已安裝」不等於「在記錄」:卡片要等到**你自己的終端機**真的送來一道指令才會說它納入了,
因為安裝只證明檔案複製過去了,不證明 rc 被重讀、adapter 被載入、傳輸接得到 RedLog。
- **HTTP(S)**:按「開始 HTTP 擷取」,再按「用擷取瀏覽器驗證」,或在你實際會用的工具裡跑畫面
給的 `curl -x … http(s)://redlog.verify.invalid/<驗證碼>`。只有帶著這次驗證碼的請求才算數,
瀏覽器自己的背景流量不算;HTTP 與 HTTPS 分開驗證,並標出是哪個 client 通過的。這個網址由
Expand All @@ -58,9 +60,42 @@ RedLog 裝在**你發動攻擊的那台機器**(Kali、筆電),不是裝
首次畫面的指令區塊有「記我的 Zsh/Bash/PowerShell 終端」:它先檢查
python3 與 curl,在 shell 的啟動檔加一行,請你**開一個新的終端機**並貼上畫面給的
`echo redlog-ok-…`,收到那道指令才算連上——已經開著的 shell 不會被掛上。之後也可以從
儀表板「擷取健康度」卡 ▸「安裝 shell hook」做同一件事。之後在那裡打的每道指令都會記下**中繼資料**:指令、
結束碼、耗時、工作目錄;**不含輸出**。要連輸出一起留,用內建終端機、在指令前加
`redlog-run`,或用 `redlog-session` 開一個錄製中的 shell。
儀表板「擷取健康度」卡 ▸「安裝 shell hook」做同一件事。之後在那裡打的每道指令都會記下指令、
結束碼、耗時、工作目錄——**在 zsh 底下連輸出一起記,而且前面什麼都不用打**。
bash 沒有(它沒有 `preexec`),要連輸出就用 `redlog-run <指令>`、內建終端機,或
`redlog-session` 開一個錄製中的 shell。

那個終端機裡多了一個 `redlog` 指令:

| | |
|---|---|
| `redlog status` | 這個終端機在不在記錄、什麼模式、綁在哪個專案 |
| `redlog stop` | 停掉**這個**終端機的記錄,會一直停著,直到你 `redlog start` |
| `redlog mode auto\|manual` | 新開的終端機預設要不要記錄(預設 `auto`) |
| `redlog class list\|add\|remove` | 哪些指令被刻意放過(見下) |

停掉不是靜音:紀錄裡會留下一對 `capture_stopped` / `capture_resumed` 把那段空白框起來,
所以一年後看紀錄的人分得出「operator 把它關了」和「operator 那段時間在讀東西」。

#### 有些指令是刻意不記的

這不是漏掉,是權衡過的:

- **`nc`、`ncat` 完全不碰。** 經過 relay 的 `nc` 會讓你失去本機暫停,
`Ctrl-Z` → `stty raw -echo` → `fg` 那套升級互動 shell 的做法就壞了——
而那通常發生在你花了兩天才打進去的機器上。
- **編輯器和 pager(`vim`、`nano`、`less`…)也不碰。** 記下來的會是游標移動和重繪,
不是檔案內容。
- **`ssh`、`socat`、`pwncat-cs` 改用真的 PTY 錄製器**(`redlog-session`),
它們自己帶 TTY,這樣錄才完整。

這些指令**還是會進紀錄**,只是標成 `metadata-only` 並寫明原因,不會是一個空的 `stdout`
讓人誤讀成「這道指令沒有輸出」。清單在 設定 ▸ Hooks 看得到,也可以用
`redlog class list` 列出來、`redlog class add|remove` 調整。把指令加進 PTY 那一類時
會提醒你:那會失去本機暫停。

RedLog 連不上的時候,指令照跑,事件會先存在 `~/.redlog/pending`,下次開專案時補回來,
**而且帶著原本發生的時間**,不是補回來那一刻的時間。終端機只會提醒你一次,不會每個提示符都唸。

### 4. HTTP 擷取
儀表板「開始 HTTP 擷取」(或 設定 ▸ 瀏覽器與 HTTP 擷取)會啟動一個掛著 RedLog addon 的
Expand Down
75 changes: 75 additions & 0 deletions docs/domain/SPEC-capture-source-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,49 @@ state a proxy with no traffic is in: working, and waiting for the operator.
Grading that amber is the same lie as a green dot over a dead hook, in the
other direction.

### One source, many terminals

The states above are a source's. Spec 052 added a second axis underneath one of
them, and conflating the two would undo most of this document.

The `terminal` source is `Verified` when any terminal has recorded a command.
But "the terminal source can record" and "**this** terminal is recording" are
different questions, and the operator asks the second one:

```
enrolled / not this machine's shells load the adapter, or they do not
recording this terminal is recording right now
stopped `redlog stop` in this terminal, durably — survives the
next prompt, and a subshell, because it is a file
mode auto|manual what a NEW terminal starts as, machine-wide
project-switched RedLog moved to another project under this terminal, so it
stopped and will not restart (see below)
```

Three rules keep the axes from collapsing into each other:

- **A per-terminal stop is not a source failure.** The `terminal` row stays
`ready`; what changed is one terminal's answer. Grading the source amber
because an operator stopped one shell is the same mistake as `Idle`, with a
different cause.
- **The state is a file, not a variable.** `~/.redlog/terminals/<id>.json`,
read by the shell at each prompt and by RedLog for the card. Two readers, one
answer — a terminal that says "stopped" while the card says "recording" is
worse than either answer alone.
- **A stop is a gap with two ends, not silence.** `capture_stopped` /
`capture_resumed` bracket it, carrying the terminal and the reason. "No
events for twenty minutes" reads very differently as *the operator stopped
recording* than as *the operator was reading*, and a reader a year later
cannot tell either from nothing at all.

**`project-switched` is the one that cannot be undone from the terminal.** A
terminal is pinned to the project it was opened against; when RedLog opens
another one, the terminal stops and `redlog start` will not restart it. The
failure that prevents is a command from engagement A filed under engagement B
— not a missing command, but a lie in a document a client reads, written by
the tool whose whole job is to be believable. The safe answer is a new
terminal, and it is cheap.

## Checklist

Any change that adds or alters a capture source answers all of these.
Expand Down Expand Up @@ -109,13 +152,45 @@ Any change that adds or alters a capture source answers all of these.
- [ ] The UI says what this source records.
- [ ] The UI says what it does **not** record. Metadata-only capture that
looks like full capture is the same lie in a quieter form.
- [ ] Where capture is deliberately declined, the **list of what is declined is
visible to the operator**, not only in the code.

Spec 052 is the case that forced this line. Terminal capture leaves a
`native` class of commands completely alone — `nc`, `ncat`, editors and
pagers — and it is not an oversight: a relayed `nc` costs the operator
the Ctrl-Z / `stty raw -echo` / `fg` upgrade in the middle of an
engagement (FR-025), and a relayed `vim` records redraws and none of the
file (FR-026). Both are the right call and both are invisible from the
timeline, because what they produce is an *absence*.

So the class lists are shown in Settings ▸ Hooks and readable with
`redlog class list`, and each such command is recorded
`completeness: metadata-only`, `output_disposition: interactive` — the
row says the body is missing and why, instead of an empty `stdout` that
reads as "this command printed nothing".
- [ ] Capture that was declined, failed, redirected or stopped are four
different words in the record, not one empty field. `interactive` is a
decision, `not-captured` is a failure, `redirected` is the operator's own
doing, and a stop is bracketed. Collapsing them makes every deliberate
silence look like a broken capture, and every broken capture look
deliberate.

### Cleanup

- [ ] Uninstall or disable has a path.
- [ ] A source RedLog cannot uninstall carries removal steps.
- [ ] Uninstall leaves nothing behind — no empty file, no directory that was
not there before.
- [ ] Where setup edits a file RedLog does not own, **uninstall returns it
byte-identical**, and a test proves it as an inverse rather than by
inspection.

The shell-source install appends a block to the operator's `.zshrc`.
Uninstall used to replace that block, leading newline included, with a
single newline — so every install/uninstall cycle left one more blank
line behind, and a file that had not ended with a newline came back with
one. Nobody would have noticed, and a red-team tool has no business
leaving a footprint on the machine it was run from.

### External contract

Expand Down
Loading
Loading