An audit trail without a second copy of the chart
Compliance asks "who ran what, when." MedSeek answers without storing any note text: one receipt per tool execution, digests only.
What one receipt holds - and only this
{
"seq": 12, "prevHash": "ab41...", "hash": "9f02...",
"ts": "2026-09-08T07:31:04.221Z",
"tool": "pubmed_evidence_search", "callId": "call-88",
"isError": false,
"argsHash": "c3aa...", "resultHash": "77be..."
}Sequence number, previous receipt's hash, timestamp, tool name, call id, error flag, and SHA-256 digests of arguments and result content. No note text, no query strings, no result bodies.
Tamper-evident by construction
Each line binds to the previous line's hash. Editing or deleting any historical line
breaks every hash after it. Verification is a few lines against the published
dsh-medseek/audit export:
import { readFileSync } from 'node:fs'
import { verifyChain } from 'dsh-medseek/audit'
const lines = readFileSync(process.env.DSH_HOME + '/medseek-audit.jsonl', 'utf8')
.split('\n').filter((l) => l.trim())
console.log(verifyChain(lines)) // null = intact; number = first broken lineSafe under real deployments
- Cross-process: appends serialize through upstream's bounded writer lock (a
<file>.locksibling with a hard timeout), so two writers can never mint the same sequence or chain position. - Fail-closed: before appending, the tail is re-read and re-verified. A torn, unreadable, or tampered tail means the receipt is dropped with an operational warning - never a silent fresh chain inside the damaged file. Move the file aside deliberately to start anew.
- Never blocking: receipts are written off the tool-execution path; auditing failing never breaks a clinical task.
medseek-audit:). Route stderr to your
log infrastructure if drop visibility matters.Where it lives
medseek-audit.jsonl plus its lock sibling sit in the same dsh
home root dsh itself resolves (explicit override > $DSH_HOME >
~/.dsh). Disable entirely with
id: medseek-audit, config: { mode: off }. Session logs are dsh's
own store and are a separate matter - see the plugin's permissions-and-data documentation.
Design and tests: src/audit.ts,
tests/audit.spec.ts,
tests/audit-concurrency.spec.ts (three concurrent OS processes,
one intact chain).