EchelonCapture Integration with HandGuard
HandGuard and EchelonCapture can now **share the same latent stream** without redundant sensor uploads. Both apps receive the same `z(t)` latent state from CC-MCS and apply different policies.
Full Public Reader
EchelonCapture Integration with HandGuard
Date: 2025-12-15
Status: ✅ SHARED LATENT STREAM ENABLED
---
Overview
HandGuard and EchelonCapture can now share the same latent stream without redundant sensor uploads. Both apps receive the same `z(t)` latent state from CC-MCS and apply different policies.
Apple Watch (100Hz sensors)
↓
HandGuard uploads as "watch_left_handguard"
↓
CC-MCS computes z(t) for "watch_left_handguard"
↓
Broadcasts to ALL subscribers:
↓ ↓
HandGuard EchelonCapture
(nail-biting policy) (music control policy)---
HandGuard Configuration ✅
Already configured in [CCConfig.swift](cc-handguard/Services/CloudBridge/CCConfig.swift):
static let deviceID = "watch_left_handguard"Both `CCBridgeManager` (upload) and `CCLatentSubscriber` (receive) use this shared device_id.
---
EchelonCapture Configuration
To enable EchelonCapture to receive the same latent stream as HandGuard, configure it to subscribe to the same device_id.
Option A: Subscribe Only (Recommended)
EchelonCapture does NOT upload, only subscribes to HandGuard's stream.
#### Location
Find the latent stream subscription code in EchelonCapture. This is likely in:
- `latent_subscriber.py` (Python)
- `LatentStreamManager.swift` (Swift)
- Or equivalent WebSocket connection code
Change Required
Before (unique device_id):
# Python example
DEVICE_ID = f"echelon_{uuid.uuid4()}" # Random device
ws_url = f"wss://cc-mcs-headless.../visualization?device_id={DEVICE_ID}&subscribe=latent,trajectory"After (shared device_id):
# Python example
DEVICE_ID = "watch_left_handguard" # Match HandGuard
ws_url = f"wss://cc-mcs-headless.../visualization?device_id={DEVICE_ID}&subscribe=latent,trajectory"Swift example:
// Before
private let deviceID = "echelon_\(UUID().uuidString)"
// After
private let deviceID = "watch_left_handguard" // Match HandGuard#### Result
- HandGuard uploads sensor data as `watch_left_handguard`
- CC-MCS broadcasts latent state for `watch_left_handguard`
- EchelonCapture subscribes to `watch_left_handguard`
- Both receive same `z(t)`, apply different policies
- **50
---
Option B: Upload from EchelonCapture (Alternative)
If you prefer EchelonCapture to handle uploads instead:
1. Disable HandGuard uploads:
// In HandGuard CloudBridgeCoordinator.swift
// Comment out:
// await ccBridge.start()2. Configure EchelonCapture to upload as `watch_left_handguard`:
DEVICE_ID = "watch_left_handguard"
# Upload sensors using this device_id3. Configure HandGuard to subscribe only:
// In HandGuard CloudBridgeCoordinator.swift
// Keep only:
await ccLatent.connect()
// Remove:
// await ccBridge.start()Result: EchelonCapture uploads, both apps subscribe to same stream.
---
Backend Behavior
CC-MCS-Headless automatically broadcasts latent state to ALL WebSocket subscribers for a given device_id.
How it works:
1. One client uploads sensor data for `device_id=watch_left_handguard`
2. CC-MCS runs LIM-RPS equilibrium solver
3. CC-MCS broadcasts latent state via WebSocket to ALL clients subscribed to `watch_left_handguard`
4. Multiple clients can receive simultaneously
No backend changes needed - this is already supported!
---
Testing the Integration
### Step 1: Deploy HandGuard
1. Deploy HandGuard to iPhone + Apple Watch
2. Verify uploads working (check console for `✅ CCBridge: Connected`)
3. Verify latent stream working (check `✅ CCLatent: Connected`)
### Step 2: Configure EchelonCapture
1. Update device_id to `"watch_left_handguard"`
2. Remove upload code (optional)
3. Keep only latent subscription
### Step 3: Run Both Apps
1. Start HandGuard on iPhone
2. Start EchelonCapture on same iPhone (or different device)
3. Both should receive same latent state
Step 4: Verify Shared Stream
HandGuard Console:
✅ CCBridge: Uploaded 100 frames
📊 Latent state received (position: [0.23, -0.45, 0.12])EchelonCapture Console:
📊 Latent state received (position: [0.23, -0.45, 0.12]) ← Same values!Both should show identical position vectors at the same timestamps.
---
File Locations
### HandGuard
All configuration in one place:
- [CCConfig.swift](cc-handguard/Services/CloudBridge/CCConfig.swift)
- `deviceID = "watch_left_handguard"`
- Used by CCBridgeManager and CCLatentSubscriber
### EchelonCapture
Find and update your latent subscription code:
- Python: `latent_subscriber.py` or similar
- Swift: `LatentStreamManager.swift` or similar
- Change device_id to `"watch_left_handguard"`
---
Troubleshooting
### "EchelonCapture receives different latent state"
- Cause: Using different device_id
- Fix: Verify both apps use exact same device_id string
### "No latent states in EchelonCapture"
- Cause: HandGuard not uploading, or wrong device_id
- Fix:
1. Check HandGuard console shows `✅ CCBridge: Uploaded`
2. Verify device_id matches exactly
3. Check WebSocket URL includes correct device_id
### "Both apps uploading (redundant)"
- Cause: Both apps have upload code active
- Fix: Choose Option A (HandGuard uploads) or Option B (EchelonCapture uploads)
- Disable upload in one app
### "High battery drain"
- Cause: Both apps uploading sensor data
- Fix: Only one app should upload, both can subscribe
- 50
---
Advanced: Multiple Policy Apps
This architecture supports unlimited policy apps subscribing to the same latent stream:
Watch → HandGuard uploads as "watch_left_handguard"
↓
CC-MCS computes z(t)
↓
Broadcasts to ALL subscribers:
↓ ↓ ↓ ↓
HandGuard Echelon GestureLog Biofeedback
(haptics) (music) (research) (HRV)Each app:
- Subscribes to same device_id
- Receives same latent state
- Applies its own policy
- Independent logic and UI
Benefits:
- Single sensor stream (minimal battery)
- Single upload (minimal network)
- Single computation (efficient)
- Multiple interpretations (flexible)
---
Device ID Naming Convention
Recommended format: `watch_{position}_{primary_app}`
Examples:
- `watch_left_handguard` - Left wrist, HandGuard primary
- `watch_right_echelon` - Right wrist, Echelon primary
- `watch_dual_performance` - Both wrists, performance app
Current: `watch_left_handguard`
To change: Edit `CCConfig.swift` line 40:
static let deviceID = "watch_left_handguard" // Change here---
Migration Guide
From Separate Streams → Shared Stream
Step 1: Record current device_ids
# HandGuard
echo "watch_left_handguard"
# EchelonCapture
# (find in your code)Step 2: Choose primary uploader
- HandGuard (recommended) - already configured
- EchelonCapture - modify HandGuard to disable upload
Step 3: Update subscriber
- If HandGuard uploads → Update EchelonCapture device_id
- If EchelonCapture uploads → Update HandGuard device_id
Step 4: Test
- Run both apps
- Verify same latent state values
- Check battery usage (should drop ~50
---
Summary
Status: ✅ HandGuard configured for shared latent stream
Device ID: `watch_left_handguard` (in CCConfig.swift)
To integrate EchelonCapture:
1. Find latent subscription code
2. Change device_id to `"watch_left_handguard"`
3. Run both apps
4. Verify receiving same latent state
Benefits:
- 50
- 50
- Single computation
- Multiple policies
Files to Update:
- HandGuard: ✅ Already configured (CCConfig.swift)
- EchelonCapture: Update device_id in latent subscriber
---
Ready to integrate! Update EchelonCapture device_id and both apps will share the same latent stream. 🚀
Promotion Decision
Keep in the searchable backlog until it intersects a live paper or system.
Source Anchor
Comp-Core/apps/ios/cc-handguard/ECHELON_INTEGRATION.md
Detected Structure
Method · Figures · Code Anchors · Architecture