- Document
- IQC-AG-01
- Revision
- Draft 9
- Updated
- 2026-10-18
- Status
- Independent project · Open alpha · No customers yet
- Use
- Informational draft. Not a controlled document; not a regulatory assessment or a validation statement.
For AI agents
1What it is
Tamper-evident records for automated research labs. A one-file local agent seals every file an instrument writes into a signed ledger, records what AI agents read and wrote, and serves check_inputs, record_step, trace and audit_summary to AI agents over MCP on standard input and output. Nothing is uploaded; it never uses the network.
This page is for an AI agent that works with lab data, and for the people who build such agents and pipelines: what the agent and its MCP server do today, how to run and connect them, and their limits. The same text is in llms.txt (short) and llms-full.txt (everything on one page).
2What works today, and what is upcoming
- The agent · iqc.mjs 0.1.0 One file you run with Node.js 22 or later, on Windows, macOS and Linux. No dependencies; it never uses the network, and it refuses to run on an older Node.js.
- watch, seal Seal every file in a folder by its bytes, from any instrument software: its SHA-256 fingerprint, size and time, each record signed (ECDSA P-256) and linked to the one before, in
<folder>/ImmutableQC/ledger.jsonl. - record, trace, review Record an AI agent’s step (what it read, what it wrote, what it did) in the agent’s own ledger, trace a file back to sealed files, and sign off steps.
- audit Check every ledger and every file under a lab folder: exit 0 pass, 3 review, 1 fail, 2 usage or read error.
- mcp The same for AI agents, over MCP on standard input and output:
check_inputs,record_step,traceandaudit_summary. - The app · in your browser Audits a whole lab folder in one click with nothing uploaded, traces agent outputs back through recorded steps to sealed files, sends untraced inputs to review, and has a review mode for sign-off. The demo shows a pass, a review and a fail.
Upcoming An npm package; a Windows installer and service; values read from inside files; per-person keys and signatures; Merkle batches; sealing inside the vendor’s software; post-quantum signatures.
3Run it
Download iqc.mjs (0.1.0), check its SHA-256, then run it. Its SHA-256 is:
bf946887b5c62c83ec630dbd9a0641890b622572cbfc789d702a0ac04f41895a
curl -fsSO https://immutableqc.com/download/iqc.mjs
echo "bf946887b5c62c83ec630dbd9a0641890b622572cbfc789d702a0ac04f41895a iqc.mjs" | sha256sum -c - # Linux: prints OK, or fails
echo "bf946887b5c62c83ec630dbd9a0641890b622572cbfc789d702a0ac04f41895a iqc.mjs" | shasum -a 256 -c - # macOS: prints OK, or fails
certutil -hashfile iqc.mjs SHA256 # Windows: compare it with the line above
If the two differ, do not run the file. Only once the check passes:
node iqc.mjs help
Then, on the computer that can read the instrument’s data folder:
node iqc.mjs watch /lab/hplc-3 # seal now, then keep sealing until Ctrl+C
node iqc.mjs seal /lab/hplc-3 # seal once, for a scheduler or a pipeline
node iqc.mjs audit /lab --report audit.html
A file is sealed once it has not changed for --settle seconds (10 by default). --ledger <path> keeps the ledger outside a folder the instrument software manages.
4Connect the MCP server
Any MCP client that starts local servers over standard input and output can run it. The server entry, as JSON:
{"command": "node", "args": ["/path/to/iqc.mjs", "mcp", "--root", "/lab/agent-workspace", "--search", "/lab"]}
- --root The folder the agent works in. Every path a tool takes is relative to it and must be inside it.
- --search Where the instruments’ ledgers are: give the lab folder. Without it, a file sealed by a station outside the root reads as untraced.
- What starting writes
<root>/ImmutableQC/, the agent’s own ledger (withREADME.txt, andagent.lockwhile it runs), and, for a new ledger, a signing key in~/.immutableqc/keys/. - One writer per root While the server runs it holds the root’s ledger: one server or one
iqc recordat a time per root.
Protocol versions: 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05. Every tool has an input and an output schema; a refused path or a bad argument comes back as a tool error with a sentence saying why.
5The four tools
| Tool | What it returns |
|---|---|
check_inputs reads only | For each file: measured (a ledger that holds no agent steps, or one signed by a listed station key, sealed these exact bytes, with its station and key id), derived (a recorded agent step wrote them) or untraced (neither), and whether a sealed file changed after it was sealed. Only files a lab station sealed read as measured. Never run iqc seal or iqc watch on files yourself. |
record_step appends one signed record | The step’s reference, each file’s SHA-256 and size, and each input’s trace. The tool cannot remove the record; a person reviews it. It is recorded even when an input is untraced, with a warning. |
trace reads only | A file’s tree back through the recorded steps to sealed files, and tracesToMeasured: whether every branch ends in a measurement. |
audit_summary reads only | Pass, review or fail for every ledger under the search folders, with the problems and the items for review. |
Each result also says which folders it searched and how many ledgers it found there.
6The loop an agent follows
- 1 · Check the inputs Call
check_inputsbefore you use lab files. - 2 · Tell the person If an input is untraced, or changed after it was sealed, tell the person before you rely on it.
- 3 · Do the work Read the files you checked; write your outputs inside the root.
- 4 · Record the step Call
record_steponce per step, naming every file you read and every file you wrote. For a file outside the root, copy it in (the same bytes trace the same way), or name it ininput_refsas<sha256>:<bytes>:<label>. - 5 · Before you report Call
traceon the result, oraudit_summaryfor the whole lab folder.
7From a pipeline
A script records its step with iqc record; with --json - the record and the traces are its standard output. In Python, with the standard library:
import json
import subprocess
def record_step(action, inputs, outputs, root="/lab/agent-workspace"):
cmd = ["node", "/opt/iqc/iqc.mjs", "record", "--root", root, "--search", "/lab",
"--actor", "nightly-pipeline", "--tool", "normalize.py", "--action", action, "--json", "-"]
for path in inputs:
cmd += ["--in", path]
for path in outputs:
cmd += ["--out", path]
done = subprocess.run(cmd, cwd=root, capture_output=True, text=True)
if done.returncode != 0:
raise RuntimeError(done.stderr.strip())
step = json.loads(done.stdout)
for item in step["inputs"]:
if item["state"] == "untraced":
print("warning: untraced input", item["path"])
return step["reference"]
ref = record_step("Normalized plate 07 to well A1",
["inputs/plate-07.csv"], ["analysis/plate-07-normalized.csv"])
print("recorded", ref)
8Sign-off by a person
A person signs off steps: approved, question or rejected, with iqc review, or in the app’s review mode, which signs the review with the browser’s key and lets the reviewer download the review ledger. There is no review tool over MCP. An agent with a shell could still run iqc review, and the reviewer’s name is whatever is typed: check the review ledger’s key id. A key identifies a computer or a browser, not a person; per-person keys and signatures are upcoming.
9What a pass shows, and what it does not
A pass shows that every sealed file is byte for byte the one that was sealed, and that no record a ledger still holds was edited or reordered since it was signed. A pass does not show:
- That a file was right when it was written: a wrong value written by the instrument software is sealed as written.
- Anything about a change made inside the settle window, between the moment the software writes a file and the moment the agent seals it.
- A rewrite by whoever holds a ledger’s key: a head pinned somewhere else (
iqc head, theniqc audit --expect-head, or a--pin-file) exposes that. - A ledger cut short, its last records deleted with their files: that needs no key, and it shows only against a head pinned earlier (
iqc head, theniqc audit --expect-head, or a--pin-file). Every audit without a pinned head says so in a note. A pin file that names a ledger no audited ledger has fails the audit: the ledger was deleted, started over, or is kept outside the folder (add it with--ledger).audit_summarychecks a pin file only when the server was started with--pin-file. - That a measured file came from your lab’s station, unless you list the station keys. A ledger that holds agent steps never measures, so an agent’s own ledger does not count its own inputs. Without
--station-key, any other ledger measures, whatever its key: anyone who runsiqc sealmakes files read as measured, the station name is a label the sealer picks, and the audit adds a note naming the keys it counted. With--station-key <keyId>(repeatable, oniqc auditandiqc trace, and in the MCP server entry), only a ledger signed by a listed key measures, and a listed key that signs no audited ledger fails the audit.iqc statuson each station prints its key id.iqc audit --expect-keyis a different check: with several ledgers, a ledger signed by a key not listed fails, and the message lists the keys found. - That a reviewer is a person: a reviewer is a signing key, and the name is a label. Without
--reviewer-key, an approval counts from any key, and a note names them. With it, only listed keys approve. A rejection or a question counts from any key. - That a step used what it names. A step that names no inputs goes to review, because its outputs trace to nothing measured.
- What a step read and did not name: a step records only the files it names, and its action line is the agent’s own words.
- That an output was computed from the inputs its step names. A trace follows the steps as the agent recorded them: a step can name inputs it did not use, or outputs it did not compute from them. A person’s review checks that.
10Questions
10.1How do I check that an instrument export is the original file?
A lab station seals the instrument’s folder with iqc watch or iqc seal. Only files a lab station sealed read as measured. Never run iqc seal or iqc watch on files yourself. Then check_inputs (or iqc trace) says whether the file’s exact bytes are the ones a ledger sealed, with that ledger’s key id, and whether that file changed after it was sealed. Compare the key id with the lab’s station key ids.
10.2Why does a file I know was sealed read as untraced?
The ledger that sealed it is outside the folders the server searches. Start the server with --search and the lab folder; each result says which folders it searched.
10.3When is it not the right tool?
When you need the values inside files checked, a hosted service, or signatures that identify a person. It works on whole files, on the computers you run it on, and there is no hosted service; reading values inside files and per-person signatures are upcoming.
11Data, format and versions
- Data handling It never uses the network and nothing is uploaded. It reads the files under the folders it is given and writes only its ledgers, their
README.txtand lock files, the pin file and reports you ask for, and a signing key in~/.immutableqc/keys/. - Format A ledger is JSON Lines: one record per line, each with the file’s SHA-256, size and time, signed with ECDSA P-256 and linked to the record before it. An audit needs no key.
- Versions iqc.mjs 0.1.0; MCP protocol versions 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05.
- License No license is published yet: ask through joseqc.com before you copy or redistribute iqc.mjs.
12Contact
Questions and reports go through joseqc.com, the founder’s site.