Debugging session issues
Common issues and where to look
Session won't start (receiver)
- •check
transport.getState()— should be"listening"afterstartAsReceiver() - •check
transport.getLocalAddress()— must return a validip:port - •on mobile: ensure local network permission is granted (iOS) or INTERNET permission exists (Android)
- •on desktop: check if port 4040 is already in use
files to check:
- •
core/src/session/session.ts—startAsReceiver() - •
core/src/transport/websocket-transport.ts—listen() - •desktop:
apps/desktop-tauri/src/adapters/tauri-ws-server.ts - •mobile:
apps/mobile-rn/src/adapters/rn-ws-server.ts
Sender can't connect
- •verify both devices are on the same Wi-Fi network
- •check the address format: should be
ip:port(e.g.192.168.1.10:4040) - •check
WS_CONNECT_TIMEOUT_MS(10 seconds) — connection may be timing out - •if using discovery: check
DISCOVERY_HOST_TIMEOUT_MS(1.5 seconds per host)
files to check:
- •
core/src/utils/discovery.ts—discoverReceiver() - •
core/src/transport/websocket-transport.ts—connect() - •desktop:
apps/desktop-tauri/src/adapters/browser-ws-client.ts - •mobile:
apps/mobile-rn/src/adapters/rn-ws-client.ts
Handshake fails
- •check that both sides have initialized libsodium:
await initCrypto() - •verify the key exchange: receiver sends CHALLENGE with pk, sender proves with AUTH
- •check
deriveSharedSecret()— both sides must derive the same shared key - •verify sequence numbers are correct (HELLO=1, AUTH=2, etc.)
files to check:
- •
core/src/session/session.ts—handleHello(),handleChallenge(),handleAuth() - •
core/src/crypto/crypto.ts—deriveSharedSecret(),encrypt(),decrypt()
State machine stuck
- •check
VALID_TRANSITIONSincore/src/session/types.ts - •verify the current state allows the expected transition
- •check if
cleanup()was called prematurely (resets to Closed) - •check for errors in the
SessionEvent.Errorhandler
diagnostic steps:
typescript
// add temporary logging in session.ts handleIncoming():
console.log(`[session] state=${this.state} received=${msg.type} seq=${msg.seq}`);
Data not decrypting
- •verify both sides derived the same shared key (key exchange succeeded)
- •check that each DATA message uses a fresh random nonce
- •verify the nonce is 24 bytes and the ciphertext includes the AEAD tag
- •check
MAX_MESSAGE_SIZE(64 KB) — message may exceed the limit
files to check:
- •
core/src/crypto/crypto.ts—encrypt(),decrypt() - •
core/src/session/session.ts—sendData(),handleData()
Auto-regeneration issues
the receiver auto-regenerates sessions every BOOTSTRAP_TTL (10 seconds):
- •check that
cleanup()is called on the old session before starting a new one - •verify new ephemeral keys are generated (not reusing old ones)
- •check
REGENERATION_DELAY_MS(300ms) — there's a small delay between expiry and regeneration - •verify the QR payload updates with the new session ID and public key
files to check:
- •
apps/desktop-tauri/src/screens/ReceiveScreen.tsx— regeneration effect - •
apps/mobile-rn/src/screens/ReceiveScreen.tsx— regeneration effect - •
core/src/session/session-controller.ts—startReceiver(),cleanup()
State transition diagram
code
Created ──────────> WaitingForSender ──> Handshaking ──> PendingApproval ──> Active ──> Closed
│ │ │ │
└── Closed └── Closed ├── Active ├── Rejected ──> Closed
├── Rejected └── Closed
└── Closed
Useful commands
bash
# run core tests to verify session logic npm run test:core # type-check core for compilation errors cd core && npx tsc --noEmit # check if port 4040 is in use lsof -i :4040