@@ -31,7 +31,7 @@ CI builds on `macos-15` runners via `.github/workflows/build.yml`, producing arm
31- **Concurrency**: Swift structured concurrency (`async/await`, `@MainActor`, strict `Sendable` conformance)
31- **Concurrency**: Swift structured concurrency (`async/await`, `@MainActor`, strict `Sendable` conformance)
32- **Networking**: `URLSession` async/await, no external dependencies
32- **Networking**: `URLSession` async/await, no external dependencies
33- **Project generation**: XcodeGen from `project.yml`
33- **Project generation**: XcodeGen from `project.yml`
34−- **Persistence**: Keychain (data-protection) for connections and passwords; `UserDefaults` for saved queries
34+- **Persistence**: Keychain (file-based login keychain) for connections and passwords; `UserDefaults` for saved queries. Do not use `kSecUseDataProtectionKeychain: true` — it requires `keychain-access-groups` entitlements that are incompatible with ad-hoc code signing and causes writes to silently fail.
36### Source layout
36### Source layout
@@ -83,10 +83,10 @@ Live tail in Parseable uses gRPC Arrow Flight streaming. This app approximates i
83- Models use defensive `try?` decoding for optional fields to tolerate API version differences
83- Models use defensive `try?` decoding for optional fields to tolerate API version differences
84- Views follow the macOS pattern: `NavigationSplitView` sidebar + detail, with tab switching in the detail pane
84- Views follow the macOS pattern: `NavigationSplitView` sidebar + detail, with tab switching in the detail pane
85- All API calls are `async` and dispatched from views via `Task { }`
85- All API calls are `async` and dispatched from views via `Task { }`
86−- Connection metadata and passwords are stored in the Keychain (data-protection keychain, scoped by bundle ID to survive ad-hoc re-signing); `ServerConnection.password` is excluded from `Codable`
86+- Connection metadata and passwords are stored in the Keychain (file-based login keychain); `ServerConnection.password` is excluded from `Codable`. Do not add `kSecUseDataProtectionKeychain: true` — it requires `keychain-access-groups` entitlements that conflict with ad-hoc code signing and causes writes to silently fail.
87- Stream names are SQL-escaped via `escapeSQLIdentifier` (double-quote wrapping) and URL-encoded via `encodePathComponent` for API paths
87- Stream names are SQL-escaped via `escapeSQLIdentifier` (double-quote wrapping) and URL-encoded via `encodePathComponent` for API paths
88- Views clear stale data before loading new content (e.g., switching streams clears previous schema/stats)
88- Views clear stale data before loading new content (e.g., switching streams clears previous schema/stats)
89−- Entitlements: App Sandbox enabled with network.client for outbound connections
89+- Entitlements: none (App Sandbox is not enabled)
91## Common patterns
91## Common patterns