Skip to content

Latest commit

 

History

History
183 lines (151 loc) · 9.63 KB

security.md

File metadata and controls

183 lines (151 loc) · 9.63 KB

Security and Privacy

Trust Model

AgentDock coordinates local provider applications. It does not make an untrusted provider process safe and is not an operating-system sandbox.

Security-sensitive operations validate:

  • expected provider bundle identifiers and publisher signing teams;
  • code signatures and executable paths;
  • exact managed profile roots and ownership markers;
  • process ancestry, PIDs, and profile arguments;
  • path containment and symlink rejection;
  • bounded file counts, bytes, subprocess output, and shared session readers.

If those checks fail, managed launch, focus, close, restore, delete, or local activity reads fail closed.

These boundaries are implemented in DesktopAppRegistry.swift, DesktopInstanceController.swift, ProfileStore.swift, and LocalChatSession.swift.

Shared Host Resources

Managed profiles retain access to normal user resources, including:

  • files and project directories;
  • shell environment and developer tools;
  • network;
  • Keychain;
  • Git and SSH configuration;
  • macOS permissions and provider updater state.

Use separate macOS accounts or stronger operating-system isolation when those resources must not be shared.

Local Data

AgentDock stores profile metadata under ~/Library/Application Support/AgentDock/. New shortcuts live under ~/Applications/AgentDock/.

Older versions may have left session indexes with bounded titles, previews, timestamps, provider metadata, and relative source paths. They contain no full transcript bodies, tool output, or absolute working directories. The app no longer creates browser indexes; explicit profile removal or deletion cleans up that profile's legacy indexes. Claude activity summaries still scan validated supported local session sources and cache summaries in memory. AgentDock has no chat browser or transcript-copying action; removing that UI does not remove activity-source reads.

AgentDock does not provide cloud synchronization, scrape browser cookies, or read ordinary web-chat caches.

Custom Codex usage refresh is a separate provider egress boundary. AgentDock reads only the active provider id and its matching provider definition from a bounded, regular, non-symlink config.toml. Custom usage requests permit HTTPS or loopback-only HTTP, reject URL credentials and unsafe schemes, and allow redirects only within the same safe origin. Configured header values, environment-backed values, and bearer tokens exist only on the request; they are not persisted, logged, surfaced in raw errors, or included in analytics. Responses are time- and size-bounded. A failed custom-provider request fails closed instead of exposing the profile's unrelated OpenAI account quota.

Provider launches discard inherited app-profile, app-server routing, and Electron/Node runtime overrides before supplying the selected profile's paths. Managed Codex launches pin both CODEX_HOME and CODEX_ELECTRON_USER_DATA_PATH, as well as the matching --user-data-dir. The Electron variable also activates the installed app's preservation of CODEX_HOME across its login-shell environment reload. Stock launches clear managed profile selectors. Managed Claude launches also pair their verified CLAUDE_USER_DATA_DIR override with a matching --user-data-dir argument, covering Chromium session storage from process startup.

The native OpenAI quota reader additionally removes inherited account, API credential, and endpoint overrides so they cannot redirect the selected account's usage request. Custom-provider usage requests retain the explicit environment-backed authentication configured for that provider.

Ordinary shell variables and shared shell startup files remain host resources. The provider can reload that shell environment after startup; launch-time sanitization is not an operating-system enforcement boundary.

Provider namespace directories cannot be registered as individual profiles or replayed as profile deletions. Persisted shortcuts must be distinct .app paths whose normalized name matches the owning profile slug. Recovery journals cannot claim another profile's directory or shortcut. Process discovery rejects ambiguous profile arguments and bundle-prefix lookalikes. Claude helper evidence also requires a publisher signature and a kernel-resolved executable contained by the selected app bundle before lifecycle operations revalidate stable process identity. Profile locks resolve path aliases to one physical root and reject symlinked, nonregular, multiply linked, or foreign-owned lock files.

Built-in Codex launches set CODEX_CLI_PATH to the validated Codex app's bundled Contents/Resources/codex executable. Named config-profile launches point it to AgentDock's signed proxy, which validates the profile-local bounded regular <name>.config.toml, converts its assignments to --config arguments accepted by the desktop app-server, and then executes that same bundled CLI. Managed named-profile launches set the managed profile's exact CODEX_HOME; official named-profile launches set ~/.codex; built-in stock launches remove CODEX_HOME. User-selected executables cannot replace the bundled CLI through AgentDock's parent environment. Settings that would place bearer tokens, API keys, or direct HTTP headers in process arguments are rejected; custom providers should reference credentials through environment-backed settings.

Product Analytics

Analytics are a separate explicit-opt-in, immediately revocable boundary implemented in ProductAnalytics.swift. Only closed typed events can be batched to the configured regional PostHog endpoint. Events never carry source content or local identifiers; GeoIP and person-profile processing are disabled, and queues exist only in bounded memory. Explicit choices persist across upgrades; opt-out clears the queue and random identifier. The public project token is release configuration, not a credential; no private PostHog key belongs in the app. See the complete catalog.

Local Claude usage summaries do not require credentials. Live subscription limits are an explicit provider-access boundary: AgentDock reads Claude Code or Claude Desktop OAuth access tokens and the active Desktop organization from provider-owned Keychain and local state, then calls Anthropic's OAuth profile and usage endpoints. Tokens are held only for the request; AgentDock never copies them into its own storage, refreshes or rotates them, includes them in analytics, or logs them. The Desktop decryption key is retained only in memory for the usage client's lifetime and reused across official and managed profiles. Each profile's tokens and account identity are still read afresh from its files. Background reads forbid Keychain interaction, including legacy login-Keychain prompts. A manual refresh can request access; choosing Always Allow lets macOS retain that grant for subsequent launches of the signed app. Managed profiles resolve credentials only from their own Desktop user-data roots. The official Claude usage row also uses Desktop credentials exclusively, so a separate Claude Code login cannot replace the displayed account. Desktop usage requires a valid account and organization identity, and responses are verified against that identity before display. An explicit identity mismatch clears the corresponding cached quota instead of showing stale data. Active-organization cookie database reads enforce the same root containment and symlink checks as other databases.

Repository and Release Hygiene

Do not commit:

  • credentials, tokens, certificates, provisioning profiles, or environment files;
  • provider profiles, chats, transcript exports, logs, databases, crash reports, or generated indexes;
  • screenshots containing real account, project, device, or session data;
  • build products or notarization material.

The release workflow uses ephemeral credentials, removes temporary keychains and key files, rejects filesystem metadata sidecars and build paths, and publishes checksums for the notarized artifacts.

Sparkle updates require HTTPS, a Developer ID signature, notarization, and an Ed25519 signature. The public verification key is embedded in AgentDock. The private key exists only as the environment-protected SPARKLE_PRIVATE_ED_KEY GitHub Actions secret and is streamed to Sparkle's tool over standard input. Release assets are published before the signed appcast, so a client cannot discover an enclosure that is not yet available.

Reporting a Vulnerability

Follow the security policy. Use a private GitHub security advisory instead of a public issue when a report could expose a vulnerability, credential, private transcript, or local path.

Publication privacy

Publish only synthetic account/profile examples and anonymous measurement counts. Never include local hardware or installed-version inventories, home paths, provider account details, real chats, logs, credentials, or private profile names in commits, PR descriptions, comments, or their edit history. Use a public GitHub noreply address for commit author and committer identities.

script/audit_privacy.sh scans every locally reachable Git ref for secrets and checks new commit identities and the measurement receipt schema. Run python3 script/audit_publication_metadata.py --all-history to audit identities throughout reachable history. Historical violations need coordinated cleanup; a clean current tree does not remove earlier commits or PR revisions. Review PR descriptions and edit history separately from the source scan.