GhostCloak
GhostCloak provides address unlinkability by routing transactions through a TLS relay server. Each transaction gets a fresh session, preventing network-level correlation.
How It Works
Mobile App → GhostCloakClient → WebSocket (TLS) → Relay Server → Midnight Node
- The
GhostCloakClientopens a WebSocket connection to the relay server - A
session_startmessage creates a new session with a uniquesessionId - All Midnight node requests are proxied through the relay via
proxymessages - When done, a
session_endmessage closes the session - The relay discards all session data after cleanup
The relay server never logs transaction content — it only tracks session metadata (ID, start/end times) for billing via the SessionBilling contract.
Architecture
Relay Server
The relay server (packages/relay-server/) is a Node.js WebSocket server with TLS:
- Listens on port 8443 (configurable)
- Upstream connection to Midnight node via WebSocket
- Session management with automatic cleanup (1-hour max age)
- Health endpoint at
/health - Memory and session monitoring every 60 seconds
Session Lifecycle
// Start a cloak session
const session = await ghostCloak.startSession();
// session = { sessionId, startedAt, isActive, relayUrl }
// Proxy a request through the relay
const response = await ghostCloak.proxyRequest(payload);
// End the session
await ghostCloak.stopSession();
Message Protocol
| Message Type | Direction | Fields |
|---|---|---|
session_start | Client → Relay | sessionId |
proxy | Client → Relay | sessionId, payload |
proxy_response | Relay → Client | sessionId, payload |
session_end | Client → Relay | sessionId |
Timeouts
- Connection timeout: 10 seconds
- Proxy request timeout: 30 seconds
- Session cleanup: 1 hour (server-side)
Mobile Integration
On mobile, the RelayClient service (packages/mobile/src/services/relay-client.ts) manages the WebSocket connection:
const client = new RelayClient('wss://relay.nightshield.app:8443');
const session = await client.connect();
// session = { sessionId, startedAt, relayUrl }
// ... use the session ...
await client.disconnect();
The GhostContext in the mobile app exposes startCloak() and stopCloak() methods that wrap the relay client.
Billing
Each relay session is tracked by the SessionBilling Compact contract:
startSession(userId, startTime)— records session startendSession(userId, endTime, fee)— records session end and accumulated fee- Fee calculation:
duration_seconds × fee_rate_per_second(computed off-chain)
See SessionBilling contract for details.
Security Considerations
- The relay server sees the user's IP but not their Midnight address (encrypted in the ZK transaction)
- TLS ensures the connection between client and relay is encrypted
- Each session uses a fresh connection — no session cookies or persistent identifiers
- The relay server should be self-hosted or operated by a trusted party