Add capability bits to announce TLV

Announces now carry an optional capabilities TLV (0x05): a little-endian
bitfield with named bits for upcoming features (prekeys, wifiBulk,
gateway, groups, board, vouch, meshDiagnostics). Old clients skip the
unknown TLV; peers without it decode as nil so features can distinguish
"legacy peer" from "advertises nothing".

PeerCapabilities lives in BitFoundation with a minimal-length encoding
that preserves unknown bits for forward compatibility. Peer capabilities
are stored in the BLE peer registry on verified announce and exposed via
BLEService.peerCapabilities(_:). The local advertisement set is empty
until each feature ships its bit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jack
2026-07-06 19:44:55 +02:00
co-authored by Claude Fable 5
parent 7341696280
commit 688b954fb8
7 changed files with 182 additions and 5 deletions
+37
View File
@@ -1,3 +1,4 @@
import BitFoundation
import Foundation
import Testing
@@ -108,6 +109,42 @@ struct PacketsTests {
#expect(decoded.directNeighbors == nil)
}
@Test
func announcementPacketRoundTripsCapabilities() throws {
let capabilities: PeerCapabilities = [.prekeys, .board, .meshDiagnostics]
let packet = AnnouncementPacket(
nickname: "alice",
noisePublicKey: Data(repeating: 0x11, count: 32),
signingPublicKey: Data(repeating: 0x22, count: 32),
directNeighbors: nil,
capabilities: capabilities
)
let encoded = try #require(packet.encode())
let decoded = try #require(AnnouncementPacket.decode(from: encoded))
#expect(decoded.capabilities == capabilities)
}
@Test
func announcementPacketWithoutCapabilitiesDecodesNilAndUnknownBitsSurvive() throws {
let legacy = try #require(
AnnouncementPacket(
nickname: "alice",
noisePublicKey: Data(repeating: 0x11, count: 32),
signingPublicKey: Data(repeating: 0x22, count: 32),
directNeighbors: nil
).encode()
)
// The TLV is emitted only when capabilities are set, so legacy peers
// (and this packet) decode as nil rather than empty.
#expect(try #require(AnnouncementPacket.decode(from: legacy)).capabilities == nil)
var withFutureBits = legacy
withFutureBits.append(makeTLV(type: 0x05, value: Data([0x80, 0x01])))
let decoded = try #require(AnnouncementPacket.decode(from: withFutureBits))
#expect(decoded.capabilities?.rawValue == 0x0180)
}
@Test
func privateMessagePacketRejectsUnknownTypeAndTruncation() {
let unknownTLV = Data([0x7F, 0x01, 0x41])