Privacy-focused P2P security camera system built on Iroh. Stream video from Raspberry Pi cameras to servers and clients over end-to-end encrypted QUIC connections, with no mandatory cloud dependency.
Download the desktop app: macOS | Linux | Windows — from kodama-app
- End-to-end Encryption: All connections use Iroh/QUIC — traffic is encrypted between peers with no central authority
- Adaptive Bitrate: Camera-side ABR with 4 quality tiers (500K-4M), adjusts to network conditions with hysteresis
- Cellular Failover: Automatic WiFi-to-cellular handoff with reconnection and ABR ramp-up (~25s failover)
- Multi-channel Streaming: Video, audio, and telemetry multiplexed as separate channels over a single QUIC stream
- Per-connection Rate Limiting: Lock-free atomic rate limiter with abuse detection (120fps video, 100fps audio, 10fps telemetry)
- Pluggable Storage: Local filesystem or cloud (S3/R2) backends with retention policies and cleanup
- GPS & Telemetry: CPU, memory, temperature, GPS position, and motion detection streamed alongside video
- HTTP API + WebSocket: axum-based HTTP API with WebSocket bridge and fMP4 muxing for browser playback
The core streaming pipeline (camera -> server -> client/browser) is production-ready and deployed on Raspberry Pi hardware with cellular failover.
| Component | Status |
|---|---|
Library (kodama) |
Production - single crate with feature-gated modules |
Firmware (kodama-firmware) |
Production - camera streaming, ABR, reconnection, relay mode |
Server (kodama-server) |
Production - routing, rate limiting, storage, HTTP API |
# Clone and setup
git clone https://github.com/andymitch/kodama.git
cd kodama
./scripts/setup.sh # Pin crypto dependencies for Iroh compatibility
# Build everything
cargo build
# Run tests
cargo test --workspace# 1. Start the server (HTTP API on port 3000, prints public key)
cargo run -p kodama-server
# 2. Start a camera with synthetic test source (no hardware needed)
KODAMA_SERVER_KEY=<key> cargo run -p kodama-firmware --features test-source -- --mode camera --test-source
# 3. Connect via HTTP API, WebSocket, or use kodama-app for a full UIThe server ships with an HTTP API but no bundled UI. To get the full Svelte UI:
# Option 1: Use the desktop app (from kodama-app repo)
# Option 2: Use pre-built static files with the server
KODAMA_UI_PATH=/path/to/kodama-ui/build kodama-serverCamera (capture) --> Iroh QUIC --> Server (Router --> broadcast) --> WS+MSE --> Browser
|
StorageBackend
Cameras open a persistent QUIC stream and push frames. The server detects cameras vs clients by behavior (cameras open a stream within 2s; clients wait). Frames are broadcast to all subscribed clients and optionally written to storage. The web module muxes H.264 into fMP4 and delivers it over WebSocket for MSE playback in browsers.
kodama/
├── crates/kodama/ # Single library crate (feature-gated)
│ # Core types, transport, capture, storage, server, web
├── apps/
│ ├── kodama-firmware/ # Camera or relay (--mode camera|relay)
│ └── kodama-server/ # Headless server + HTTP API
├── pi/ # Pi system configs (gpsd, NetworkManager, systemd)
└── scripts/
├── setup.sh # Pin crypto dependencies
├── pi.sh # Pi management (setup, deploy, wifi-off)
└── test-e2e.sh # Full pipeline test (server + firmware)
| Repo | License | Description |
|---|---|---|
| kodama (this repo) | MIT / Apache-2.0 | Core library, server, firmware |
| kodama-app | BSL 1.1 | Desktop/mobile app + Svelte UI |
default = []
transport — Iroh endpoint, frame mux/demux
capture — Video/audio/telemetry, ABR, H.264
storage — Local + cloud backends (implies transport)
server — Router, RateLimiter, StorageManager (implies transport + storage)
web — axum HTTP, WebSocket bridge, fMP4 muxer (implies server)
test-source — Synthetic video/audio (implies capture)
Wire: [4-byte length prefix][22-byte header][payload]
Header:
┌──────────┬─────────┬───────┬───────────┬────────┐
│ source │ channel │ flags │ timestamp │ length │
│ (8 bytes)│ (1 byte)│(1 byte)│ (8 bytes)│(4 bytes)│
└──────────┴─────────┴───────┴───────────┴────────┘
- Channels: Video (0), Audio (1), Telemetry (2)
- Flags: KEYFRAME (0x01)
- Max payload: 2 MB (enforced on wire)
Kodama runs on a Raspberry Pi Zero 2W with an IMX219 camera sensor, optional GPS (SIM7600G-H), and cellular connectivity.
# First-time setup (installs deps, configures GPS/cellular, deploys binary)
./scripts/pi.sh setup
# Quick deploy after code changes (cross-compile + scp)
./scripts/pi.sh deploy
# Toggle WiFi off for cellular failover testing (auto-restores after N seconds)
./scripts/pi.sh wifi-off 60The deploy script cross-compiles for aarch64-unknown-linux-gnu and pushes the binary over SSH. System configs in pi/ handle GPS daemon setup, NetworkManager cellular routing, and service management.
# All unit tests
cargo test --workspace
# E2E regression suite (real QUIC connections, no hardware)
cargo test -p kodama --test e2e
# Full pipeline test (builds release, runs server + firmware for 20s)
./scripts/test-e2e.shThe E2E suite validates frame flow through the router, 2 MB frame size enforcement, and per-connection rate limiting using real Iroh QUIC endpoints.
| Variable | Description | Default |
|---|---|---|
KODAMA_SERVER_KEY |
Server's base32 public key (required for camera) | - |
KODAMA_KEY_PATH |
Path to persistent keypair file | ./server.key or ./camera.key |
KODAMA_STORAGE_PATH |
Recording storage location (enables recording) | disabled |
KODAMA_STORAGE_MAX_GB |
Maximum storage size in GB | 10 |
KODAMA_RETENTION_DAYS |
Recording retention period | 7 |
KODAMA_BUFFER_SIZE |
Broadcast channel capacity | 512 |
KODAMA_WEB_PORT |
Web server port (server only) | 3000 |
KODAMA_UI_PATH |
Path to static UI build directory | auto-detect |
KODAMA_ABR |
Set to 0 to disable adaptive bitrate |
enabled |
KODAMA_MODE |
Firmware mode: camera or relay |
camera |
KODAMA_UPSTREAM_KEY |
Upstream server key (relay mode only) | - |
RUST_LOG |
Tracing filter (e.g., kodama=debug) |
kodama=info |
- Rust stable (1.75+)
- FFmpeg (for video muxing - server)
- For Pi deployment:
aarch64-unknown-linux-gnucross-compilation toolchain
Iroh depends on pre-release curve25519-dalek and ed25519-dalek which require pinned digest and sha2 versions. After any cargo update, re-run:
./scripts/setup.sh- 100% async with Tokio
anyhow::Result<T>for errors throughout- Work with
Relay,Router, andStorageBackendabstractions - not Iroh primitives directly - Feature flag
test-sourceenables synthetic video/audio without camera hardware
See open issues for the full list. Key priorities:
- #6 - QR code camera registration
- #7 - Production Pi deployment with minimal setup
- #9 - Graceful shutdown with CancellationToken
MIT OR Apache-2.0