---
name: alisay-call
description: Operate, debug, test, deploy, and safely modify the Alisay Call two-person WebRTC video/audio chat app. Use when working on `/Users/swader/repos/alisay`, WebRTC signaling, TURN/coturn credentials, relay-only call failures, browser camera/microphone issues, Nginx WebSocket proxying, or production service checks for this app.
---

# Alisay Call

## Core Workflow

Start every task by reading:

- `/Users/swader/repos/alisay/docs/IMPLEMENTATION_AND_SPEC.md`
- Relevant source files in `/Users/swader/repos/alisay/src/`, `public/`, `tests/`, and `deploy/`

Keep the app minimal: two people, one secret hash URL, no accounts, no database, no chat history, no recordings, no persistent room logs.

Use Bun commands from the repo root:

```bash
bun install
bun run test
bun run typecheck
bun run dev
```

## Task Selection

- For code changes or tests, read `references/local-debugging.md`.
- For browser/WebRTC failures, read `references/webrtc-turn-triage.md`.
- For production deployment or server work, read `references/production-deployment.md`.

## Safety Rules

- Treat room hash fragments, TURN secrets, cookies, certs, and env files as sensitive. Never print secret values.
- Keep server-side signaling schema strict; reject unknown message types instead of relaying arbitrary JSON.
- Keep `/turn-credentials` ephemeral and server-generated; never hardcode TURN passwords in frontend code.
- In production, require `PUBLIC_ORIGIN`, `ALLOWED_ROOM_IDS`, and `ALLOW_OPEN_ROOMS=false`; never publish live allowlist hashes because the signaling protocol admits that hash directly.
- Scope production changes to Alisay-owned paths only. Do not touch unrelated apps on a shared host.
- Do not deploy or mutate production unless the user explicitly asks. When deploying, verify both HTTP and WebSocket paths.

## Expected Architecture

The Bun service serves static files, `/healthz`, `/rooms`, `/ws`, and a disabled-by-default `/turn-credentials` test endpoint.

The browser:

1. Keeps the secret room token in `location.hash`.
2. On bare visits, waits for **Generate room** before creating a hash token; existing hash invite links enable **Start** directly.
3. Registers only `sha256(hash)` through same-origin `POST /rooms`.
4. Opens `/ws` without a query string and sends `sha256(hash)` in the first `join` message.
5. Receives ICE servers only after WebSocket admission and refreshes near-expiry TURN credentials with `refresh-ice`.
6. Requests camera/microphone after signaling and Relay-only preflight succeeds.
7. Uses Relay-only mode by default and Auto mode as an option.
8. Exchanges offer/answer/ICE over the WebSocket.
9. Treats same-tab hash changes as a room switch: stop startup/call, recompute room state from the visible hash, and disable **Copy invite** until that room is registered and admitted over WebSocket.

The signaling server:

1. Allows at most two clients per room.
2. Admits static allowlisted rooms or same-origin generated rooms registered through `/rooms`, with active-room plus pending-reservation capacity checks and a per-IP generated-room cap that counts active generated rooms plus unjoined reservations.
3. Rejects room IDs in WebSocket URLs so proxy logs do not persist join-bearing room identifiers.
4. Relays only validated `offer`, `answer`, `ice`, and `hangup`.
5. Relays negotiation messages only after both peers are ready and negotiation has started.
6. Rate-limits post-upgrade signaling messages and configures WebSocket transport/backpressure caps.
7. Closes upgraded sockets that do not send `join` promptly.
8. Deletes empty rooms and expires inactive/generated rooms, with browser heartbeat keeping live sessions active.
9. Generates coturn REST credentials from `TURN_SECRET` only after room admission.

## Verification Bar

For code changes, run at least:

```bash
bun run test
bun run typecheck
```

For call-path changes, also run:

```bash
bun run test:e2e
```

For manual browser confirmation, use two tabs:

1. Open the same hash URL in both tabs.
2. Start both sides.
3. Confirm local previews appear.
4. Confirm the signaling server emits peer-ready behavior.
5. Confirm mode-specific behavior: Relay-only requires TURN, Auto can run with STUN/TURN config.

For production checks, verify:

- `<PUBLIC_ORIGIN>/healthz`
- WebSocket upgrade on `/ws` without a room query
- a real browser relay-only call receives `relayAvailable: true` without exposing `TURN_SECRET`
- coturn is not an open relay
- the father’s actual network can reach the configured TURN routes

## Handoff Discipline

Update `/Users/swader/repos/alisay/docs/IMPLEMENTATION_AND_SPEC.md` after meaningful behavior, config, deployment, or verification changes. Include what changed, what was verified, and which environment-only checks remain unverified.
