Merge remote-tracking branch 'origin/feat/mesh-diagnostics' into feat/integration-all

# Conflicts:
#	bitchat/Models/CommandInfo.swift
#	bitchat/Services/BLE/BLEOutboundPacketPolicy.swift
#	bitchat/Services/BLE/BLEReceivePipeline.swift
#	bitchat/Services/BLE/BLEService.swift
#	bitchat/Services/CommandProcessor.swift
#	bitchat/Services/Transport.swift
#	bitchat/Sync/SyncTypeFlags.swift
#	bitchat/ViewModels/ChatViewModel.swift
#	bitchatTests/BLEServiceCoreTests.swift
#	localPackages/BitFoundation/Sources/BitFoundation/MessageType.swift
This commit is contained in:
jack
2026-07-06 22:22:40 +02:00
22 changed files with 1320 additions and 8 deletions
@@ -0,0 +1,57 @@
//
// MeshPingPayload.swift
// BitFoundation
//
// This is free and unencumbered software released into the public domain.
// For more information, see <https://unlicense.org>
//
import struct Foundation.Data
/// Wire payload shared by the `ping` (0x26) and `pong` (0x27) message types.
///
/// Layout (9 bytes):
/// - 8 bytes: random nonce (a pong echoes the nonce of the ping it answers)
/// - 1 byte: origin TTL the TTL the packet was launched with, so the
/// receiver can compute the hop count as `originTTL - receivedTTL`.
///
/// Both directions are unencrypted and unsigned: the payload carries no
/// private data, and the unguessable nonce already binds a pong to a probe
/// the local device actually sent.
public struct MeshPingPayload: Equatable {
public static let nonceLength = 8
private static let encodedLength = nonceLength + 1
public let nonce: Data
public let originTTL: UInt8
public init?(nonce: Data, originTTL: UInt8) {
guard nonce.count == Self.nonceLength else { return nil }
self.nonce = nonce
self.originTTL = originTTL
}
public func encode() -> Data {
var data = Data(capacity: Self.encodedLength)
data.append(nonce)
data.append(originTTL)
return data
}
/// Accepts payloads with trailing bytes so future revisions can extend
/// the format without breaking older clients.
public static func decode(_ data: Data) -> MeshPingPayload? {
guard data.count >= encodedLength else { return nil }
let nonce = Data(data.prefix(nonceLength))
let originTTL = data[data.index(data.startIndex, offsetBy: nonceLength)]
return MeshPingPayload(nonce: nonce, originTTL: originTTL)
}
/// Number of links a packet crossed, derived from TTL decrements plus the
/// final delivery link (a directly connected peer is 1 hop away).
/// Returns nil when the TTLs are inconsistent (received above origin).
public static func hopCount(originTTL: UInt8, receivedTTL: UInt8) -> Int? {
guard originTTL >= receivedTTL else { return nil }
return Int(originTTL - receivedTTL) + 1
}
}
@@ -25,14 +25,18 @@ public enum MessageType: UInt8 {
case fragment = 0x20 // Single fragment type for large messages
case fileTransfer = 0x22 // Binary file/audio/image payloads
// 0x23/0x26/0x27 reserved by other in-flight features.
// 0x23 reserved by other in-flight features.
case prekeyBundle = 0x24 // Signed batch of one-time prekeys (gossiped)
case groupMessage = 0x25 // Group-encrypted broadcast (cleartext group ID, ChaChaPoly body)
// Mesh diagnostics
case ping = 0x26 // Directed echo request (nonce + origin TTL)
case pong = 0x27 // Directed echo reply (echoed nonce + origin TTL)
// Gateway mode: signed Nostr event ferried between a mesh-only peer and
// an internet gateway peer.
case nostrCarrier = 0x28
public var description: String {
switch self {
case .announce: return "announce"
@@ -46,6 +50,8 @@ public enum MessageType: UInt8 {
case .fileTransfer: return "fileTransfer"
case .prekeyBundle: return "prekeyBundle"
case .groupMessage: return "groupMessage"
case .ping: return "ping"
case .pong: return "pong"
case .nostrCarrier: return "nostrCarrier"
}
}