<feed xmlns='http://www.w3.org/2005/Atom'>
<title>domain-dig.git/DomainDig/CloudSyncService.swift, branch v5.0.0</title>
<subtitle>local-first ios domain inspection toolkit. dns, tls, rdap, audit.
</subtitle>
<id>http://git.krz.sh/krz/domain-dig.git/atom?h=v5.0.0</id>
<link rel='self' href='http://git.krz.sh/krz/domain-dig.git/atom?h=v5.0.0'/>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/'/>
<updated>2026-07-24T23:32:24+00:00</updated>
<entry>
<title>fix: sync CloudKit through a custom zone instead of queries (#29)</title>
<updated>2026-07-24T23:32:24+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-24T23:17:09+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=2ed92d773a2505ac3b6fbd3d280258549936ba2a'/>
<id>urn:sha1:2ed92d773a2505ac3b6fbd3d280258549936ba2a</id>
<content type='text'>
With the entitlement gate removed, enabling iCloud sync surfaced a chain
of CloudKit errors, each rooted in the query-based fetch:

- "Did not find record type: TrackedDomain" — a fresh container has no
  schema until the first save, so querying any type failed.
- "SharedDB does not support Zone Wide queries" — the shared database
  rejects database-wide queries.
- "field 'recordName' is not marked queryable" — TRUEPREDICATE queries
  require a Queryable index on recordName, which auto-created development
  schemas do not have.

All three are inherent to reading with CKQuery. Move the user's own
records into a single custom record zone and read every record with
CKFetchRecordZoneChangesOperation, which needs no queryable indexes and
works on a brand-new zone. The shared database is read the same way, one
zone per accepted share. A custom zone is also a prerequisite for CloudKit
sharing, so share root records now live there too.

- Ensure the custom zone exists before every push and fetch (idempotent).
- Save all records — and build tombstone delete IDs — in the custom zone.
- Replace the per-type CKQuery fetch with a per-zone change fetch that
  drains truncated responses via the server change token.
</content>
</entry>
<entry>
<title>fix: reach CloudKit instead of gating on unreliable entitlement introspection (#29)</title>
<updated>2026-07-24T23:32:24+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-24T22:51:57+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=0aa8cdf3bb55f31039df1ce473dc23eca22b6449'/>
<id>urn:sha1:0aa8cdf3bb55f31039df1ce473dc23eca22b6449</id>
<content type='text'>
iCloud sync always failed with "This build does not have the CloudKit
entitlement required for iCloud sync." on normal Debug/device builds.

`cloudKitContainer()` gated construction on `isEntitlementConfigurationAvailable()`,
which inspected `Info.plist` keys and an `archived-expanded-entitlements.xcent`
file. Entitlements live in the code signature, not `Info.plist`, and the `.xcent`
file only exists in archived/distribution builds — so the check returned false for
a correctly-signed development build, the container was never created, and every
path reported `.missingEntitlement`.

Stop introspecting entitlements. Always construct `CKContainer.default()` and let
CloudKit's own `accountStatus()` drive availability. When the entitlement is
genuinely absent, CloudKit surfaces `.missingEntitlement` / `.badContainer` /
`.permissionFailure`, which `accountStatus()` now maps back to the "no entitlement"
message — preserving that signal without the false negative.
</content>
</entry>
<entry>
<title>v4.8.3: Clear SonarCloud new-code issues</title>
<updated>2026-07-20T21:27:59+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-20T21:22:28+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=bb18197860ffdb491f010648ec903003b6e1bb65'/>
<id>urn:sha1:bb18197860ffdb491f010648ec903003b6e1bb65</id>
<content type='text'>
Fixes the 4 reported bugs and ~97 code smells flagged in the new-code
period. No behavior changes.

Bugs (swift:S3923) — DomainInspectionService's confidenceFor* helpers each
returned `error == nil ? .low : .low`, an inert conditional. Simplified to
`return .low` and dropped the now-unused `error` parameter.

Smells:
- Merged 14 identical `.empty`/`.error` switch branches in DomainViewModel
- Consolidated duplicate implementations (clearPresentedResults/reset,
  String.nonEmpty/nilIfEmpty, ExportFormat.id/fileExtension)
- Extracted nested ternaries into TLSGrade.tone, EmailSecurityGrade.tone,
  and ChangeImpactClassification.color; removed ContentView.impactColor
  and the duplicate mapping in BatchResultsView
- Documented empty closures and singleton inits
- Marked unused protocol-conformance parameters `_`
- Renamed CloudSyncTrigger.`import` to `imported` (raw value preserved)
  and SSLSessionDelegate's _serverTrust/_tlsMetadata
- Merged nested ifs in the DER parser; flattened closure nesting in
  PortScanService and IntegrationService
- Replaced two-case switches with if/else

Left open: S107 (init parameter counts), S115 (constants mirroring DoH and
ipapi JSON keys), S1075 (false positives on https:// literals), and two
S117 hits on SwiftUI $binding shorthand. These want a Won't Fix resolution
in SonarCloud, not a code change.
</content>
</entry>
<entry>
<title>DomainDig v4.1.0: Add a full local data reset flow in Data Management</title>
<updated>2026-04-25T05:41:34+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-04-25T05:41:34+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=535bb0ff0f64d57be1074e33ceb42f598f80205b'/>
<id>urn:sha1:535bb0ff0f64d57be1074e33ceb42f598f80205b</id>
<content type='text'>
- add a destructive Delete All Data action with confirmation, progress, success,
  and failure handling
- centralize wipe behavior in DataResetService instead of scattering delete
  logic in views
- clear local persistence, temp/export files, integration secrets,
  notifications, caches, and in-memory app state
- reset sync, purchase, and integration services after wipe so the app returns
  to a clean first-launch state

Polish batch and empty-state UX

- present the batch sweep summary after manual bulk searches complete, not only
  from Watchlist
- align the Workflows empty state styling with other empty states by removing
  the extra background card treatment

Bump the project version from 4.0.0 to 4.1.0
</content>
</entry>
<entry>
<title>DomainDig v3.8.0 — Smart Monitoring</title>
<updated>2026-04-24T20:04:54+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-04-24T20:04:54+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=3f0b7746b1bee4c2f626eb805e3fd13c5f2f5d22'/>
<id>urn:sha1:3f0b7746b1bee4c2f626eb805e3fd13c5f2f5d22</id>
<content type='text'>
* Stable domain intervals
* Frequent changes decrease intervals
* Duplicate states do not trigger alerts
* Quiet hours suppress alerts
* Quiet hours across midnight work correctly
* Sensitivity levels alter behavior as expected
</content>
</entry>
<entry>
<title>feat(v3.6.0): add iCloud sharing for workflows and tracked domains</title>
<updated>2026-04-24T04:12:34+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-04-24T04:12:34+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=b35f1b432fc24fbab1fa05593c9a0bca7e4d95a6'/>
<id>urn:sha1:b35f1b432fc24fbab1fa05593c9a0bca7e4d95a6</id>
<content type='text'>
- implement CloudKit sharing (CKShare) for TrackedDomain and DomainWorkflow
- support read-only and editable permissions
- add Share actions and shared state indicators in UI
- handle shared record ownership and participant roles
- implement deterministic conflict resolution to prevent duplication
- ensure compatibility with existing iCloud sync layer
- maintain local-first behavior with graceful offline handling
- remove debug logging for domain availability/rdap messages

notes:
- no custom backend or accounts introduced
- sharing is Apple ecosystem only (iCloud-based)
- large history data remains local-only
</content>
</entry>
</feed>
