Polycode data lifecycle

Polycode stores conversations, files, and credentials locally on your Mac, and does not require an account. Providers (Anthropic, OpenAI, Google, OpenAI-compatible endpoints, Apple Foundation Models, plus enterprise cloud gateways: Vertex AI, AWS Bedrock, Azure OpenAI) are reached directly from your Mac using your own credentials, which are stored in your macOS Keychain. No Polycode-operated server stores or relays user content. Polycode sends anonymous usage metrics and automatic crash reports by default — see Diagnostics below for the full list of categories and the one-click opt-out.

Polycode is a sandboxed Mac App Store application that uses an App Group container — a sandbox affordance for cross-target shared storage — for its primary state (sessions, messages, model metadata, caches, indexes, and user preferences):

~/Library/Group Containers/group.com.izzo.polycode/Library/Application Support/Polycode/

A small number of macOS-managed preferences (@AppStorage toggles) live alongside the app’s per-app sandbox at ~/Library/Containers/com.izzo.Polycode/. Everything else Polycode reads or writes lives under the App Group path. That structure shapes the four workflows below. In-app controls for these actions are tracked for a subsequent release; this article describes the manual-equivalent procedure available today.

Wipe

To remove every piece of local Polycode state from your Mac:

  1. Quit Polycode (⌘Q).
  2. In Finder, open ~/Library/Group Containers/ (use ⇧⌘G and paste the path).
  3. Delete the group.com.izzo.polycode folder. The Finder trashes it like any other folder.
  4. In Finder, open ~/Library/Containers/.
  5. Delete the com.izzo.Polycode folder. This removes the macOS-managed Preferences plist alongside the app’s per-app sandbox.
  6. Empty the Trash if you want the bytes gone immediately.

Alternatively, drag Polycode.app to the Trash. macOS deletes the per-app sandbox container alongside the app, but does NOT delete the App Group container — that persists across re-installs because it is shared with other targets in the developer team. To wipe the App Group data, delete ~/Library/Group Containers/group.com.izzo.polycode/ separately.

Keychain note: your credentials are stored in the macOS Keychain, not inside either container. Deleting the containers does not remove them. To clear credentials, open Polycode → Settings → Privacy → Clear all credentials — a single action that removes every stored credential from the Keychain: provider API keys, custom and OpenAI-compatible endpoint keys, MCP server tokens, cloud-gateway credentials, and sign-in (OAuth) tokens. Your anonymous install identity is not a credential and is not removed by this action (use Settings → Privacy → Reset diagnostics ID to rotate it). Note that macOS does NOT automatically remove Keychain items when you drag the app to the Trash — use “Clear all credentials” first if you want them gone.

Back up

Polycode’s App Group container is an ordinary directory. To back it up manually:

  1. Quit Polycode.
  2. In Finder, open ~/Library/Group Containers/ (use ⇧⌘G).
  3. Copy the group.com.izzo.polycode folder to your backup location (external drive, cloud folder, archive utility — whichever you prefer).
  4. (Optional) Also copy the small ~/Library/Containers/com.izzo.Polycode/ folder if you want to preserve macOS-managed Preferences (@AppStorage toggles).
  5. Relaunch Polycode. Your session resumes unchanged.

What’s inside the copy:

  • Library/Application Support/Polycode/Store/ — the SwiftData store and the GRDB-backed FTS5 index (which lives inside the same SQLite file).
  • Library/Application Support/Polycode/Caches/ — model-metadata caches and the large-result cache. Regenerated on demand if missing. (The tool approval-session cache is in-memory only and is never written here.)
  • User preferences (pinned models, fan-out opt-outs, tool-approval defaults, and similar) live inside the SwiftData store at the path above — in the SyncedPrefs table under polycode.prefs.* keys, with per-device UI state under polycode.ui.* in LocalUIState.
  • The optional per-app sandbox at ~/Library/Containers/com.izzo.Polycode/ — standard macOS preferences plist for @AppStorage-backed toggles. The full path is <sandbox>/Data/Library/Preferences/com.izzo.Polycode.plist.

Time Machine, Arq, Backblaze, and any other backup tool that walks the user library captures both paths automatically.

Restore

To restore a previous backup onto the same or a different Mac:

  1. Quit Polycode.
  2. In Finder, open ~/Library/Group Containers/ (use ⇧⌘G).
  3. Delete the current group.com.izzo.polycode folder (or move it aside).
  4. Copy your backup group.com.izzo.polycode folder into ~/Library/Group Containers/.
  5. (Optional) If you also backed up ~/Library/Containers/com.izzo.Polycode/ for the Preferences plist, replace that folder too.
  6. Relaunch Polycode.

Version compatibility: the SwiftData schema version is embedded in the store. Polycode runs its migration plan on launch when it detects an older schema, so restores from an older Polycode into a newer Polycode work as long as the schema remains within the supported migration window. Restoring a newer container into an older Polycode is not supported — install the matching version or newer.

Keychain credentials do not travel with the container. On a restore (or a new Mac), re-enter your provider API keys via Settings → Providers.

Migrate between Macs

Polycode has no cloud sync and no account system, so migration is exactly the backup + restore workflow above, run across two machines:

  1. On the source Mac: quit Polycode, copy ~/Library/Group Containers/group.com.izzo.polycode/ (and optionally ~/Library/Containers/com.izzo.Polycode/ for the Preferences plist) to portable storage (AirDrop, external drive, file-sync folder).
  2. On the destination Mac: install Polycode from the App Store (or your TestFlight build), launch it once to let macOS create an empty App Group container, then quit.
  3. Replace ~/Library/Group Containers/group.com.izzo.polycode/ on the destination Mac with the copy from the source. If you brought the per-app sandbox too, replace ~/Library/Containers/com.izzo.Polycode/ as well.
  4. Launch Polycode on the destination.
  5. Open Settings → Providers and re-enter your API keys (they live in the per-Mac Keychain and do not travel with the container).

Because nothing Polycode needs lives outside these two containers, this is the complete migration procedure.

Attachments

Polycode lets you attach files to a draft in two ways: drag-drop or paste from anywhere on disk (an inline-content attachment), or pick a file from a project you’ve added in the sidebar (a scoped-file attachment). The handle and byte semantics differ:

  • Scoped-file attachments persist as a handle — Polycode stores the project bookmark + the file’s path inside the project, not its bytes. When you submit, the bytes are read fresh from disk through the bookmark and sent to the providers. When you regenerate that exchange later, the file is re-read again from disk, so the providers see whatever the current on-disk content is — not a snapshot from the original submit.
  • Inline-content attachments persist as bytes — drag-dropped or pasted file content is stored locally inside the conversation’s promptAttachmentsData sidecar in the SwiftData store. This is the only handle Polycode has, so the bytes round-trip exactly on regenerate.

Pressing submit transmits attachment bytes off your device. Each attached file is sent to every LLM provider that is enabled in the current fan-out and to the primary provider’s synthesis call. A single chip therefore produces N provider calls + 1 synthesis call, all carrying the bytes. Anthropic / OpenAI / Google receive the file as a native content block (document / input_file / inlineData); OpenAI-compatible endpoints (Ollama / LM Studio / xAI / OpenRouter / Custom) receive a banner-wrapped text fallback and reject non-text binary attachments. Apple Foundation Models receives a text projection of the file inline in the prompt.

To remove a persisted attachment from the local store, delete the conversation that contains it (right-click the session in the sidebar → Delete). Wiping the sandbox container per Wipe above also removes every attachment Polycode has stored.

Project context

When a session is bound to a project (the project chip on the chat titlebar is set), Polycode synthesizes a project context block and sends it as the system prompt on every chat submit and every regenerate. The block contains the project’s name, its absolute root path, today’s date, your macOS marketing version, your project’s excludedPatterns, a depth-2 directory listing, and the contents of any recognized convention files at the project root: AGENTS.md, CLAUDE.md, GEMINI.md, .cursorrules, every *.md file under .cursor/rules/ (alpha-sorted), CONVENTIONS.md, with README.md as a fallback only when none of those exist. The directory listing honors your project’s excludedPatterns; the convention files do not (a stray *.md exclusion does not silently disable an authored AGENTS.md).

The block is sent to every LLM provider you have configured — Anthropic, OpenAI, Google, OpenAI-compatible endpoints, Apple Foundation Models, and the enterprise cloud gateways (Vertex AI, AWS Bedrock, Azure OpenAI) — as part of the standard fan-out + synthesis flow. It is not sent to PostHog or Sentry. The block is rebuilt on every submit (no cache), so edits to your context files take effect on the very next turn. Per-model systemPromptOverride settings (Settings → Providers → Provider Detail → advanced drawer) take precedence: when you’ve set an override, the project context block is replaced by your override for that model and is not transmitted.

To opt out per-project, toggle off “Inject project context into chats” under Settings → Projects. The opt-out persists locally in the same SwiftData container as the rest of your settings; subsequent submits on sessions bound to the opted-out project send systemPrompt: nil (or the per-model override if set).

Usage

The Settings → Usage tab summarizes how much you’ve spent across providers and where that spend is concentrated. It is a read-only dashboard computed entirely on your Mac:

  • Local-only computation. Cost numbers come from ProviderResponseRecord.costUSD columns inside the local SwiftData store. Each fan-out peer’s cost is computed once at exchange time from the model’s listed pricing × the tokens that peer used, and persisted alongside the response. The Usage tab sums these existing columns over the selected range. Nothing leaves your Mac to render the dashboard.
  • Snapshot-at-exchange-time pricing. Costs reflect what the model’s pricing was when the exchange ran, not what current pricing implies. If a provider changes their pricing later, your historical exchanges keep the dollar figure they were originally quoted at — a stable record of what you actually paid.
  • Reset baseline rebases display, not data. The Reset baseline… button writes a new timestamp under the per-device LocalUIState key polycode.ui.usage.baselineDate and the dashboard’s range filters clamp their start to that timestamp. Past exchanges and per-peer cost records are not deleted. They remain in the store and stay accessible from the sidebar; only the dashboard’s totals start counting fresh from the new baseline. To actually delete prior exchanges, delete the conversations from the sidebar (right-click → Delete).
  • Estimates, not bills. The dashboard’s footnote names the limitation explicitly: numbers are estimates from your provider’s listed pricing at exchange time. Actual provider billing may differ (cache pricing tiers, enterprise discounts, billing-period rounding). The dashboard is a directional view, not a substitute for your provider’s billing console.

Cloud gateways

Polycode supports three enterprise cloud gateways alongside the direct provider APIs: Vertex AI (Google Cloud), AWS Bedrock (Amazon), and Azure OpenAI Service (Microsoft). These are configured under Settings → Providers → Cloud Gateways with one row per per-account / per-project / per-resource instance. A user may configure many instances per gateway.

Credential flow. The credential you provide (Vertex service-account JSON, AWS access-key + secret + optional session-token, or Azure resource api-key) is read once at add-instance time, JSON-encoded, and stored in your macOS Keychain under a per-instance slot (cloud.<uuid>.credential). The credential is never logged, never sent to a Polycode-operated server, and only used to mint per-request auth headers against the cloud you configured:

  • Vertex AI uses your service-account JSON to mint short-lived OAuth2 access tokens via oauth2.googleapis.com/token (RFC 7523 JWT-bearer assertion grant). The minted token is cached in memory for ~1 hour and refreshed before expiry.
  • AWS Bedrock uses your access-key pair to sign each request via AWS SigV4 (HMAC-SHA256 chain). Nothing is cached server-side; the signature is recomputed per request.
  • Azure OpenAI Service uses your resource api-key as the api-key header on each request. No token exchange — the api-key IS the auth material.

Where prompts go. Cohort prompts flow only to the cloud gateway you configured. Vertex AI prompts go to <region>-aiplatform.googleapis.com. Bedrock prompts go to bedrock-runtime.<region>.amazonaws.com. Azure prompts go to <resource>.openai.azure.com. Polycode does not relay cohort prompts through any Polycode-operated server.

Diagnostics scope. Cohort traffic is out of scope for the diagnostics pipeline. Disabling diagnostics under Settings → Privacy → Diagnostics has no effect on cohort prompt routing, because cohort prompts never go through PostHog or Sentry to begin with — they go directly to the cloud you configured. See Diagnostics for what diagnostics actually does cover.

Removing a cohort instance. Settings → Providers → Cloud Gateways → Remove deletes both the CloudInstance row from your local config and the matching cloud.<uuid>.credential slot from your Keychain. A launch-time orphan-reconciliation pass also runs every time Polycode starts, so a credential whose CloudInstance was deleted by another path is cleaned up on next launch.

Diagnostics

Polycode sends two kinds of anonymous diagnostics by default to help improve the app:

  • Product interaction events (PostHog). A curated allowlist of events — app launch, provider added, submit started, submit failed with an error category, tool invocation with an approval outcome, and similar. Each event carries only bucketed or categorical data (provider kind as one of anthropic/openai/google/openaiCompat/foundationModels, latency bucket, error code, token counts). Chat content, model responses, file contents, file paths, session titles, custom endpoint names, custom endpoint URLs, custom model IDs, and personal identifiers are never emitted.
  • Crash and performance diagnostics (Sentry). Crash stacks, hang diagnostics, and CPU exceptions delivered by the system’s MetricKit subsystem. Personal identifiers and file paths are removed before transmission; basic-auth URLs, API-key shapes, emails, and home-directory prefixes are scrubbed by a pre-send hook.

Neither pipeline includes your IP address, your macOS username, your email, a device serial, or any account identifier in the event properties the app sends. As with any HTTPS service, PostHog and Sentry may see your IP address at the network layer when the app connects to their ingest endpoints; Polycode does not use IP addresses to identify you — see the privacy policy for the precise statement. The product-interaction pipeline uses an anonymous install UUID generated on first launch so retention can be measured; that UUID is scoped to PostHog only — it is never passed to Sentry, and it never appears in crash payloads.

Opt out. Open Settings → Privacy → Diagnostics. Two toggles — “Share anonymous usage metrics” and “Share crash reports” — each default to on and can be flipped off independently. Turning a toggle off flushes in-flight events and stops the corresponding SDK from sending further data; turning it back on resumes collection with the existing install UUID.

Reset diagnostics ID. The “Reset diagnostics ID” button in the same section generates a new anonymous UUID, discarding the prior one. Use this if you want to break continuity of anonymous analytics for this install — the SDKs re-initialize, the next event carries the new UUID, and PostHog’s anonymous identifier is rotated alongside it. One technical caveat: PostHog’s SDK retains an internal $device_id that it does not expose any way to clear, so that single identifier persists across a reset; because Polycode runs PostHog in anonymous-only mode (no person profiles), this identifier is not used to merge your old and new events into one person, but a reset does not erase it. Resetting changes future identifier association only — it does not delete data already received by PostHog or Sentry, whose own retention policies govern that.

Export diagnostic logs. The “Export diagnostic logs” button in the same section saves a ZIP to a location you choose, containing the last 7 days of Polycode’s own log output (the com.izzo.polycode.* OSLog subsystems) plus a small manifest (app version, export date, entry count). It exists so you can attach concrete logs to a support request if you choose to. Log lines never contain chat content, model responses, file contents, API keys, or other credentials — that is a standing rule of Polycode’s logging layer, not a filter applied at export time. The export is entirely user-initiated: nothing uploads anywhere, and the file goes only where you save it. This is a diagnostics affordance, not a user-data export — session backup/restore remains a Finder operation as described above.

Debug builds never phone home. Development (DEBUG) builds short-circuit both SDKs to a no-op regardless of toggles — overridable only by an internal engineering launch flag — so engineer activity does not pollute production diagnostics.

Data residency (current release configuration). Polycode v1.0 release builds send diagnostics to PostHog’s US cloud (https://us.i.posthog.com) and Sentry’s US cloud. The ingest endpoints are injected per build rather than hardcoded, so other release configurations — or self-hosted builds — may target different clouds, including EU regions; this paragraph describes what the current release does, not a permanent product guarantee. The retention and deletion policies of PostHog and Sentry govern already-shipped data; the in-app Reset diagnostics ID button changes future identifier association, not historical data.

Last synced from polycode-apple on Jul 5, 2026, 2:43 AM UTC · 60945cc