Releases: KeilerHirsch-Labs/schroedinger-sync
Release list
v2.2.0 — ingest integrity + sessionKey hardening + gold-standard review pass
Ingest integrity + sessionKey hardening, plus the gold-standard review pass from the last round now shipped. Driven by an independent go-reviewer pass (twice — once on the new features, once re-reviewing the fix for what it found).
Added
- Write-side SHA-256 manifest — first half of the native MemPalace ingest handshake. Every export write now records its SHA-256 in
outDir/.content-hashes.json, saved per-file (not batched) so an interrupted harvest never loses a hash for a file already on disk. Only the write side ships here — the read-back verification (a re-hash from MemPalace's own store, reported as an X/Y scorecard) needs a change in the separate mempalace-src ingest pipeline and stays open. - sessionKey/master-key zero-hardening. The raw AES master key and raw decrypted sessionKey plaintext are now explicitly zeroed (
zeroBytes, deferred) the moment their last read completes, narrowing (not closing — the returned string is still held for the session by design) the window covered by SECURITY.md's existing memory-forensics caveat.
Fixed (from the prior gold-standard review round)
- Gated autostart, chrome-profile single-instance race fix, residue-free uninstall (
cleanup-temp, pre-install sweep). - Installer: dark-panel rendering, Back button, license localization — visually re-verified this release against the actual compiled installer.
- CI: Windows PATH now inherited into the MSYS2 race-job shell.
docs/how-it-works.svg: OBSERVE box text was overflowing its border — widened the box instead of shrinking the font.
Known, deliberately deferred gap: the new SHA-256 manifest's cross-process locking only covers goroutines within one process — two schroedinger-sync.exe instances sharing an outDir (this tool's own supported supervise+manual-harvest combination) can still race a lost update. Currently inert (nothing reads the manifest yet); documented rather than rushed, matching this repo's existing precedent for .sync-state.json's identical unaddressed gap.
Full details in CHANGELOG.md.
v2.1.2 — sync-engine hardening round 2 (harvest data integrity)
Second correctness pass on the harvest/sync engine — a data-integrity release. Driven by a full byte-level teardown of the codebase plus three independent multi-agent review rounds.
Fixed
- A Cloudflare/login/WAF HTML page or a JSON API-error body (e.g. a 404 for a since-deleted conversation) could be saved as a conversation — and once written, was skipped forever. Both are now rejected before they can be persisted as, or overwrite, a real export.
- Legacy poisoned files now self-heal on the daemon path (the on-disk
- Updated:header is checked, not just file size). - Failed conversations retry on the next cycle — bounded, so a permanently-failing item can't make the daemon re-list and pop Chrome every interval forever.
- Partial project-doc failures are propagated; empty memory is a no-op; a mid-harvest session timeout is a hard error; the one-shot
harvestnow refreshes changed conversations and exits non-zero on errors.
The security surface is unchanged — gosec, staticcheck and govulncheck are all clean. Tests-first; statement coverage 27.2% → 28.4%.
Still open (deferred to a follow-up): a single-instance guard (lockfile / named mutex) so two daemons can't share one output directory.
Full details in CHANGELOG.md.
v2.1.1 — sync-engine correctness hardening
A focused correctness pass on the live-sync engine after a full multi-agent ECC review (Go + security reviewer; gosec, staticcheck, govulncheck all clean). The security surface came through clean — every fix here is in the sync/robustness path.
Fixed
- Silent data loss on the first daemon cycle. The daemon seeded its sync state from any existing on-disk file without checking that the file reflected the conversation's current server version — a conversation edited between the one-shot
harvestand the first daemon cycle was recorded as up to date while the file still held old content, and never re-fetched. It now compares the file's ownupdated_atagainst the server's and re-fetches on any mismatch. - One-shot
harvestno longer reports success on a partial export. A mid-pagination listing failure used tobreakand then report "DONE … 0 errors" over an incomplete list. Listing failures are now fatal with a clear INCOMPLETE message. - Data race on
os.Stdoutin the tray daemon. Project-doc/memory diagnostics wrote straight to theos.Stdoutpackage variable from the background sync goroutine while the tray "Beenden" handler reassigned it on another goroutine. They now route through the samelogfsink.
Internal
- Shared conversation-listing and filename helpers (DRY; root cause of the partial-export drift), injectable retry/backoff delays, and sync-engine regression tests. Coverage 20.9% → 27.2%.
Full details in CHANGELOG.md.
v2.1.0 — AGPLv3 + gold-standard hardening
Schroedinger Sync v2.1.0 — AGPLv3 relicense + a full gold-standard hardening pass.
Export your own claude.ai conversations, project knowledge docs, and memory to local Markdown. Windows, single Go binary, DPAPI + real-Chrome (CDP), no telemetry, sends data nowhere but your disk.
🔑 License
Relicensed MIT → AGPLv3 — still free and open, permanently. Strong copyleft: any derivative (including a hosted/network version) must stay open source. For a tool whose whole value is "you can trust it because you can read it", that guarantee matters.
🔒 Security
- Toolchain pinned to go 1.26.5, clearing GO-2026-5856 (a reachable Encrypted Client Hello privacy leak in
crypto/tls). - Filename hardening: every API-sourced path component is stripped to
[0-9A-Za-z-], so a tampered response can never escape the output folder via...
🛠️ Fixed
- No more silent data loss —
extractTextnow preserves unknown conversation block types (extended-thinking, images, future types) instead of dropping them. - Fatal/tray exits flush the secret-redactor and tear down any open Chrome first — no lost diagnostics, no orphaned browser window.
- Mutex around all system-tray calls (closes a Win32 icon-handle use-after-free).
decryptValuestrips the 32-byte app-bound prefix for v20 cookies only.cleanValuetrim order; sentinel rate-limit error; write-error logging; unknown-subcommand usage.
✅ Verified
go build · vet · test (coverage 5% → 21%, new unit suite) · staticcheck 0 · gosec 0 · govulncheck clean · gofmt.
Download schroedinger-sync.exe below. Run it yourself (it reads your own cookies — the credential step stays in your hand). See SECURITY.md for the full threat model.
v2.0.1 — Independent re-audit pass
Independent re-audit pass — two fresh review passes (general Go code quality, and a
security/threat-model review re-deriving its own judgment rather than trusting v2.0.0's
prior hardening claims) against the already-shipped v2.0.0 code, with all confirmed
findings fixed. go build/vet/test/govulncheck/gosec/staticcheck all still
report clean; nothing here was a tool-detectable issue — that's exactly why a second,
independent read mattered.
Fixed:
- Data race on the tray's status string (
tray.go) — written by the background sync
goroutine, read from a menu-click callback on a different goroutine, with no
synchronization. Now a small mutex-guardedstatusHolder. - Redaction blind spot: chromedp's own internal logging defaulted to Go's stdlib
log.Printf(targets stderr), entirely bypassing this program's stdout redactor while
a live session held the injected sessionKey cookie. Wiredchromedp.WithErrorf/
WithLogfto route through the sameredact()every other output path uses. - VBScript injection:
install-task's optionaloutDirargument was spliced
unescaped into the generated logon-autostart.vbsfile; an embedded"could break
out of the intended quoted argument. Now escaped per VBScript's own quote-doubling
convention, with a test case that exercises exactly that input. - Silent multi-org gap: the harvester always picked the account's first organization
with no signal if a second (e.g. a Team workspace) existed. Now logs a warning so "did
I get everything?" has an answer. - Non-atomic state writes:
.sync-state.jsonwas written directly; now written to a
temp file and renamed into place, so a crash or overlapping run can't leave a
truncated/corrupt state file. - Byte-unsafe truncation:
trunc()sliced strings by byte index, which can split a
multi-byte UTF-8 rune in half — reachable in practice via German titles (umlauts, ß)
or emoji, producing a corrupted character in a filename. Now rune-safe. - Documentation honesty pass on SECURITY.md: several claims read stronger than what the
code actually enforces (the exact scope ofTestNetworkEgressIsClaudeOnly's literal-
only URL matching, the redaction guarantee's stdout-vs-stderr boundary before the
chromedp fix above, "one secret" undercounting the broader on-disk temp-copy
footprint). Clarified each; added an explicit section on what the regex-based
invariant tests do and don't protect against. - Minor: an inaccurate code comment (attributed a Chrome cookie-encryption detail to
"v10" when it's actually v20 App-Bound Encryption), an imprecise doc comment ("Task-
Scheduler" when the actual mechanism is a Startup-folder.vbsdrop), and an
unbounded-growth nit in the redactor's secrets list across long daemon uptimes
(deduped on register).
v2.0.0 — Complete rewrite (DPAPI + CDP)
Changelog
v2.0.0
Complete rewrite. Nothing from v1 survives except the name and the goal
(export your own Claude conversations to local Markdown).
Why the rewrite: v1 was a VS Code extension (TypeScript) plus a Python CLI
that parsed local Claude Code session transcripts. It never touched claude.ai
itself — it had no way to reach Desktop/Web conversations, only what was
already sitting in local JSONL files. v2 solves the actual problem: pulling
your full claude.ai account (conversations, project docs, memory) via the
same API the web/desktop client uses.
How it works now:
- Decrypts your own
sessionKeyfrom Claude Desktop's DPAPI-protected cookie
store — replaces v1's "read local files only" approach. - An earlier v2 iteration tried raw TLS/JA3 impersonation
(bogdanfinn/tls-client) to talk to the claude.ai API directly. Cloudflare's
managed JS challenge defeated it —cf_clearanceis fingerprint-bound and a
borrowed cookie doesn't validate. That whole path (~12 dependencies) was
deleted, not kept as a fallback. - Replaced it with driving a real, visible Chrome via CDP (
chromedp): inject
the cookie, navigate to claude.ai, let Chrome solve the challenge itself,
then call the API same-origin from inside the page. This is the only
approach that actually works and it's the one this release ships.
New in this release, none of it existed in v1:
harvest— full export: conversations, project knowledge docs, memory.probe— dumps the raw API schema, useful for finding new surfaces claude.ai
adds later without guessing.watch/tray— a live-sync daemon (headless or with a system-tray icon),
gated on Claude Desktop being closed (DPAPI needs the cookie file unlocked).- Windows installer (
installer/schroedinger-sync.iss, Inno Setup) — per-user,
no admin required, autostart via the app's owninstall-taskcommand. security.go/security_test.go— the threat model in SECURITY.md is
enforced by tests (redaction, hardcoded non-headless flag, claude.ai-only
egress, no importable package), not just asserted in prose.govulncheck/gosec/staticcheckall clean; see SECURITY.md and the
CI workflow for the specifics.
Removed:
- The VS Code extension. It solved a different, smaller problem (local JSONL
→ Markdown) thatharvest'splatformfield doesn't even distinguish
anymore — Code/Cowork/Design conversations all live in the same
chat_conversationsAPI v1 never had access to. - The Python CLI and its
curl_cffidependency — same reason, superseded by
the Go/CDP approach above. - Freemium pricing plan (free/Pro €5/Team €15) from the original 2026-03
business plan. v2 is MIT-licensed and free, permanently — see SECURITY.md
"Business model" for why.
v1.0 (superseded, code removed)
VS Code extension + Python CLI. Converted local Claude Code session JSONL
files into readable Markdown summaries. Never had access to claude.ai
Desktop/Web conversations. Frozen since 2026-03-17.