Multi-Platform Project (Protocol-First + Parallel Delegation)
When to Use
- A user asks to build the same app on Android + iOS + Desktop (or any multi-platform combination)
- The project has multiple runtime components that share a wire protocol or data contract
- You need to generate a complete project tree with shared libraries and per-platform implementations
- The request is large enough that parallel delegation saves wall-clock time over building each component sequentially
Do not use for:
- Adding a feature to an existing single-platform project (use the platform's own skill)
- A pure library with no platform-specific UI (use
spike or a library-scaffold skill)
- A task where the platforms share so much logic that one codebase (e.g. React Native, Flutter) is a better answer
Core Pattern
1. PROTOCOL FIRST
2. DELEGATE PLATFORMS IN PARALLEL
3. BUILD PRIMARY PLATFORM DIRECTLY IN THIS SESSION
4. VERIFY WITH AVAILABLE TOOLCHAINS
5. CLEAN UP (remove temp scaffolding, verification scripts)
Phase 1 — Protocol First
Before writing platform code, define the wire protocol. This becomes the contract every platform implements against.
Recommended approach
- Protobuf (
.proto file) — strongly typed, cross-platform, code-gen for Rust/Kotlin/Swift/Python
- Alternative: a JSON schema + documented message shapes (lighter weight, no code-gen)
- Keep the proto at the repo root under
protocol/
Structure
project/
├── protocol/
│ └── streamsync.proto ← shared contract
├── shared/ ← cross-platform shared libs
│ ├── rust/ ← core library (serialization, crypto, discovery)
│ └── kotlin/ ← KMP shared module (optional)
├── android/ ← primary platform (Kotlin/Jetpack Compose)
├── ios/ ← companion (Swift/SwiftUI)
├── desktop-rust/ ← companion (Rust + Tauri)
├── desktop-python/ ← companion (Python CLI/GUI)
└── README.md
Key rules
- The proto file must be the single source of truth — every platform reads the same spec
- Define all message types, enums, and wrapper structures up front
- Document the message flow (discovery → handshake → transfer → completion) in the README
- Use protocol versioning for forward compatibility
Alternative: MCP as the Unifying Protocol
For agent-native applications where AI agents need to control every platform, use the Model Context Protocol (JSON-RPC 2.0 over WebSocket/stdio) instead of Protobuf:
project/
├── mcp-spec/
│ └── schema.yaml ← shared MCP tool/resource/event contract
├── rust-core/ ← primary platform (Rust, speaks MCP natively)
├── python-bridge/ ← orchestrator (Python MCP client)
├── clojure-engine/ ← rule engine (Clojure MCP server)
├── web-frontend/ ← Svelte/TypeScript MCP client
├── ios/ ← Swift MCP client (NEDNSSettingsManager)
├── android/ ← Kotlin MCP client (VpnService)
└── cross-build/ ← CI matrix
Each platform implements an MCP client that connects to the Rust core's MCP server on ws://host:9822. The MCP contract (10+ tools for status, block, firewall, config) is the single source of truth — identical tool signatures across every language.
| Platform |
MCP Client Implementation |
Transport |
| Rust |
tokio-tungstenite WebSocket server |
ws:// |
| Python |
websockets library |
ws:// |
| TypeScript |
WebSocket API (browser) |
ws:// |
| Swift |
URLSessionWebSocketTask |
ws:// |
| Kotlin |
raw Socket + buffered I/O |
ws:// |
Advantages over Protobuf:
- No code-gen step — tools are discovered dynamically via
tools/list
- AI agents (Hermes, Claude) can control every platform without language-specific bindings
- Runtime capability discovery — tools self-describe via JSON Schema
- Event subscription model for real-time updates
- Same protocol powers the web dashboard, mobile apps, and agent control
Trade-off: Higher per-message overhead (JSON vs binary), but negligible for control-plane operations.
Phase 2 — Delegate Platform Components in Parallel
Use delegate_task with the batch tasks array to dispatch N platform implementations concurrently.
delegate_task(tasks=[
{
"goal": "Build the Rust core library + Tauri desktop app...",
"context": "Protocol at <path>. Project root at <path>.",
},
{ "goal": "Build the iOS Swift/SwiftUI companion...", "context": "..." },
{ "goal": "Build the Python desktop companion...", "context": "..." },
])
Rules for subagent context
- Be maximally explicit — the subagent has NO conversation history. Include absolute paths, directory trees, file names, and import paths.
- Each subagent owns its own directory — never overlap paths between subagents.
- Give each subagent a
todo plan first — it should plan its files, then write them.
Sibling file conflict warning
When subagents write under the same project root, write_file warns if a sibling modified a file since your last read. The file IS written; acknowledge the warning. If you must re-write a sibling's file, read it first to clear the "last-read" state.
Phase 3 — Build Primary Platform Directly
Build the primary platform (usually the first one the user named) while subagents work.
Android (Kotlin + Jetpack Compose)
| Layer |
Key files |
| Build |
settings.gradle.kts, app/build.gradle.kts, gradle.properties |
| Manifest |
AndroidManifest.xml, file_paths.xml, network_security_config.xml |
| Theme & Nav |
Theme.kt, NavGraph.kt |
| Models |
Models.kt — Device, Transfer, StreamSession, ClipboardEntry, enums |
| Protocol |
ProtocolHandler.kt — JSON serialization, AES-256-GCM, chunking |
| Services |
DiscoveryService.kt (NSD), TransferService.kt (Ktor WS), StreamService.kt (ExoPlayer), ClipboardSyncService.kt |
| UI |
Dashboard, Devices, Transfers, Stream, Settings, Clipboard, DeviceDetail, StreamPlayer |
iOS (Swift + SwiftUI)
| Layer |
Key files |
| Entry |
StreamSyncApp.swift, ContentView.swift |
| Models |
DeviceIdentity.swift, DiscoveredDevice.swift, TransferSession.swift, StreamSession.swift |
| Services |
DiscoveryService.swift (NWBrowser), TransferService.swift (NWConnection), StreamService.swift (AVPlayer), CryptoService.swift, ClipboardService.swift |
| Views |
Dashboard, Devices, Transfer, Stream, Settings, ClipboardSync |
Desktop (Rust + Tauri)
| Layer |
Key files |
| Backend |
src-tauri/Cargo.toml, build.rs, tauri.conf.json |
| Rust |
main.rs (commands), discovery.rs, transfer.rs, crypto.rs, streaming.rs |
| Frontend |
index.html — single-page app, embedded CSS+JS, no build step |
Desktop (Python CLI + GUI)
| Layer |
Key files |
| Config |
pyproject.toml, requirements.txt, README.md |
| Core |
protocol.py, discovery.py (zeroconf), transport.py (websockets), crypto.py (AES-GCM), transfer.py, streaming.py, clipboard.py, config.py |
| UI |
__init__.py (backend auto-detect), cli_app.py (click), qt_app.py (PyQt6), tui_app.py (textual) |
| Server |
server.py (async WebSocket daemon) |
Phase 4 — Ad-Hoc Verification
Create a self-contained Python script that validates what the available toolchains support.
| Platform |
Can verify |
Cannot verify without |
| Rust |
cargo test (compile + unit tests) |
cargo tauri build (needs node + tauri-cli) |
| Python |
py_compile every .py |
PyQt6 GUI (needs display) |
| Kotlin/Android |
Structural: files exist, named correctly |
./gradlew assembleDebug (needs Android SDK) |
| Swift/iOS |
Structural: files exist, named correctly |
xcodebuild (needs macOS + Xcode) |
Always clean up the verifier after running — it's a one-shot tool, not a project artifact.
Phase 5 — Final Inventory
Run a final file count per component (files, lines, KB). Prints a completeness table.
Common Pitfalls
Rust: prost-build
- Add
protoc-bin-vendored = "3" to [build-dependencies]
- Set
PROTOC env var in build.rs
- Generated module name is snake_case of the proto package:
streamsync_message → stream_sync_message
- Use
include!(concat!(env!("OUT_DIR"), "/streamsync.rs")) — not relative
prost 0.13 generates bytes::Bytes for bytes fields; use .to_vec() for Vec
- Delete
target/ after proto changes
Rust: third-party crate quirks
mdns-sd 0.13: ServiceInfo::new(type, instance, host, target, port, props: HashMap<String,String>). ServiceEvent has ServiceResolved and ServiceRemoved(_, name) — no ServiceAdded.
aes-gcm 0.10: Use Aes256Gcm::new_from_slice(key), 12-byte nonce prepended to ciphertext. Import aead::{Aead, KeyInit}, not AeadInOut.
tokio-tungstenite 0.24: Bind TcpListener with SocketAddr, not u16. accept_async returns WebSocketStream<MaybeTlsStream<TcpStream>>.
Python: no protobuf
Hand-write JSON message serialization matching proto shapes. No code-gen needed.
Subagents: don't poll
delegate_task returns immediately. The consolidated result re-enters when all finish. Read live transcripts to watch progress.
Verification Checklist
Reference files in this skill
references/cross-platform-firewall-threads.md — Platform-specific firewall backends, thread pool config for 24-core CPUs, AhoCorasick API migration, Hickory DNS rename, SvelteKit + Tailwind v4 compatibility notes.
references/rust-build-patterns.md — Rust crate selection and build config patterns.
1---2name: multi-platform-project3description: Multi-platform apps: protocol-first, parallel delegation.4---56# Multi-Platform Project (Protocol-First + Parallel Delegation)78## When to Use910- A user asks to build the same app on **Android + iOS + Desktop** (or any multi-platform combination)11- The project has multiple runtime components that share a **wire protocol or data contract**12- You need to **generate a complete project tree** with shared libraries and per-platform implementations13- The request is large enough that **parallel delegation** saves wall-clock time over building each component sequentially1415Do **not** use for:16- Adding a feature to an existing single-platform project (use the platform's own skill)17- A pure library with no platform-specific UI (use `spike` or a library-scaffold skill)18- A task where the platforms share so much logic that one codebase (e.g. React Native, Flutter) is a better answer1920## Core Pattern2122```231. PROTOCOL FIRST242. DELEGATE PLATFORMS IN PARALLEL253. BUILD PRIMARY PLATFORM DIRECTLY IN THIS SESSION264. VERIFY WITH AVAILABLE TOOLCHAINS275. CLEAN UP (remove temp scaffolding, verification scripts)28```2930## Phase 1 — Protocol First3132Before writing platform code, define the wire protocol. This becomes the contract every platform implements against.3334### Recommended approach3536- **Protobuf** (`.proto` file) — strongly typed, cross-platform, code-gen for Rust/Kotlin/Swift/Python37- Alternative: a JSON schema + documented message shapes (lighter weight, no code-gen)38- Keep the proto at the repo root under `protocol/`3940### Structure4142```43project/44├── protocol/45│ └── streamsync.proto ← shared contract46├── shared/ ← cross-platform shared libs47│ ├── rust/ ← core library (serialization, crypto, discovery)48│ └── kotlin/ ← KMP shared module (optional)49├── android/ ← primary platform (Kotlin/Jetpack Compose)50├── ios/ ← companion (Swift/SwiftUI)51├── desktop-rust/ ← companion (Rust + Tauri)52├── desktop-python/ ← companion (Python CLI/GUI)53└── README.md54```5556### Key rules5758- The proto file must be **the single source of truth** — every platform reads the same spec59- Define all message types, enums, and wrapper structures up front60- Document the message flow (discovery → handshake → transfer → completion) in the README61- Use protocol versioning for forward compatibility6263### Alternative: MCP as the Unifying Protocol6465For **agent-native** applications where AI agents need to control every platform, use the **Model Context Protocol** (JSON-RPC 2.0 over WebSocket/stdio) instead of Protobuf:6667```68project/69├── mcp-spec/70│ └── schema.yaml ← shared MCP tool/resource/event contract71├── rust-core/ ← primary platform (Rust, speaks MCP natively)72├── python-bridge/ ← orchestrator (Python MCP client)73├── clojure-engine/ ← rule engine (Clojure MCP server)74├── web-frontend/ ← Svelte/TypeScript MCP client75├── ios/ ← Swift MCP client (NEDNSSettingsManager)76├── android/ ← Kotlin MCP client (VpnService)77└── cross-build/ ← CI matrix78```7980**Each platform implements an MCP client** that connects to the Rust core's MCP server on `ws://host:9822`. The MCP contract (10+ tools for status, block, firewall, config) is the single source of truth — identical tool signatures across every language.8182| Platform | MCP Client Implementation | Transport |83|----------|--------------------------|-----------|84| Rust | `tokio-tungstenite` WebSocket server | ws:// |85| Python | `websockets` library | ws:// |86| TypeScript | `WebSocket` API (browser) | ws:// |87| Swift | `URLSessionWebSocketTask` | ws:// |88| Kotlin | raw `Socket` + buffered I/O | ws:// |8990**Advantages over Protobuf:**91- No code-gen step — tools are discovered dynamically via `tools/list`92- AI agents (Hermes, Claude) can control every platform without language-specific bindings93- Runtime capability discovery — tools self-describe via JSON Schema94- Event subscription model for real-time updates95- Same protocol powers the web dashboard, mobile apps, and agent control9697**Trade-off**: Higher per-message overhead (JSON vs binary), but negligible for control-plane operations.9899## Phase 2 — Delegate Platform Components in Parallel100101Use `delegate_task` with the **batch `tasks` array** to dispatch N platform implementations concurrently.102103```104delegate_task(tasks=[105 {106 "goal": "Build the Rust core library + Tauri desktop app...",107 "context": "Protocol at <path>. Project root at <path>.",108 },109 { "goal": "Build the iOS Swift/SwiftUI companion...", "context": "..." },110 { "goal": "Build the Python desktop companion...", "context": "..." },111])112```113114### Rules for subagent context1151161. **Be maximally explicit** — the subagent has NO conversation history. Include absolute paths, directory trees, file names, and import paths.1172. **Each subagent owns its own directory** — never overlap paths between subagents.1183. **Give each subagent a `todo` plan first** — it should plan its files, then write them.119120### Sibling file conflict warning121122When subagents write under the same project root, `write_file` warns if a sibling modified a file since your last read. The file IS written; acknowledge the warning. If you must re-write a sibling's file, read it first to clear the "last-read" state.123124## Phase 3 — Build Primary Platform Directly125126Build the primary platform (usually the first one the user named) while subagents work.127128### Android (Kotlin + Jetpack Compose)129130| Layer | Key files |131|---|---|132| Build | `settings.gradle.kts`, `app/build.gradle.kts`, `gradle.properties` |133| Manifest | `AndroidManifest.xml`, `file_paths.xml`, `network_security_config.xml` |134| Theme & Nav | `Theme.kt`, `NavGraph.kt` |135| Models | `Models.kt` — Device, Transfer, StreamSession, ClipboardEntry, enums |136| Protocol | `ProtocolHandler.kt` — JSON serialization, AES-256-GCM, chunking |137| Services | `DiscoveryService.kt` (NSD), `TransferService.kt` (Ktor WS), `StreamService.kt` (ExoPlayer), `ClipboardSyncService.kt` |138| UI | Dashboard, Devices, Transfers, Stream, Settings, Clipboard, DeviceDetail, StreamPlayer |139140### iOS (Swift + SwiftUI)141142| Layer | Key files |143|---|---|144| Entry | `StreamSyncApp.swift`, `ContentView.swift` |145| Models | `DeviceIdentity.swift`, `DiscoveredDevice.swift`, `TransferSession.swift`, `StreamSession.swift` |146| Services | `DiscoveryService.swift` (NWBrowser), `TransferService.swift` (NWConnection), `StreamService.swift` (AVPlayer), `CryptoService.swift`, `ClipboardService.swift` |147| Views | Dashboard, Devices, Transfer, Stream, Settings, ClipboardSync |148149### Desktop (Rust + Tauri)150151| Layer | Key files |152|---|---|153| Backend | `src-tauri/Cargo.toml`, `build.rs`, `tauri.conf.json` |154| Rust | `main.rs` (commands), `discovery.rs`, `transfer.rs`, `crypto.rs`, `streaming.rs` |155| Frontend | `index.html` — single-page app, embedded CSS+JS, no build step |156157### Desktop (Python CLI + GUI)158159| Layer | Key files |160|---|---|161| Config | `pyproject.toml`, `requirements.txt`, `README.md` |162| Core | `protocol.py`, `discovery.py` (zeroconf), `transport.py` (websockets), `crypto.py` (AES-GCM), `transfer.py`, `streaming.py`, `clipboard.py`, `config.py` |163| UI | `__init__.py` (backend auto-detect), `cli_app.py` (click), `qt_app.py` (PyQt6), `tui_app.py` (textual) |164| Server | `server.py` (async WebSocket daemon) |165166## Phase 4 — Ad-Hoc Verification167168Create a self-contained Python script that validates what the available toolchains support.169170| Platform | Can verify | Cannot verify without |171|---|---|---|172| **Rust** | `cargo test` (compile + unit tests) | `cargo tauri build` (needs node + tauri-cli) |173| **Python** | `py_compile` every `.py` | PyQt6 GUI (needs display) |174| **Kotlin/Android** | Structural: files exist, named correctly | `./gradlew assembleDebug` (needs Android SDK) |175| **Swift/iOS** | Structural: files exist, named correctly | `xcodebuild` (needs macOS + Xcode) |176177**Always clean up the verifier** after running — it's a one-shot tool, not a project artifact.178179## Phase 5 — Final Inventory180181Run a final file count per component (files, lines, KB). Prints a completeness table.182183## Common Pitfalls184185### Rust: prost-build186187- Add `protoc-bin-vendored = "3"` to `[build-dependencies]`188- Set `PROTOC` env var in `build.rs`189- Generated module name is **snake_case** of the proto package: `streamsync_message` → `stream_sync_message`190- Use `include!(concat!(env!("OUT_DIR"), "/streamsync.rs"))` — not relative191- `prost 0.13` generates `bytes::Bytes` for bytes fields; use `.to_vec()` for Vec192- Delete `target/` after proto changes193194### Rust: third-party crate quirks195196- **`mdns-sd 0.13`**: `ServiceInfo::new(type, instance, host, target, port, props: HashMap<String,String>)`. `ServiceEvent` has `ServiceResolved` and `ServiceRemoved(_, name)` — no `ServiceAdded`.197- **`aes-gcm 0.10`**: Use `Aes256Gcm::new_from_slice(key)`, 12-byte nonce prepended to ciphertext. Import `aead::{Aead, KeyInit}`, not `AeadInOut`.198- **`tokio-tungstenite 0.24`**: Bind `TcpListener` with `SocketAddr`, not `u16`. `accept_async` returns `WebSocketStream<MaybeTlsStream<TcpStream>>`.199200### Python: no protobuf201202Hand-write JSON message serialization matching proto shapes. No code-gen needed.203204### Subagents: don't poll205206`delegate_task` returns immediately. The consolidated result re-enters when all finish. Read live transcripts to watch progress.207208## Verification Checklist209210- [ ] Protocol file: all message types, enums, wrapper envelope defined211- [ ] Protocol version field for forward compat212- [ ] Every platform has complete directory + build config213- [ ] Rust: `cargo check` + `cargo test` pass214- [ ] Python: all `.py` files pass `py_compile`215- [ ] Source files present, named correctly, non-empty216- [ ] Verification ran and cleaned up217- [ ] README documents protocol, architecture, build218- [ ] Project structure matches the plan219220### Reference files in this skill221- `references/cross-platform-firewall-threads.md` — Platform-specific firewall backends, thread pool config for 24-core CPUs, AhoCorasick API migration, Hickory DNS rename, SvelteKit + Tailwind v4 compatibility notes.222- `references/rust-build-patterns.md` — Rust crate selection and build config patterns.