mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-24 22:45:19 +00:00
Periphery 3.7.4 audit of both schemes (macOS + iOS, intersected so platform-specific code is never touched), with test targets indexed and the share extension built. 277 dead declarations removed or demoted: dead forwarding wrappers (ChatViewModel+Nostr/+PrivateChat), removed- feature remnants (autocomplete command suggestions, back-swipe tuning, MediaSendError, GeohashParticipantTracker), unused Tor dormancy bindings, assign-only properties, unused parameters (renamed to _), and redundant public accessibility. 13 orphaned localization keys deleted across all 29 locales (old pre-#1392 location-notes UI, app_info warnings). Two real tests were flagged as unused because they never ran: Swift Testing methods missing @Test (NostrProtocolTests. testAckRoundTripNIP44V2_Delivered, NotificationStreamAssemblerTests. testAssemblesCompressedLargeFrame). Re-armed both; they pass. Deliberately kept, now recorded in .periphery.baseline.json: iOS-only code invisible to the CI macOS scan, C FFI signatures, keep-alive NWPathMonitor reference, InboundEventKey.eventID (dedup semantics), wifiBulk capability bit (reserved for Wi-Fi bulk work, used by BitFoundation package tests), and the String secureClear cluster (exercised by package tests). New: .periphery.yml config and an advisory Dead Code CI job (mirrors the SwiftLint precedent from #1361) that fails on findings not in the committed baseline. Verified: full macOS app suite, BitFoundation (119) and BitLogger (13) package tests green; periphery scan --strict exits clean. Co-authored-by: jack <jackjackbits@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
69 lines
2.6 KiB
Swift
69 lines
2.6 KiB
Swift
//
|
|
// StoreAndForwardMetrics.swift
|
|
// bitchat
|
|
//
|
|
// This is free and unencumbered software released into the public domain.
|
|
// For more information, see <https://unlicense.org>
|
|
//
|
|
|
|
import BitLogger
|
|
import Foundation
|
|
|
|
/// Privacy-safe local counters for the store-and-forward stack: bare event
|
|
/// tallies with no message IDs, peer identities, or timestamps, so delivery
|
|
/// behavior can be measured on-device without recording who talked to whom.
|
|
/// Log-only surface — nothing here ever leaves the device.
|
|
final class StoreAndForwardMetrics {
|
|
enum Event: String, CaseIterable {
|
|
/// A private message entered the outbox (no prompt route available).
|
|
case outboxQueued = "outbox.queued"
|
|
/// A retained message was re-sent on a flush.
|
|
case outboxResent = "outbox.resent"
|
|
/// A delivery/read ack cleared a retained message.
|
|
case outboxDelivered = "outbox.delivered"
|
|
/// A retained message was dropped (attempt cap, TTL, or overflow).
|
|
case outboxDropped = "outbox.dropped"
|
|
/// We handed sealed mail to a courier.
|
|
case courierDeposited = "courier.deposited"
|
|
/// We accepted sealed mail to carry for a third party.
|
|
case courierAccepted = "courier.accepted"
|
|
/// We handed carried mail to its recipient over a direct link.
|
|
case courierHandedOver = "courier.handedOver"
|
|
/// We pushed carried mail toward a recipient heard via relay.
|
|
case courierRemoteHandover = "courier.remoteHandover"
|
|
/// We split spray copies to another courier.
|
|
case courierSprayed = "courier.sprayed"
|
|
/// Couriered mail addressed to us was opened and delivered.
|
|
case courierOpened = "courier.opened"
|
|
}
|
|
|
|
static let shared = StoreAndForwardMetrics()
|
|
|
|
private let lock = NSLock()
|
|
private var counts: [String: Int]
|
|
private let defaults: UserDefaults
|
|
private static let defaultsKey = "chat.bitchat.storeAndForwardMetrics"
|
|
|
|
init(defaults: UserDefaults = .standard) {
|
|
self.defaults = defaults
|
|
self.counts = defaults.dictionary(forKey: Self.defaultsKey) as? [String: Int] ?? [:]
|
|
}
|
|
|
|
func record(_ event: Event) {
|
|
lock.lock()
|
|
let total = (counts[event.rawValue] ?? 0) + 1
|
|
counts[event.rawValue] = total
|
|
defaults.set(counts, forKey: Self.defaultsKey)
|
|
lock.unlock()
|
|
SecureLogger.debug("📊 S&F \(event.rawValue) → \(total)", category: .session)
|
|
}
|
|
|
|
/// Included in the panic wipe alongside the stores it describes.
|
|
func reset() {
|
|
lock.lock()
|
|
counts = [:]
|
|
defaults.removeObject(forKey: Self.defaultsKey)
|
|
lock.unlock()
|
|
}
|
|
}
|