On-Demand Provisioning

Slice lifecycle from Connect click through QR scan to ready state -- replacing the standby pool

What Changed

Before (Standby Pool)
Gateway maintains 3 idle containers
ensureSlicePool() on every health check
User registers → assigned an idle slice
cleanupExpiredSlices() timer
Orphan containers consuming memory
Port conflicts from stale containers
After (On-Demand)
Zero idle containers
POST /api/connect provisions on click
User registers → slice_id = NULL
Auto-destroy on QR timeout
Resources used only when needed
~7s startup, acceptable latency

Slice Lifecycle

No Slice Connect Click Provisioning (~7s) QR Showing QR Scanned Loading Contacts Ready
Failure path: QR Showing 3 Codes Expire QR Timeout Slice Destroyed No Slice

POST /api/connect Flow

1
User clicks "Connect WhatsApp" — frontend calls POST /api/connect
2
Per-user lock prevents double-click (in-memory Set of provisioning user IDs)
3
Check existing slice — if container running, forward POST /api/status/initialize to it
4
No slice? Call provisionSlice(userId) — creates Docker container, DB schema, waits for healthy
5
Forward initialize to new slice, return { status: 'provisioned', port, sliceId }
6
SSE activates — frontend reconnects SSE, QR codes begin flowing to browser

Removed Code

Function File Reason
ensureSlicePool() provisioner.ts Removed No standby pool needed
assignSliceToUser() provisioner.ts Removed Slice assigned during provisioning
cleanupExpiredSlices() monitor.ts Removed No subscription-based lifecycle
MIN_AVAILABLE provisioner.ts Removed No pool size concept

Connection UX Phases

Phase Duration User Sees
Setup Until click "Connect WhatsApp" button + instructions
Provisioning ~7-15s Progress bar, elapsed timer, status messages
QR Code ~60s QR image, countdown bar, "Code X of 3" counter
Linking 1-3 min "Linked!" + live contact counter ("Found: N contacts")
Ready Instant Auto-redirect to ChatPage
Drill down: Full Provisioning Docs · Connection UX Flow · Stealth Chrome · Back to Project