Claude Flight Recorder — an MCP server that archives Claude Code session transcripts to Filecoin Warm Storage with client-side encryption and tamper-evident hash chaining. Transcripts are treated as opaque blobs except for a narrow secret-shape redaction scan, and the archive pipeline (redact → gzip → encrypt → upload) is verifiable end-to-end via retrieval checks and PDP proofs.
| Phase | Scope | Status |
|---|---|---|
| 0 | MCP scaffold + local session discovery (ping, session_list) |
✅ |
| 1 | session_archive (gzip → AES-256-GCM → Warm Storage) + wallet_status + payments setup script |
✅ |
| 2 | archive_list + session_retrieve + session_verify — round-trip, byte-equal verification, PDP status |
✅ |
| 3 | Redaction pass + hash-chained local index + index_verify |
✅ |
| 4 | SessionEnd auto-archive hook, CLI dispatch, npm publish prep |
✅ |
Filight runs against the Filecoin Calibration testnet only in this phase. MCP tools never move funds — deposits and approvals happen exclusively through the human-run setup script.
-
Create a throwaway wallet and fund it with tFIL (gas) and USDFC (storage payments). Faucet links are on the Synapse docs: https://synapse.filecoin.services/
-
Register the MCP server with the key (config comes from real env vars only —
.envfiles are not supported;dotenvis permanently banned because v17+ writes to stdout and corrupts the MCP stdio transport):claude mcp remove -s user filight claude mcp add -s user -e FILIGHT_PRIVATE_KEY=0x<TESTNET_KEY> filight -- npx -y filight
⚠️ The key lands in plaintext in~/.claude.json. That is acceptable for a throwaway key holding testnet funds only, and forbidden for anything that has ever held real value. -
Deposit USDFC and approve the Warm Storage operator (interactive; asks for confirmation before sending):
export FILIGHT_PRIVATE_KEY=0x<TESTNET_KEY> # shell session only npx -y filight setup --deposit 2
-
Optional auto-archive: rotate to a fresh Calibration key first, export it, then install the SessionEnd hook:
export FILIGHT_PRIVATE_KEY=0x<ROTATED_TESTNET_KEY> npx -y filight install-hook
-
In a new Claude Code session:
/mcpshows 8 tools,wallet_statusshows balances, thensession_archive(trydryRun: truefirst for a cost estimate and redaction counts).
For local development from this repo, replace npx -y filight with node /Users/{YOURUSERNAME}/Desktop/Filight/dist/index.js after npm run build.
session_archive redacts secret-shaped text by default before gzip/encrypt/upload. Opt out with redact: false only when byte-for-byte transcript fidelity is more important than safety. Redaction never logs, returns, or stores matched values. Replacement tokens are deterministic and JSON-string-safe:
[FILIGHT-REDACTED:<rule>:<sha256(match)[0:12]>]
Schema-2 index entries store plaintextSha256 for the original transcript bytes, redactedSha256 for the bytes actually sealed, and redactions as rule/count summaries only.
| Rule | Shape |
|---|---|
evm-private-key |
0x[0-9a-fA-F]{64} |
env-assignment |
Uppercase env-ish names containing KEY, TOKEN, SECRET, PASSWORD, PASSWD, or CREDENTIAL, then = or :, with only the value replaced |
aws-access-key-id |
AKIA[0-9A-Z]{16} |
github-token |
gh[pousr]_... token shapes |
api-key-prefixes |
sk-ant-..., sk-..., sk_live_..., and xox... token shapes |
jwt |
Three-part JWTs beginning with eyJ...eyJ... |
pem-block |
Private-key PEM blocks, including JSON strings with escaped \n |
Bare 64-hex strings are deliberately not redacted because they collide with normal SHA-256 hashes in transcripts, including Filight's own output. Use the env-assignment rule as the backstop for values presented as *_KEY=..., *_SECRET: ..., and similar names.
filight install-hook additively merges a Claude Code SessionEnd hook into ~/.claude/settings.json, backing up the file first. It writes:
~/.filight/wallet.env(0600) from the currently exportedFILIGHT_PRIVATE_KEY~/.filight/hook.jsonif missing- a
SessionEndcommand pointing at the packagedsession-end-hook.shwithtimeout: 10
The hook script reads the SessionEnd JSON from stdin, extracts transcript_path with Node (no jq dependency), sources wallet.env, launches node dist/index.js --archive-hook <path> with setsid nohup when available, redirects all output to ~/.filight/hook.log, and exits immediately. The upload continues in a detached process, so ending a Claude Code session is not blocked by the roughly minute-scale Warm Storage upload.
SessionEnd is best-effort: it will not run after SIGKILL, power loss, or a hard crash. Manual session_archive remains the guaranteed path.
hook.json defaults:
{
"enabled": true,
"minBytes": 2048,
"includeProjects": [],
"excludeProjects": []
}Empty includeProjects means all projects. excludeProjects wins over includes. The hook skips below-floor files, filtered projects, disabled config, missing keys, unreadable paths, and unchanged session content. Unchanged detection compares the current transcript's plaintextSha256 to the newest index entry for that session id, preventing duplicate USDFC spend on resume/end without content changes.
Uninstall only the settings hook:
npx -y filight uninstall-hookwallet.env and hook.json are left in place for safety; delete them manually if you want to fully deprovision.
Use archive_list to find a pieceCid. session_verify reports shallow PDP/retrieval status by default; pass deep: true to also download, authenticate, decrypt, gunzip, and hash in memory without writing a transcript. session_retrieve restores bytes to ~/.filight/restored/<sessionId>-<unix-ts>.jsonl by default, or to an absolute output path outside ~/.claude.
For the round-trip proof, run the redaction-aware harness:
npx tsx scripts/e2e-roundtrip.tsIt archives the latest session, retrieves the returned pieceCid, locally recomputes redact(original), and byte-compares the restored file against those redacted bytes. When redactionsApplied is 0, that is byte-identical to the original.
~/.filight/archives.jsonl is append-only. Legacy schema-1 lines are preserved as historical prefix bytes. Every schema-2 line stores prevHash, the SHA-256 of all file bytes before that line; appends are serialized by ~/.filight/index.lock with 30-second stale-lock stealing and a stderr warning. index_verify walks the file once and returns valid, entry counts, the first invalid line when detected, and headHash (the SHA-256 of the whole file for future external anchoring).
From a shell:
npx -y filight verify-indexFilight is Calibration testnet only. Do not point a mainnet or value-bearing private key at it.
Transcripts are gzipped and sealed client-side with AES-256-GCM before upload. The local AES key lives at ~/.filight/key (0600); losing it means losing the ability to decrypt existing archives. Auto-archive also stores the exported testnet wallet key in ~/.filight/wallet.env (0600) because Claude Code hooks run as a separate process and do not inherit the MCP server's -e environment. This is a deliberate convenience tradeoff for throwaway testnet keys only.
MCP tools never move funds. Deposits and approvals are human-only through filight setup --deposit N. npm publish is also human-only; this repo prepares npm pack and npm publish --dry-run, but publishing must be run by the operator.
Real Calibration evidence from the Phase 3/4 closeout:
{
"pieceCid": "bafkzcibe5wcqedfad6uzxqsdugz3hes4hcoe5qht2hhihkq27levp5w5ylvtrtcnfi",
"byteEqualToRedactedOriginal": true,
"diff": "IDENTICAL against locally redacted original",
"redactionsApplied": 23,
"redactions": [
{ "rule": "env-assignment", "count": 21 },
{ "rule": "evm-private-key", "count": 2 }
],
"headHash": "d6ad1e9a9e37a841d27ad5c76aa86a002f99507228f8d97bd971bb27321d2fd5",
"tamperedCopy": {
"valid": false,
"firstInvalidLine": 3
}
}session_verify shallow mode reports provider PDP timestamps (dataSetLastProven, dataSetNextProofDue) when the storage context returns them; rerun it for fresh timestamp values in the final submission artifact.
| Variable | Meaning |
|---|---|
FILIGHT_PRIVATE_KEY |
0x-hex Calibration private key (required for wallet_status, session_archive, session_retrieve, session_verify, setup, and hook provisioning) |
FILIGHT_NETWORK |
calibration (default; the only accepted value) |
FILIGHT_RPC_URL |
Optional RPC endpoint override |
FILIGHT_DATA_DIR |
Data directory (default ~/.filight) |
~/.filight (mode 0700) holds the AES-256-GCM encryption key (key, mode 0600, created lazily on first archive), optional hook wallet env (wallet.env, mode 0600), hook config (hook.json), hook log (hook.log), the append-only archive index (archives.jsonl), the index lock (index.lock while appending), and restored transcripts under restored/. Encrypted blobs use the layout [12-byte nonce][ciphertext][16-byte GCM auth tag] over the gzipped redacted transcript bytes. Losing the key file means losing the ability to decrypt every archive — back it up if the archives matter.