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/app/src/adapters/electron-ws-server.ts - mobile:
apps/app/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/app/src/adapters/web-ws-client.ts - mobile:
apps/app/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:
// 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/app/src/screens/ReceiveScreen.tsx— regeneration effectcore/src/session/session-controller.ts—startReceiver(),cleanup()
State transition diagram
Created ──────────> WaitingForSender ──> Handshaking ──> PendingApproval ──> Active ──> Closed
│ │ │ │
└── Closed └── Closed ├── Active ├── Rejected ──> Closed
├── Rejected └── Closed
└── Closed
Useful commands
# run core tests to verify session logic
pnpm run test:core
# type-check core for compilation errors
cd core && npx tsc --noEmit
# check if port 4040 is in use
lsof -i :4040
Converted and distributed by TomeVault — claim your Tome and manage your conversions.