Summary
The Stop hook sends captured transcript text to the memory API with no secret redaction at all. Anything typed into the conversation — an API key pasted while debugging, a connection string, a token from a log — leaves the machine verbatim and is stored as a memory document.
There is no setting to redact or exclude patterns, and no redaction code anywhere in plugin/ (grep -rin "redact" plugin/ returns nothing). Plugin version 0.1.6.
Repro
Point the plugin at a local server instead of the API and run the capture hook directly:
git clone --depth 1 https://github.com/supermemoryai/claude-supermemory
cd claude-supermemory
cat > /tmp/transcript.jsonl <<'JSONL'
{"type":"user","uuid":"u1","message":{"role":"user","content":"deploy is broken. i used OPENAI_API_KEY=sk-proj-FAKEKEYFORREPRO0000000000000000 and DATABASE_URL=postgres://admin:FAKEPASSWORD@10.0.0.5:5432/prod — what is wrong?"}}
{"type":"assistant","uuid":"a1","message":{"role":"assistant","content":[{"type":"text","text":"Rotate that key immediately, then check the host."}]}}
JSONL
# minimal sink that prints what it receives on :8787
node -e 'require("http").createServer((q,s)=>{let b="";q.on("data",c=>b+=c);q.on("end",()=>{console.log(q.method,q.url);console.log(b);s.end("{\"id\":\"doc_fake\"}")})}).listen(8787,"127.0.0.1")' &
echo '{"session_id":"repro-1","transcript_path":"/tmp/transcript.jsonl","cwd":"'"$PWD"'"}' \
| SUPERMEMORY_CC_API_KEY=sm_fake \
SUPERMEMORY_API_URL=http://127.0.0.1:8787 \
node plugin/hooks/capture.js
Received body (POST /v3/documents), unmodified:
{
"content": "<|turn_start|>2026-09-09T17:34:26.994Z\n\n<|start|>user<|message|>deploy is broken. i used OPENAI_API_KEY=sk-proj-FAKEKEYFORREPRO0000000000000000 and DATABASE_URL=postgres://admin:FAKEPASSWORD@10.0.0.5:5432/prod — what is wrong?<|end|>\n\n<|start|>assistant<|message|>Rotate that key immediately, then check the host.<|end|>\n\n<|turn_end|>",
"containerTag": "repo_claude_supermemory__6b8d7c204683aa6a",
"metadata": { "type": "session_turn", "sm_capture_mode": "automatic", "...": "..." }
}
Both the fake key and the fake password arrive intact.
Where this happens
plugin/hooks/lib/settings.js:7 — BASE_URL = 'https://api.supermemory.ai' is the default sink.
plugin/hooks/capture.js:50-72 — builds the delta and posts it on every Stop.
plugin/hooks/lib/transcript.js:98-107 — user text goes through cleanContent() only, and is not truncated.
plugin/hooks/lib/transcript.js:183-190 — cleanContent() strips <system-reminder> and <supermemory-context> blocks. That is the entire sanitisation step.
- Tool payloads are length-limited but not filtered:
transcript.js:177 (tool input, 100 chars per field) and transcript.js:10/115-118 (tool result, 500 chars).
Defaults make this the common path rather than an edge case: signalExtraction defaults to false (settings.js:17), so formatNewEntries captures every user and assistant turn, not just signal turns.
Why it is worth fixing even if the backend is trusted
This is not only about what crosses the network. A captured secret becomes a memory document, gets fact-extracted, and can then be re-injected into later sessions through recall and profile context — including sessions in a different repository, since the container is per-repo but the profile is not. A key pasted once during debugging can resurface in an unrelated conversation weeks later. Client-side redaction keeps it out of the store in the first place, where deleting it afterwards is far harder.
The README's Privacy section currently just links to the privacy policy; nothing tells the user that raw prompt text is captured verbatim by default.
Suggested minimal fix
- Add a redaction pass inside
cleanContent() (or a new redact() called from it) covering the common shapes: sk-/sk-proj-/ghp_/gho_/github_pat_, AWS AKIA…, Bearer <token>, xox[baprs]-, JWTs, and URLs carrying user:password@. Replace with [REDACTED] rather than dropping the message, so the memory stays useful.
- Make it configurable in
~/.supermemory-claude/settings.json: redactPatterns (extra regexes) and redactionEnabled (default true), matching the existing settings style.
- Document the behaviour in the README's Privacy section — one line stating what is captured and how to extend the pattern list.
Happy to send a PR for 1 and 2 if the approach looks right to you.
Summary
The
Stophook sends captured transcript text to the memory API with no secret redaction at all. Anything typed into the conversation — an API key pasted while debugging, a connection string, a token from a log — leaves the machine verbatim and is stored as a memory document.There is no setting to redact or exclude patterns, and no redaction code anywhere in
plugin/(grep -rin "redact" plugin/returns nothing). Plugin version 0.1.6.Repro
Point the plugin at a local server instead of the API and run the capture hook directly:
Received body (
POST /v3/documents), unmodified:{ "content": "<|turn_start|>2026-09-09T17:34:26.994Z\n\n<|start|>user<|message|>deploy is broken. i used OPENAI_API_KEY=sk-proj-FAKEKEYFORREPRO0000000000000000 and DATABASE_URL=postgres://admin:FAKEPASSWORD@10.0.0.5:5432/prod — what is wrong?<|end|>\n\n<|start|>assistant<|message|>Rotate that key immediately, then check the host.<|end|>\n\n<|turn_end|>", "containerTag": "repo_claude_supermemory__6b8d7c204683aa6a", "metadata": { "type": "session_turn", "sm_capture_mode": "automatic", "...": "..." } }Both the fake key and the fake password arrive intact.
Where this happens
plugin/hooks/lib/settings.js:7—BASE_URL = 'https://api.supermemory.ai'is the default sink.plugin/hooks/capture.js:50-72— builds the delta and posts it on everyStop.plugin/hooks/lib/transcript.js:98-107— user text goes throughcleanContent()only, and is not truncated.plugin/hooks/lib/transcript.js:183-190—cleanContent()strips<system-reminder>and<supermemory-context>blocks. That is the entire sanitisation step.transcript.js:177(tool input, 100 chars per field) andtranscript.js:10/115-118(tool result, 500 chars).Defaults make this the common path rather than an edge case:
signalExtractiondefaults tofalse(settings.js:17), soformatNewEntriescaptures every user and assistant turn, not just signal turns.Why it is worth fixing even if the backend is trusted
This is not only about what crosses the network. A captured secret becomes a memory document, gets fact-extracted, and can then be re-injected into later sessions through recall and profile context — including sessions in a different repository, since the container is per-repo but the profile is not. A key pasted once during debugging can resurface in an unrelated conversation weeks later. Client-side redaction keeps it out of the store in the first place, where deleting it afterwards is far harder.
The README's Privacy section currently just links to the privacy policy; nothing tells the user that raw prompt text is captured verbatim by default.
Suggested minimal fix
cleanContent()(or a newredact()called from it) covering the common shapes:sk-/sk-proj-/ghp_/gho_/github_pat_, AWSAKIA…,Bearer <token>,xox[baprs]-, JWTs, and URLs carryinguser:password@. Replace with[REDACTED]rather than dropping the message, so the memory stays useful.~/.supermemory-claude/settings.json:redactPatterns(extra regexes) andredactionEnabled(defaulttrue), matching the existing settings style.Happy to send a PR for 1 and 2 if the approach looks right to you.