krz/domain-dig
an ios app for DNS & SSL analysis
clone: git clone https://gitbay.org/krz/domain-dig.git
017785c4ce07cfe026a56d72963eb977fea06440
verified · cmc
author: Christian Cleberg <hello@cleberg.net> · 2026-08-22T07:24:32Z
DomainDig/DomainMonitoringService.swift | 55 ++-------------------- DomainDig/DomainViewModel.swift | 55 ++-------------------- DomainDig/Models.swift | 83 +++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 104 deletions(-) @@ -526,24 +526,7 @@ final class DomainMonitoringService { trackedDomainID: trackedDomainID, note: trackedDomains.first(where: { $0.id == trackedDomainID })?.note ), - inspection: HistoryEntry.Inspection( - dnsSections: snapshot.dnsSections, - sslInfo: snapshot.sslInfo, - httpHeaders: snapshot.httpHeaders, - reachabilityResults: snapshot.reachabilityResults, - ipGeolocation: snapshot.ipGeolocation, - emailSecurity: snapshot.emailSecurity, - mtaSts: snapshot.emailSecurity?.mtaSts, - ptrRecord: snapshot.ptrRecord, - redirectChain: snapshot.redirectChain, - subdomains: snapshot.subdomains, - extendedSubdomains: snapshot.extendedSubdomains, - dnsHistory: snapshot.dnsHistory, - portScanResults: snapshot.portScanResults, - hstsPreloaded: snapshot.hstsPreloaded, - availabilityResult: snapshot.availabilityResult, - suggestions: snapshot.suggestions - ), + inspection: .init(snapshot: snapshot), registration: HistoryEntry.Registration( ownership: snapshot.ownership, ownershipHistory: snapshot.ownershipHistory, @@ -552,22 +535,7 @@ final class DomainMonitoringService { intelligence: HistoryEntry.Intelligence( reputation: snapshot.reputation ), - provenance: HistoryEntry.Provenance( - appVersion: snapshot.appVersion, - resultSource: snapshot.resultSource, - dataSources: snapshot.dataSources, - provenanceBySection: snapshot.provenanceBySection, - availabilityConfidence: snapshot.availabilityConfidence, - ownershipConfidence: snapshot.ownershipConfidence, - subdomainConfidence: snapshot.subdomainConfidence, - emailSecurityConfidence: snapshot.emailSecurityConfidence, - geolocationConfidence: snapshot.geolocationConfidence, - isPartialSnapshot: snapshot.isPartialSnapshot, - validationIssues: snapshot.validationIssues, - resolverDisplayName: snapshot.resolverDisplayName, - resolverURLString: snapshot.resolverURLString, - totalLookupDurationMs: snapshot.totalLookupDurationMs - ), + provenance: .init(snapshot: snapshot), summary: HistoryEntry.Summary( primaryIP: Self.primaryIPAddress(from: snapshot), finalRedirectURL: snapshot.redirectChain.last?.url, @@ -576,24 +544,7 @@ final class DomainMonitoringService { httpGradeSummary: snapshot.httpSecurityGrade ?? snapshot.httpHeadersError, changeSummary: changeSummary ), - failures: HistoryEntry.Failures( - errorDetails: snapshot.errorDetails, - sslError: snapshot.sslError, - httpHeadersError: snapshot.httpHeadersError, - reachabilityError: snapshot.reachabilityError, - ipGeolocationError: snapshot.ipGeolocationError, - emailSecurityError: snapshot.emailSecurityError, - ownershipError: snapshot.ownershipError, - ownershipHistoryError: snapshot.ownershipHistoryError, - ptrError: snapshot.ptrError, - redirectChainError: snapshot.redirectChainError, - subdomainsError: snapshot.subdomainsError, - extendedSubdomainsError: snapshot.extendedSubdomainsError, - dnsHistoryError: snapshot.dnsHistoryError, - domainPricingError: snapshot.domainPricingError, - reputationError: snapshot.reputationError, - portScanError: snapshot.portScanError - ) + failures: .init(snapshot: snapshot) ) history.insert(entry, at: 0) @@ -1942,24 +1942,7 @@ final class DomainViewModel { trackedDomainID: trackedDomainID, note: currentHistoryEntry?.note ), - inspection: HistoryEntry.Inspection( - dnsSections: snapshot.dnsSections, - sslInfo: snapshot.sslInfo, - httpHeaders: snapshot.httpHeaders, - reachabilityResults: snapshot.reachabilityResults, - ipGeolocation: snapshot.ipGeolocation, - emailSecurity: snapshot.emailSecurity, - mtaSts: snapshot.emailSecurity?.mtaSts, - ptrRecord: snapshot.ptrRecord, - redirectChain: snapshot.redirectChain, - subdomains: snapshot.subdomains, - extendedSubdomains: snapshot.extendedSubdomains, - dnsHistory: snapshot.dnsHistory, - portScanResults: snapshot.portScanResults, - hstsPreloaded: snapshot.hstsPreloaded, - availabilityResult: snapshot.availabilityResult, - suggestions: snapshot.suggestions - ), + inspection: .init(snapshot: snapshot), registration: HistoryEntry.Registration( ownership: snapshot.ownership, ownershipHistory: snapshot.ownershipHistory, @@ -1976,22 +1959,7 @@ final class DomainViewModel { intelligenceTimeline: intelligence.timelineEvents, reputation: snapshot.reputation ), - provenance: HistoryEntry.Provenance( - appVersion: snapshot.appVersion, - resultSource: snapshot.resultSource, - dataSources: snapshot.dataSources, - provenanceBySection: snapshot.provenanceBySection, - availabilityConfidence: snapshot.availabilityConfidence, - ownershipConfidence: snapshot.ownershipConfidence, - subdomainConfidence: snapshot.subdomainConfidence, - emailSecurityConfidence: snapshot.emailSecurityConfidence, - geolocationConfidence: snapshot.geolocationConfidence, - isPartialSnapshot: snapshot.isPartialSnapshot, - validationIssues: snapshot.validationIssues, - resolverDisplayName: snapshot.resolverDisplayName, - resolverURLString: snapshot.resolverURLString, - totalLookupDurationMs: snapshot.totalLookupDurationMs - ), + provenance: .init(snapshot: snapshot), summary: HistoryEntry.Summary( primaryIP: Self.primaryIPAddress(from: snapshot), finalRedirectURL: Self.finalRedirectTarget(from: snapshot), @@ -2004,24 +1972,7 @@ final class DomainViewModel { changeCount: domainDiff?.changeCount ?? changeSummary?.changedSections.count ?? 0, severitySummary: changeSummary?.severity ), - failures: HistoryEntry.Failures( - errorDetails: snapshot.errorDetails, - sslError: snapshot.sslError, - httpHeadersError: snapshot.httpHeadersError, - reachabilityError: snapshot.reachabilityError, - ipGeolocationError: snapshot.ipGeolocationError, - emailSecurityError: snapshot.emailSecurityError, - ownershipError: snapshot.ownershipError, - ownershipHistoryError: snapshot.ownershipHistoryError, - ptrError: snapshot.ptrError, - redirectChainError: snapshot.redirectChainError, - subdomainsError: snapshot.subdomainsError, - extendedSubdomainsError: snapshot.extendedSubdomainsError, - dnsHistoryError: snapshot.dnsHistoryError, - domainPricingError: snapshot.domainPricingError, - reputationError: snapshot.reputationError, - portScanError: snapshot.portScanError - ) + failures: .init(snapshot: snapshot) ) if updateCurrentState { @@ -1267,6 +1267,86 @@ struct MonitoringPendingAlert: Codable, Identifiable, Equatable, Sendable { } } + +// Declared in an extension so the struct keeps its synthesized memberwise +// initializer: adding one inside the body would suppress it. +extension HistoryEntry.Inspection { + /// Every field here is copied straight off the snapshot, and both call + /// sites did exactly that. Naming it once keeps them from drifting. + init(snapshot: LookupSnapshot) { + self.init( + dnsSections: snapshot.dnsSections, + sslInfo: snapshot.sslInfo, + httpHeaders: snapshot.httpHeaders, + reachabilityResults: snapshot.reachabilityResults, + ipGeolocation: snapshot.ipGeolocation, + emailSecurity: snapshot.emailSecurity, + mtaSts: snapshot.emailSecurity?.mtaSts, + ptrRecord: snapshot.ptrRecord, + redirectChain: snapshot.redirectChain, + subdomains: snapshot.subdomains, + extendedSubdomains: snapshot.extendedSubdomains, + dnsHistory: snapshot.dnsHistory, + portScanResults: snapshot.portScanResults, + hstsPreloaded: snapshot.hstsPreloaded, + availabilityResult: snapshot.availabilityResult, + suggestions: snapshot.suggestions + ) + } +} + +// Declared in an extension so the struct keeps its synthesized memberwise +// initializer: adding one inside the body would suppress it. +extension HistoryEntry.Provenance { + /// Every field here is copied straight off the snapshot, and both call + /// sites did exactly that. Naming it once keeps them from drifting. + init(snapshot: LookupSnapshot) { + self.init( + appVersion: snapshot.appVersion, + resultSource: snapshot.resultSource, + dataSources: snapshot.dataSources, + provenanceBySection: snapshot.provenanceBySection, + availabilityConfidence: snapshot.availabilityConfidence, + ownershipConfidence: snapshot.ownershipConfidence, + subdomainConfidence: snapshot.subdomainConfidence, + emailSecurityConfidence: snapshot.emailSecurityConfidence, + geolocationConfidence: snapshot.geolocationConfidence, + isPartialSnapshot: snapshot.isPartialSnapshot, + validationIssues: snapshot.validationIssues, + resolverDisplayName: snapshot.resolverDisplayName, + resolverURLString: snapshot.resolverURLString, + totalLookupDurationMs: snapshot.totalLookupDurationMs + ) + } +} + +// Declared in an extension so the struct keeps its synthesized memberwise +// initializer: adding one inside the body would suppress it. +extension HistoryEntry.Failures { + /// Every field here is copied straight off the snapshot, and both call + /// sites did exactly that. Naming it once keeps them from drifting. + init(snapshot: LookupSnapshot) { + self.init( + errorDetails: snapshot.errorDetails, + sslError: snapshot.sslError, + httpHeadersError: snapshot.httpHeadersError, + reachabilityError: snapshot.reachabilityError, + ipGeolocationError: snapshot.ipGeolocationError, + emailSecurityError: snapshot.emailSecurityError, + ownershipError: snapshot.ownershipError, + ownershipHistoryError: snapshot.ownershipHistoryError, + ptrError: snapshot.ptrError, + redirectChainError: snapshot.redirectChainError, + subdomainsError: snapshot.subdomainsError, + extendedSubdomainsError: snapshot.extendedSubdomainsError, + dnsHistoryError: snapshot.dnsHistoryError, + domainPricingError: snapshot.domainPricingError, + reputationError: snapshot.reputationError, + portScanError: snapshot.portScanError + ) + } +} + struct TrackedDomain: Codable, Identifiable, Equatable { let id: UUID var domain: String @@ -2436,6 +2516,7 @@ struct HistoryEntry: Identifiable, Codable { var hstsPreloaded: Bool? = nil var availabilityResult: DomainAvailabilityResult? = nil var suggestions: [DomainSuggestionResult] = [] + } /// Who owns the domain and where it is hosted. @@ -2474,6 +2555,7 @@ struct HistoryEntry: Identifiable, Codable { var resolverDisplayName: String var resolverURLString: String var totalLookupDurationMs: Int? = nil + } /// Precomputed display values and change tracking. @@ -2508,6 +2590,7 @@ struct HistoryEntry: Identifiable, Codable { var domainPricingError: String? = nil var reputationError: String? = nil var portScanError: String? = nil + } init(identity: Identity,