Skip to content

feat: import Claude.ai conversations through desktop auth - #1380

Open
pascalwhoop wants to merge 8 commits into
kenn-io:mainfrom
pascalwhoop:fix/claude-ai-repair-import
Open

feat: import Claude.ai conversations through desktop auth#1380
pascalwhoop wants to merge 8 commits into
kenn-io:mainfrom
pascalwhoop:fix/claude-ai-repair-import

Conversation

@pascalwhoop

Copy link
Copy Markdown

Summary

  • add a desktop-only authenticated Claude.ai connection and browser transport
  • ingest browser-fetched Claude conversations locally with durable incremental sync, retry/cancel support, and optional while-running scheduling
  • preserve non-text Claude blocks losslessly during parsing
  • add explicit Repair import, which refetches conversations and restores only matching Claude.ai permanent-deletion markers

Validation

  • go test ./internal/cloudsync/claudeai ./internal/db ./internal/server ./internal/parser
  • npm --prefix frontend run check
  • cargo check --manifest-path desktop/src-tauri/Cargo.toml
  • go vet ./...

Security

Claude credentials remain in the isolated WKWebView/Keychain; the Go sidecar receives only browser response JSON and stores no credentials.

@roborev-ci

roborev-ci Bot commented Aug 11, 2026

Copy link
Copy Markdown

roborev: Combined Review (7835ecb)

Verdict: Changes require fixes for a broken scheduler, unsafe privileged WebView access, and several import reliability issues.

High

  • Scheduled synchronization cannot authenticate

    • Location: desktop/src-tauri/src/lib.rs:975
    • Scheduled POST requests omit the required loopback Origin, causing CORS rejection with 403. When authentication is required, they also omit the bearer token and receive 401.
    • Fix: Use an internal scheduler interface or an HTTP client that supplies the matching loopback origin and configured authentication.
  • Untrusted remote pages can invoke privileged native commands

    • Locations: desktop/src-tauri/src/lib.rs:578, desktop/src-tauri/src/lib.rs:1090
    • The authentication WebView allows navigation to arbitrary HTTP/HTTPS origins while exposing globally registered, secret-bearing and state-changing commands. The response callback validates only the window label, allowing remote content to alter configuration, disconnect authentication, forge browser results, and potentially poison the local archive. The pinned Tauri 2.10.2 also predates relevant remote-origin ACL fixes in 2.11.1.
    • Fix: Upgrade Tauri and its runtime dependencies to at least 2.11.1, declare commands through AppManifest, restrict remote IPC and navigation to the minimum required HTTPS origins, validate caller windows, and verify the current origin is exactly Claude before accepting results.

Medium

  • Persisted schedules fail after restart or disconnect

    • Location: desktop/src-tauri/src/lib.rs:783
    • Schedule configuration persists, but authentication state and the required WebView do not. Enabled schedules repeatedly fail after restart or disconnect, while the disconnected UI prevents disabling them.
    • Fix: Restore a hidden authenticated WebView at startup, disable the persisted schedule on disconnect, or allow schedules to be disabled while disconnected.
  • Failed imports are incorrectly checkpointed

    • Location: internal/server/huma_routes_cloud.go:173
    • ImportClaudeAI records per-conversation database failures in stats.Errors without returning an error, but the manifest is committed unconditionally. Failed conversations are then treated as current and skipped by subsequent syncs.
    • Fix: Fail the batch when errors occur or checkpoint only successfully imported conversation IDs.
  • Browser imports repeatedly process the entire cache

    • Location: internal/cloudsync/claudeai/import.go:206
    • Each browser batch exports and reimports the full cache, making initial paginated imports quadratic and causing unchanged sessions to receive new local_modified_at values and unnecessary PostgreSQL pushes.
    • Fix: Build import payloads only from the supplied batch while retaining the full cache solely for durable content and markers.
  • Repair mode can reuse corrupt cached content

    • Location: internal/cloudsync/claudeai/import.go:192
    • Although repair mode downloads fresh details, PrepareBrowserImport discards them when timestamps match the manifest, preventing recovery from corrupt cached files.
    • Fix: Propagate the repair/force flag and bypass the unchanged-cache shortcut during repair.
  • Credential storage is unavailable on Windows and Linux

    • Location: desktop/src-tauri/Cargo.toml:23
    • The keyring dependency enables only apple-native, despite the connection UI being available on Windows and Linux, where no credential-store backend is configured.
    • Fix: Add target-specific Windows and Linux keyring backends or restrict the feature to macOS.
  • Pagination can silently truncate imports

    • Location: desktop/src-tauri/src/lib.rs:1177
    • Pagination ignores the provider’s has_more field and assumes continuation only when a page contains exactly the requested limit. Short nonterminal pages therefore stop early, and offsets advance by a fixed 50 instead of the returned count.
    • Fix: Honor has_more, use page length only as a fallback when it is absent, and advance by the actual number of returned items.

Reviewers: 2 done | Synthesis: codex, 30s | Total: 14m26s

@mariusvniekerk

Copy link
Copy Markdown
Collaborator

Can you adjust this to be more general? Tying this to the Tauri app instead of the generic webapp feels like the wrong approach.

@pascalwhoop

pascalwhoop commented Aug 11, 2026

Copy link
Copy Markdown
Author

I can pull as much as possible out of rust and into go but the actual routing of all the fetch requests through the webkit was on purpose. Because openai/anthropic have extensive blocking of non web based interfaces. so staying in the webkit engine just removes a whole lot of spoofing complexity.

What I can aim for is

  • Svelte: control UI
  • Go: Main lifecycle, pagination etc
  • Rust: Just perform requests that it's being told to execute from Go

@mariusvniekerk

Copy link
Copy Markdown
Collaborator

Yeah the idea is we should be able to relatively easily sub in electron / some other browser thing to make this behave.

@roborev-ci

roborev-ci Bot commented Aug 12, 2026

Copy link
Copy Markdown

roborev: Combined Review (30cd0f4)

The PR has three medium-severity issues involving authentication configuration, session restoration, and lease synchronization.

Medium

  • desktop/src-tauri/src/lib.rs:631 — Native cloud requests read config.toml from Tauri’s app-data directory, while the sidecar defaults to ~/.agentsview and may use AGENTSVIEW_DATA_DIR or AGENTSVIEW_AUTH_TOKEN. With require_auth enabled, requests may carry no token or the wrong token, causing claim/result requests to fail.

    • Fix: Resolve the sidecar’s effective data directory and environment token using the same rules as the Go configuration, or pass the effective bearer token directly to native state when launching the sidecar.
  • desktop/src-tauri/src/lib.rs:880 — Stored credentials are never restored at startup, so the authenticated webview and transport worker are not recreated. Every restart appears disconnected, and automatic sync stalls until manual reconnection. Credential-store failures can also silently prevent a valid browser session from becoming connected.

    • Fix: Restore and validate the saved session during startup, create the hidden authenticated webview, start exactly one transport worker, and surface credential-store errors.
  • internal/cloudsync/transport/transport.go:108Complete validates p.request.Lease outside the broker mutex while the lease-expiry callback mutates it under the mutex. This creates a data race and can accept an expired response after its request has already been requeued.

    • Fix: Under one locked critical section, look up the pending request, validate its lease, stop its timer, and invalidate or mark the lease completed before delivering the response.

Reviewers: 2 done | Synthesis: codex, 12s | Total: 15m50s

@roborev-ci

roborev-ci Bot commented Aug 12, 2026

Copy link
Copy Markdown

roborev: Combined Review (8b6474b)

Changes need attention: seven medium-severity issues could break authentication, synchronization, scheduling, or safe shutdown.

Medium

  • desktop/src-tauri/src/lib.rs:631 — The transport reads auth_token from Tauri’s app-data directory, while the sidecar uses the effective AgentsView data directory (~/.agentsview or AGENTSVIEW_DATA_DIR) and may source the token from AGENTSVIEW_AUTH_TOKEN. With require_auth enabled, cloud transport requests can receive 401 responses. Resolve the sidecar’s effective configuration and environment override, or securely pass the effective token to the desktop transport.

  • internal/cloudsync/transport/transport.go:108Complete releases the broker mutex before validating p.request.Lease. The lease-expiration callback or another Claim can mutate it concurrently, allowing an expired response to complete a re-leased request and causing a data race. Validate the entry and lease, stop the timer, and mark completion atomically under the mutex.

  • internal/cloudsync/claudeai/service.go:342 — Each 50-conversation page calls ImportClaudeAI separately, potentially dropping and rebuilding the entire SQLite FTS index once per page. Suspend and rebuild FTS once per cloud-sync job, or maintain it incrementally across batches.

  • desktop/src-tauri/src/lib.rs:880 — Persisted keychain sessions are written but never restored. Each launch starts disconnected without an authenticated webview or transport worker, so automatic schedules stall until the user reconnects manually. Restore saved credentials/profile and start the authenticated transport before scheduled work begins.

  • desktop/src-tauri/src/lib.rs:508 — The authentication window rejects navigation outside claude.ai, preventing common OAuth and enterprise identity-provider redirects from completing. Permit required identity-provider origins while restricting Tauri command capabilities to Claude-owned pages.

  • frontend/src/lib/components/settings/ClaudeAiSettings.svelte:125 — The component fetches sync status only once on mount. Jobs discovered already running never enter polling, leaving controls disabled after completion; cancellation can likewise remain stuck at cancelling. Poll active jobs through their terminal state and stop polling when the component is destroyed.

  • internal/cloudsync/claudeai/service.go:128Close cancels work without waiting for scheduler or job goroutines. Imports or FTS rebuilds can continue after shutdown starts closing the archive engine and database. Track these goroutines with a wait group and await their exit after cancellation.


Reviewers: 2 done | Synthesis: codex, 23s | Total: 13m31s

@roborev-ci

roborev-ci Bot commented Aug 12, 2026

Copy link
Copy Markdown

roborev: Combined Review (8b38e54)

The PR has one high-severity local transport vulnerability and several medium-severity correctness and performance issues that should be addressed before merging.

High

  • Unauthenticated cached loopback endpoint can expose credentials and Claude conversation datadesktop/src-tauri/src/lib.rs:655

    The worker reconnects to a remembered backend port, sends the reusable AgentsView bearer token, accepts transport instructions, and returns authenticated Claude API responses without verifying which process owns the port. If the detached backend exits, another local user could bind the freed port, steal the bearer token, and request Claude conversation bodies.

    Fix: Authenticate the backend independently of its port. Prefer user-restricted OS IPC or a per-daemon authenticated channel with a pinned ephemeral key. Stop the worker and clear the cached endpoint when daemon liveness is lost.

Medium

  • Desktop transport and sidecar resolve authentication from different configurationsdesktop/src-tauri/src/lib.rs:631

    The transport reads config.toml from Tauri’s application-data directory, while the sidecar uses ~/.agentsview or AGENTSVIEW_DATA_DIR. With authentication enabled or an environment-provided token, requests may use no token or the wrong token, leaving synchronization stuck.

    Fix: Use the same effective configuration and environment as the sidecar, or securely retain the effective token in native state when starting the backend.

  • Credential-store failures prevent otherwise valid browser sessions from connectingdesktop/src-tauri/src/lib.rs:906

    A session is marked connected only after all cookies are written to the credential store, yet those credentials are never restored. Silently retried storage failures can therefore prevent connection indefinitely.

    Fix: Do not require the unused keyring write, or expose failures and restore the saved session and transport during startup.

  • Broker lease can expire before the browser operation’s equal-length timeoutdesktop/src-tauri/src/lib.rs:65, internal/cloudsync/transport/transport.go:20

    Both limits are 45 seconds, but the lease begins before cookie lookup, script evaluation, fetch, and result submission. Slow requests can expire and be requeued repeatedly instead of returning a timeout.

    Fix: Make the lease comfortably longer than the complete client operation or support lease renewal.

  • Lease validation races with expiry and subsequent claimsinternal/cloudsync/transport/transport.go:108

    Complete releases the mutex before inspecting mutable lease state. Expiry or another claim can race with that check, causing a data race and potentially accepting a response from an expired lease.

    Fix: Validate the pending request and lease, stop the timer, and mark completion within one critical section.

  • Initial synchronization repeatedly rebuilds the full FTS indexinternal/cloudsync/claudeai/service.go:342

    Each 50-conversation page invokes ImportClaudeAI separately, and every changed batch drops and rebuilds the complete message FTS index.

    Fix: Suspend FTS maintenance for the entire synchronization and rebuild once afterward while retaining bounded conversation batches.

  • Scheduled synchronization bypasses read-only-store protectioninternal/cloudsync/claudeai/service.go:118

    Only the manual HTTP handler checks writability. The scheduler calls Service.Start directly, allowing jobs to start against PostgreSQL, DuckDB, or read-only SQLite even though they cannot import and may wait indefinitely for transport.

    Fix: Enforce writable-store availability inside Service.Start and reject or disable scheduling for read-only stores.


Reviewers: 2 done | Synthesis: codex, 22s | Total: 15m55s

@roborev-ci

roborev-ci Bot commented Aug 13, 2026

Copy link
Copy Markdown

roborev: Combined Review (122b7b7)

Verdict: Changes require fixes for shutdown safety, authentication lifecycle, token consistency, FTS availability, and remote-mode behavior.

High

  • internal/cloudsync/claudeai/service.go:127 — Shutdown can race with active synchronization.
    Close neither waits for the scheduler to exit nor joins the active sync goroutine. A scheduler tick can begin work after cancellation, and SQLite may close while a sync is importing or rebuilding FTS. Track both goroutines, stop and join the scheduler first, then cancel and await the active job.

  • desktop/src-tauri/src/lib.rs:606 — Disconnect does not clear the persistent Claude session on macOS.
    WKWebView does not support data_directory(profile_dir) on macOS, so deleting that directory during disconnect does not remove cookies from WebKit’s default persistent store. Restarting can silently reconnect and resume transport, contrary to the user’s disconnect decision. Explicitly clear Claude cookies or WKWebView browsing data before reporting success; on macOS 14+, use a dedicated data_store_identifier, with explicit credential deletion retained for macOS 11–13.

Medium

  • desktop/src-tauri/src/lib.rs:625 — Auth cannot recover after the startup watcher expires.
    The watcher exits after ten minutes but leaves the hidden window open. A later claude_auth_start finds the existing window and returns without launching another watcher, so successful sign-in is never detected. Track watcher state and restart it when reusing an unauthenticated window, or keep it alive until the window closes.

  • desktop/src-tauri/src/lib.rs:642 — Desktop transport and sidecar can use different bearer tokens.
    The transport reads from Tauri’s app-data directory, while the sidecar uses its configured data directory and may source the token from AGENTSVIEW_AUTH_TOKEN. With authentication required, claim and result requests can receive 401 responses indefinitely. Resolve the effective token and data directory using the same precedence as the sidecar, preferably during launch.

  • internal/cloudsync/claudeai/service.go:214 — FTS can remain unavailable throughout network waits.
    The first changed batch drops FTS, but restoration is deferred until the entire job returns, including subsequent browser and network waits. Search may fail for an extended or indefinite period, and completion is published before rebuilding finishes. Limit FTS suspension to each archive-write section and restore it before publishing terminal status.

  • frontend/src/lib/components/settings/ClaudeAiSettings.svelte:32 — Claude sync controls remain enabled for remote connections.
    Desktop capability alone enables the controls even when the UI targets a remote server. Requests create jobs on the remote broker while the Tauri worker polls only the local sidecar, leaving remote jobs running indefinitely. Hide or disable these controls when isRemoteConnection() is true unless transport explicitly targets that server.


Reviewers: 2 done | Synthesis: codex, 21s | Total: 15m43s

@roborev-ci

roborev-ci Bot commented Aug 14, 2026

Copy link
Copy Markdown

roborev: Combined Review (2d934ba)

Code review found five medium-severity issues; no critical or high-severity findings or exploitable security vulnerabilities were identified.

Medium

  • desktop/src-tauri/src/lib.rs:667 — Loopback auth token may remain stale or be parsed incorrectly. The token is resolved immediately after spawning serve --background, potentially before the process generates and persists it. The cached value is never refreshed, and ad hoc TOML parsing mishandles some valid quoted values with comments. With require_auth, requests can remain permanently unauthorized.
    Fix: Capture the sidecar’s exact environment, parse TOML properly, and resolve or refresh the generated token after the daemon is ready.

  • internal/cloudsync/claudeai/service.go:376 — Cloud imports do not emit session-change events. Successful scheduled imports write to the archive without notifying subscribers, leaving the sidebar, analytics, and other consumers stale until polling or reload.
    Fix: Add a completion callback or emitter and emit a session mutation after imported or updated rows commit.

  • frontend/src/lib/components/settings/ClaudeAiSettings.svelte:51 — Sync polling can leak or miss scheduled jobs. An interval and waitForSync poll concurrently; the loop is not canceled on component destruction, and failures leave the status running. Meanwhile, an initially idle page does not detect a scheduled job that starts later.
    Fix: Use one lifecycle-owned poller with active and idle intervals, cancellation on destroy, and explicit handling of persistent status failures.

  • internal/cloudsync/claudeai/service.go:97 — Concurrent schedule persistence can overwrite newer state. Mutations copy state while holding s.mu but persist after unlocking, so writes may land out of order and an older snapshot can replace a disabled schedule or newer timestamps. In-memory configuration may also survive failed persistence.
    Fix: Serialize persistence in mutation order and roll back or avoid retaining in-memory changes when persistence fails.

  • frontend/src/lib/components/settings/ClaudeAiSettings.svelte:131 — Schedule errors can prevent native disconnect. If disabling automatic sync fails, the native disconnect command is skipped, potentially leaving the persistent Claude browser session intact.
    Fix: Always execute native disconnect, disable scheduling independently, and report schedule errors separately.


Reviewers: 2 done | Synthesis: codex, 14s | Total: 17m38s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants