<feed xmlns='http://www.w3.org/2005/Atom'>
<title>domain-dig.git/DomainDig/ExternalDataService.swift, branch main</title>
<subtitle>local-first ios domain inspection toolkit. dns, tls, rdap, audit.
</subtitle>
<id>http://git.krz.sh/krz/domain-dig.git/atom?h=main</id>
<link rel='self' href='http://git.krz.sh/krz/domain-dig.git/atom?h=main'/>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/'/>
<updated>2026-07-23T04:23:11+00:00</updated>
<entry>
<title>fix: adopt Swift 6 language mode; resolve all concurrency issues (#27)</title>
<updated>2026-07-23T04:23:11+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-23T02:43:42+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=872583eca8b4e7ae6ef85917604d4a5257b9d125'/>
<id>urn:sha1:872583eca8b4e7ae6ef85917604d4a5257b9d125</id>
<content type='text'>
All three product targets (app, widget, share extension) now build under
SWIFT_VERSION = 6.0 with zero errors and zero warnings. The UITests
target stays on 5.0: XCTestCase's nonisolated setUp/init overrides
conflict with the target's MainActor default isolation under 6, and test
tooling is not shipping code.

The original seven diagnostics, plus the layers Swift 6 mode surfaced
once those cleared:

- SMTPChannel is an actor. It was implicitly MainActor while running its
  receive loop on a background queue, so parsedLines/lineWaiters/
  receiveBuffer were declared main-actor-protected and mutated off it —
  concurrent mutation while resuming a CheckedContinuation can
  double-resume, which traps. The actor serialises all state; Network
  callbacks hop in via Task. The start() continuation also gains an
  OSAllocatedUnfairLock resume-once guard: the state handler can fire
  .ready and later .failed, and resuming twice was a pre-existing trap of
  the same family.
- CachedLookupResult is nonisolated (a value pair built inside actor
  LookupRuntime cannot have a MainActor-bound memberwise init) with
  conditional Sendable — opting out of MainActor isolation also opted out
  of the implicit Sendable that globally-isolated types get.
- PortScanService.printableBanner is nonisolated: a pure transformation
  called from the connection's queue.
- SweepActivityController stores the activity's Sendable id instead of
  the non-Sendable Activity, re-resolving via Activity.activities inside
  each fire-and-forget task, so nothing non-Sendable crosses isolation.
- App Intents' static title/description/openAppWhenRun become lets
  (get-only protocol requirements; static var is shared mutable global
  state), and the summary helpers are @MainActor to match the model
  properties they read and the perform() implementations that call them.
- ExternalDataService's ISO8601DateFormatter is nonisolated(unsafe),
  citing Apple's documented thread-safety, rather than risking a parser
  behaviour change by switching APIs with no test coverage.
- TaskMetricsDelegate.metrics is nonisolated(unsafe): written on the
  session's delegate queue, read only after the request completes, and
  URLSession guarantees didFinishCollecting precedes task completion.
- The share extension extracts the host via async/withCheckedContinuation
  instead of sending a non-Sendable completion into loadItem's @Sendable
  handler; Task inherits the view controller's MainActor so the manual
  DispatchQueue.main hop goes too.

Validated: clean Swift 6 build of all product targets, and the full
enforced 11-test audit suite green on the floor runtime — Swift 6's
runtime isolation checks ran the app through every screen without a
trap.
</content>
</entry>
<entry>
<title>v4.7.0: Add domain reputation/blocklist data source</title>
<updated>2026-07-17T15:57:30+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-17T15:39:17+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=6e273c2676ce29cef057d117e4427e031886e743'/>
<id>urn:sha1:6e273c2676ce29cef057d117e4427e031886e743</id>
<content type='text'>
- New DomainReputationResult model (status: clean/listed/unknown, listed
  sources, checked-at) and a `reputation(domain:)` method on ExternalDataService,
  mirroring the existing pluggable-URL enrichment pattern (ownership history,
  DNS history, extended subdomains, pricing). With no endpoint configured
  (the default; DomainDig ships no bundled third-party reputation dependency)
  it resolves to unavailable rather than "clean".
- New .reputation FeatureCapability/DataCapability, gated Pro+ like domainPricing.
- Threaded reputation/reputationError through LookupSnapshot and HistoryEntry
  (backward-compatible decode) so results persist with history entries.
- Auto-fetched in performLookup alongside pricing; surfaced as a "Reputation"
  info row, folded into DomainInsightEngine's risk score/factors and top-level
  insights (a listed domain raises risk score and adds a factor/insight), and
  exported in text, CSV, and JSON report output.
- Reputation-driven risk changes ride the existing change-severity pipeline, so
  a listed status flip is visible to monitoring the same way any other risk
  delta is, without bespoke monitoring wiring.
</content>
</entry>
<entry>
<title>DomainDig v3.5.0: Expand the Pro+ Data+ intelligence layer with deeper local historical context</title>
<updated>2026-04-26T05:39:28+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-04-26T05:39:28+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=237a72a03102319638c5b0572ca3ab543238b821'/>
<id>urn:sha1:237a72a03102319638c5b0572ca3ab543238b821</id>
<content type='text'>
and inferred enrichment.

- add derived intelligence fields for provider fingerprinting, classification,
  ownership transitions, hosting transitions, subdomain history, risk signals,
  and inferred timeline events
- expand DNS history beyond A/NS snapshots to retain A, AAAA, MX, NS, TXT, and
  CNAME change state
- persist enriched intelligence in snapshots and history entries so analysis is
  local-first and incremental
- add a dedicated Data+ Intelligence panel to current and historical domain
  detail views
- surface intelligence events in timeline rows and include Data+ changes in diff
  output
- preserve non-blocking inspection behavior by keeping enrichment additive to
  the main lookup path

This makes Pro+ materially deeper for investigative workflows by improving
historical ownership visibility, infrastructure context, hosting change
detection, subdomain intelligence, and explainable risk signals.
</content>
</entry>
<entry>
<title>feat(v3.2.0): add Data+ tier and external data integrations</title>
<updated>2026-04-22T18:47:37+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-04-22T18:47:37+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/domain-dig.git/commit/?id=6c23daaaba2a954220ece2afa7193dbdcad55c22'/>
<id>urn:sha1:6c23daaaba2a954220ece2afa7193dbdcad55c22</id>
<content type='text'>
- introduce Data+ tier for advanced data features
- add ownership history and DNS history
- expand subdomain discovery with external sources
- add domain pricing insights
- implement local usage/credit system
- integrate external data service layer with rate limiting
- extend export and CLI for Data+ features
</content>
</entry>
</feed>
