Skip to content

Releases: KeilerHirsch-Labs/schroedinger-sync

v2.2.0 — ingest integrity + sessionKey hardening + gold-standard review pass

Choose a tag to compare

@KeilerHirsch KeilerHirsch released this 19 Jul 11:53

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)

Choose a tag to compare

@KeilerHirsch KeilerHirsch released this 14 Jul 16:59

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 harvest now 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

Choose a tag to compare

@KeilerHirsch KeilerHirsch released this 14 Jul 01:11

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 harvest and 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 own updated_at against the server's and re-fetches on any mismatch.
  • One-shot harvest no longer reports success on a partial export. A mid-pagination listing failure used to break and then report "DONE … 0 errors" over an incomplete list. Listing failures are now fatal with a clear INCOMPLETE message.
  • Data race on os.Stdout in the tray daemon. Project-doc/memory diagnostics wrote straight to the os.Stdout package variable from the background sync goroutine while the tray "Beenden" handler reassigned it on another goroutine. They now route through the same logf sink.

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

Choose a tag to compare

@KeilerHirsch KeilerHirsch released this 11 Jul 12:43

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 lossextractText now 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).
  • decryptValue strips the 32-byte app-bound prefix for v20 cookies only.
  • cleanValue trim 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

Choose a tag to compare

@KeilerHirsch KeilerHirsch released this 07 Jul 16:03

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-guarded statusHolder.
  • 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. Wired chromedp.WithErrorf/
    WithLogf to route through the same redact() every other output path uses.
  • VBScript injection: install-task's optional outDir argument was spliced
    unescaped into the generated logon-autostart .vbs file; 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.json was 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 of TestNetworkEgressIsClaudeOnly'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 .vbs drop), 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)

Choose a tag to compare

@KeilerHirsch KeilerHirsch released this 02 Jul 18:51

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 sessionKey from 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_clearance is 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 own install-task command.
  • 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 / staticcheck all 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) that harvest's platform field doesn't even distinguish
    anymore — Code/Cowork/Design conversations all live in the same
    chat_conversations API v1 never had access to.
  • The Python CLI and its curl_cffi dependency — 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.