Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jamesarich <2199651+jamesarich@users.noreply.github.com> Co-authored-by: James Rich <james.a.rich@gmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
10 KiB
Implementation Plan: TAK v2 Protocol Integration
Branch: tak_v2 | Date: 2026-05-13 | Spec: specs/005-tak-v2-protocol/spec.md
Input: Feature specification from /specs/005-tak-v2-protocol/spec.md
Status: Retroactive — documents existing implementation (PR #5434, 99 files, +4698 lines)
Summary
Upgrades Meshtastic Android's TAK integration from legacy v1 (port 72, PLI + GeoChat only) to TAK v2 (port 78, ATAK_PLUGIN_V2) with zstd dictionary compression and full CoT type coverage. The implementation uses a bidirectional bridge pattern (TAKMeshIntegration) that version-gates output based on firmware capability (Capabilities.supportsTakV2 ≥ 2.8.0) while always accepting inbound traffic on both ports. Compression is provided by the meshtastic/TAKPacket-SDK (JitPack) with platform abstraction via expect/actual for iOS stubs.
Technical Context
Language/Version: Kotlin 2.3+ targeting JDK 21 (KMP multi-target)
Primary Dependencies: TAKPacket-SDK v0.1.3 (zstd compression), xmlutil (CoT XML parsing), Ktor Network (TCP), zstd-jni 1.5.7-7, Okio (I/O), Koin 4.2+ (DI), Kermit (logging)
Storage: App-private filesystem for route KML data packages; bundled .p12/.pem certificates for TLS
Testing: commonTest (9 test classes, 65+ test methods), 40 XML fixture files in jvmAndroidMain/resources/tak_test_fixtures/
Target Platform: Android (primary), JVM Desktop (secondary), iOS (stubs only)
Project Type: Mobile app — KMP module (core:takserver) + UI integration (feature:settings)
Performance Goals: CoT processing < 100ms; compressed PLI < 100 bytes; fits within ~225-byte usable LoRa payload
Constraints: 237-byte raw LoRa MTU (~225 usable after protobuf framing); PARTIAL_WAKE_LOCK for CPU keepalive; mTLS on port 8089
Scale/Scope: 28 CoT type mappings, 2 protocol versions, 3 platform targets, 1 new KMP module + UI screen
Constitution Check
GATE: ✅ All six principles evaluated and satisfied.
-
I. Kotlin Multiplatform Core: ✅ All business logic (TAKMeshIntegration, conversions, type mapper, CoT parser, detail stripper, server manager, models, DI module) resides in
commonMain. Platform-specific code isolated to:jvmAndroidMain: TAKServerJvm (JSSE TLS), TakV2Compressor (zstd-jni via SDK), TakCertLoader, TAKClientConnectionandroidMain: AtakFileWriter (SAF/private dirs), TakPermissionUtil (runtime permissions)jvmMain: AtakFileWriter (desktop filesystem), TakPermissionUtil (no-op)iosMain: TAKServerIos (no-op), TakV2Compressor (uncompressed stub), AtakFileWriter (stub)
-
II. Zero Lint Tolerance: ✅ Verification commands:
./gradlew spotlessApply spotlessCheck detekt :core:takserver:allTests :feature:settings:allTests -
III. Compose Multiplatform UI: ✅
TAKConfigItemList.ktuses Compose Multiplatform components (DropDownPreference,SwitchPreference,TitledCard). No direct Android Jetpack Compose imports. Float values useNumberFormatter.format()where displayed. -
IV. Privacy First: ✅ No PII/location/crypto keys logged. CoT data stays local to device/mesh.
core/protosubmodule not modified (read-only upstream). Certificates bundled as resources, not logged. -
V. Design Standards Compliance: ✅ TAK config UI uses M3 components (SwitchPreference, DropDownPreference). Cross-Platform Spec: TAKPacket-SDK defines shared wire protocol behavior; Android-specific UI is N/A for cross-platform spec (ATAK integration is Android/JVM-only; iOS uses stubs).
-
VI. Verify Before Push: ✅ Local verification:
./gradlew spotlessApply spotlessCheck detekt assembleDebug :core:takserver:allTests :feature:settings:allTests gh pr checks 5434
Project Structure
Documentation (this feature)
specs/005-tak-v2-protocol/
├── plan.md # This file
├── research.md # Phase 0: Technology decisions and rationale
├── data-model.md # Phase 1: Entity models and state machines
├── quickstart.md # Phase 1: Developer onboarding guide
├── contracts/ # Phase 1: Wire protocol contracts
│ └── wire-protocol.md
└── tasks.md # Phase 2 output (/speckit.tasks command)
Source Code (repository root)
core/takserver/
├── build.gradle.kts # Module config + TAKPacket-SDK dependency
└── src/
├── commonMain/kotlin/org/meshtastic/core/takserver/
│ ├── di/CoreTakServerModule.kt # Koin DI wiring
│ ├── TAKMeshIntegration.kt # Bidirectional bridge (main orchestrator)
│ ├── TAKServer.kt # Platform interface (expect)
│ ├── TAKServerManager.kt # Lifecycle + offline queue
│ ├── TAKPacketV2Conversion.kt # CoT ↔ TAKPacketV2 (v2 protocol)
│ ├── TAKPacketConversion.kt # CoT ↔ TAKPacket (v1 legacy)
│ ├── TakV2Compressor.kt # Zstd compression (expect)
│ ├── TakV2TypeMapper.kt # CoT type string ↔ enum (28 mappings)
│ ├── CoTDetailStripper.kt # Strip 16 bloat elements for MTU
│ ├── CoTXmlParser.kt # Streaming XML → CoTMessage
│ ├── CoTXmlDataClasses.kt # Serializable CoT data model
│ ├── CoTXmlFrameBuffer.kt # TCP stream framing
│ ├── CoTXml.kt # CoTMessage → XML serialization
│ ├── CoTConversion.kt # Shared conversion helpers
│ ├── TakConversionHelpers.kt # Coordinate scaling utilities
│ ├── RouteDataPackageGenerator.kt # Route → KML data package
│ ├── TAKDataPackageGenerator.kt # Connection .zip export
│ ├── TAKModels.kt # Domain models
│ ├── TAKDefaults.kt # Constants and defaults
│ ├── TAKPrefXmlDataClasses.kt # ATAK preference XML schema
│ ├── TakFixtureLoader.kt # Test fixture loading (expect)
│ ├── TakMeshTestRunner.kt # In-app diagnostic runner
│ ├── AtakFileWriter.kt # Filesystem abstraction (expect)
│ ├── XmlUtils.kt # XML escaping (5 special chars)
│ └── ZipArchiver.kt # ZIP creation (expect)
├── commonTest/kotlin/.../
│ ├── CoTConversionTest.kt
│ ├── CoTDetailStripperTest.kt
│ ├── CoTXmlFrameBufferTest.kt
│ ├── CoTXmlParserTest.kt
│ ├── CoTXmlTest.kt
│ ├── TAKDefaultsTest.kt
│ ├── TAKPacketConversionTest.kt
│ ├── TAKPacketV2RawDetailTest.kt
│ └── XmlUtilsTest.kt
├── jvmAndroidMain/kotlin/.../
│ ├── TAKServerJvm.kt # JSSE mTLS implementation
│ ├── TAKClientConnection.kt # Per-client state machine
│ ├── TakCertLoader.kt # Certificate loading
│ ├── TakV2Compressor.kt # Zstd actual (via TAKPacket-SDK)
│ ├── TakFixtureLoader.kt # JVM resource loading
│ └── ZipArchiver.kt # java.util.zip actual
├── jvmAndroidMain/resources/
│ ├── tak_certs/ # Bundled mTLS certificates
│ └── tak_test_fixtures/ # 40 CoT XML fixtures
├── androidMain/kotlin/.../
│ └── AtakFileWriter.kt # SAF/private directory writer
├── jvmMain/kotlin/.../
│ └── AtakFileWriter.kt # Desktop filesystem writer
└── iosMain/kotlin/.../
├── TAKServerIos.kt # No-op server
├── TakV2Compressor.kt # Uncompressed stub (flags=0xFF)
├── AtakFileWriter.kt # No-op
├── TakFixtureLoader.kt # No-op
└── ZipArchiver.kt # No-op
feature/settings/src/
├── commonMain/kotlin/.../radio/component/
│ └── TAKConfigItemList.kt # Compose UI (team, role, server toggle)
├── commonMain/kotlin/.../tak/
│ └── TakPermissionUtil.kt # Permission interface (expect)
├── androidMain/kotlin/.../tak/
│ └── TakPermissionUtil.kt # ACCESS_LOCAL_NETWORK (API 37+)
├── jvmMain/kotlin/.../tak/
│ └── TakPermissionUtil.kt # No-op
└── iosMain/kotlin/.../tak/
└── TakPermissionUtil.kt # No-op
core/model/src/commonMain/kotlin/.../
└── Capabilities.kt # supportsTakV2 (>= 2.8.0)
core/service/src/androidMain/kotlin/.../
└── MeshService.kt # PARTIAL_WAKE_LOCK for TAK server
Structure Decision: KMP multi-module architecture. New core:takserver module contains all TAK business logic in commonMain with platform actuals for TLS, compression, and filesystem. UI lives in the existing feature:settings module. Wake lock integration in existing core:service.
Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
jvmAndroidMain shared source set |
TAKPacket-SDK (JitPack) provides JVM-only zstd + protobuf bindings; Android and Desktop share the same JSSE TLS and compression code | Separate androidMain/jvmMain actuals would duplicate ~500 lines of identical code |
| Regex-based XML stripping (not DOM) | CoTDetailStripper uses regex instead of XML DOM to remain dependency-free in commonMain and avoid xmlutil dependency for a simple element-removal task |
Full DOM parsing adds allocation overhead and requires xmlutil import for a ~30-line utility |
Branch name tak_v2 (no prefix) |
Pre-existing feature branch; retroactive documentation | N/A — not a new branch |