# Changelog

All notable changes to callingserver. Dates are release dates; entries are grouped by area.

## 0.5.0 — 2026-10-28

- **SEO**: the landing page and docs are server-rendered with managed `<head>` tags (title, description, canonical,
  Open Graph image, Twitter card, Organization / SoftwareApplication / FAQPage / Article structured data, site
  verification tags, GA4 or Plausible); dynamic `/sitemap.xml` (routes, published pages, docs), `/robots.txt`, `/feed.xml`;
  staff-managed redirects; cache headers on static assets; a proper 404 page.
- **Content pages**: staff write blog posts (`/blog/<slug>/`) and landing pages (`/<slug>/`) in Markdown from the admin
  panel, with excerpt, tags, FAQ schema, draft/publish, preview and per-page meta.
- **Admin SEO section**: settings, built-in page meta with search-result preview, pages editor with live preview,
  redirects, sitemap and robots view, and a crawl-based health check with a score. New staff role `marketing`.
- Docs viewer shell now carries pre-rendered content for crawlers; `SITE_URL` sets the canonical host.

## 0.4.0 — 2026-10-28

- **Plans and invoices**: account tiers (sandbox, starter, growth, scale, custom; USD or INR), monthly close that
  generates one invoice per account (fee + overage, optional GST), invoice lifecycle draft → sent → paid / overdue / void
  with emails, Razorpay payment links and webhook, customer billing page `/portal/billing.html`.
- **Email**: verification links at signup (live access needs a verified email; staff can mark verified), password reset
  for accounts and team members, welcome mail, usage alerts at 80% and 100% of an app's monthly quota. SMTP via
  `SMTP_*`; without it mail goes to `DATA_DIR/outbox.jsonl`.
- **Sales pipeline** in the admin panel: stage, owner, source, next step with due date, notes per account; "due today" list.
- **Data export and deletion**: customers download a zip of everything about their account and can delete the account
  (password + 2FA); staff export and superadmin delete. Invoices and audit entries are retained.
- Metrics page uses plan-based MRR and paid invoices when plans exist.

## 0.3.0 — 2026-10-28

- **Role-based admin panel** (`/admin/`): staff accounts with roles superadmin, ops, finance, support, sales, viewer;
  email/password login (`POST /v1/staff/login`, 12 h sessions, lockout after 5 failures), permissions enforced
  per route in `internal/api/roles.go`; `X-Admin-Key` remains a superadmin for bootstrap. New admin views:
  staff management, per-account team listing, per-app consultations, billing-only pricing edits for finance.
- **Customer team roles** (`/portal/team.html`): account owners invite team members with roles owner, admin,
  developer, finance, support, viewer; members sign in with their own email; the portal hides keys and
  actions the role lacks and the server enforces the same table. Account 2FA stays with the account login.
- Audit log records `staff` and `member` actors; `GET /v1/admin/audit?actor=staff|member`.
- **Monday metrics page** (`/admin/#/metrics`, `GET /v1/admin/metrics`): signups, activation (first API call within 7 days; keys now carry `first_used_at`), live requests, live and paying customers, MRR, relayed minutes, uptime and incidents, weekly chart, funnel, attention list; day-90 targets with commit/stretch and R/A/G status plus cash, burn and runway lines editable with `billing.write` (`PATCH /v1/admin/metrics/targets`, stored in `DATA_DIR/metrics.json`); one-click Monday summary for the team channel.
- Self-service live access: customers click **Request live access** in the portal (`POST /v1/account/live-request`); staff see the queue on the admin Overview and approve or decline with a reason.
- Documentation site at `/docs/` with a Getting started guide and an Errors & limits page; Markdown links render in the browser, raw for curl.
- Contract and matrices: `docs/ROLES.md`.
- Landing page redesigned for bitcall.stream; nginx serves the marketing site on the bare domain (`SITE_DOMAIN`).

## 0.2.0 — 2026-10-27

All items below are implemented and tested on localhost.

Work in progress on `main`, landing after 0.1.0. Each item is tracked separately; nothing here is
guaranteed to ship together.

- **Call recording**: recorder peer / SFU-side capture of consultations for dispute handling.
- **Chat**: text mode for consultations alongside audio/video, over the same room socket.
- **Scheduled consultations**: book a slot ahead of time; the request is raised automatically at the start time.
- **PSTN fallback**: dial out through a telephony provider when a party has no data connection.
- **SFU group calls**: rooms above the peer-to-peer limit routed through an SFU; tokens, rooms and TURN unchanged.
- **Mobile SDKs**: Kotlin (Android) and Swift (iOS) clients mirroring `sdk/js/calling-client.js`.
- **Audit log + 2FA**: admin and portal actions recorded; TOTP second factor for operator and owner accounts.
- **Alerting + status page**: alerts on error rate, relay saturation and webhook failures; public status page.
- **Webhook retries, signatures and replay**: retried delivery with backoff, `X-Webhook-Signature`
  (`v1=` HMAC-SHA256 over `timestamp.body`) and `X-Webhook-Timestamp`, replay of missed deliveries from the portal.
- **Load test tool and capacity report**: scripted multi-peer load generator and a published capacity report per node size.
- **Sample integration**: `examples/astro-platform`, a zero-dependency Node backend for a pay-per-minute
  astrology marketplace (wallets, onboarding, consultation request, webhook billing, mid-call top-up) with a scripted demo.

## 0.1.0 — 2026-10-27

Initial release: a calling-as-a-service platform (TURN/STUN relay, signaling + REST API, peer tokens,
analytics, landing page and portals) plus a metered consultation layer for marketplaces.

### Core calling

- Go signaling server (`cmd/signaling`): stdlib router, multi-tenant REST API, WebSocket signaling hub.
- Applications with hashed API keys (SHA-256, constant-time compare), key rotation, `GET /v1/me`.
- Rooms with participant caps, TTLs and metadata; `POST /v1/rooms`, list/get/delete, `/peers`, `/close`.
- Short-lived HS256 JWT peer tokens bound to app + room + peer id, `peer` and `subscriber` scopes,
  per-app maximum TTL; `peer_id` validated against `^[A-Za-z0-9_.:@-]{1,64}$`.
- `GET /v1/ice`: STUN + TURN servers with time-limited HMAC credentials (`HMAC-SHA1(TURN_SECRET, "<expiry>:<peer>")`).
- WebSocket protocol (`GET /v1/ws?token=`): `welcome`, `peer-joined` / `peer-left`, `offer` / `answer` / `ice`
  relay, `data` messages (direct or broadcast), `stats` samples, `ping` / `pong` keep-alive, server pings every 54 s,
  per-peer send queue of 256 frames that drops slow consumers instead of blocking the hub.
- TURN/STUN relay (`cmd/turnserver`, pion/turn) over UDP, TCP and TLS (TURNS), configurable relay port range,
  private-network peer blocking; coturn as a drop-in alternative (`deploy/coturn.conf`); `cmd/turncheck` CLI
  performs a real allocation with API-issued credentials.
- In-memory call records (`GET /v1/calls`), lifecycle webhooks `call.started`, `peer.joined`, `peer.left`,
  `call.ended` (`duration_seconds`), `stats.reported`; at-most-once delivery with a 5 s timeout.
- Analytics service (`services/analytics`, FastAPI + SQLite): signed event ingestion (`X-Signature` HMAC-SHA256),
  call detail records, per-peer quality samples, daily usage, quality and billing reports, platform summary.
- Browser SDK `sdk/js/calling-client.js`: `CallingClient` (RTCPeerConnection + signaling, incoming/track/meter
  events, automatic stats reporting) and `PresenceClient` (consultant side).
- Security and operations: rate limiting per key/IP (`RATE_LIMIT_RPS`, burst 2x, `429` + `Retry-After`),
  CORS allow-list, WebSocket origin check, 64 KiB frame limit, 1 MiB body limit, Prometheus `/metrics`,
  `/healthz`, `/readyz`, graceful shutdown.
- Landing page (`web/landing`) and two-window call demo (`web/demo`).

### Consultations (metered 1:1 sessions)

- Consultant directory: `PUT/GET/DELETE /v1/consultants/{id}` with `name`, `modes` (`audio`, `video`, `chat`),
  `metadata`; `GET /v1/consultants?status=online`; manual `POST /v1/consultants/{id}/status`.
- Presence socket `GET /v1/presence/ws` with a consultant token (`POST /v1/consultants/{id}/token`):
  connecting sets `online`, disconnecting sets `offline` after 10 s; receives `consultation.requested`,
  `.accepted` (with `consultant_token`), `.ended`, `.cancelled`, `.expired`, `.declined`; sends `accept`,
  `decline`, `status`, `ping`.
- Lifecycle `requested → accepted → active → ended` with `declined`, `cancelled`, `expired` branches;
  ring timeout (`request_timeout_seconds`, default 45 s) and connect timeout (`connect_timeout_seconds`, default 60 s).
- `POST /v1/consultations` takes `max_seconds` (0 = uncapped) and `warn_at_seconds` computed by the platform
  backend from the user's balance; `409` when the consultant is offline, busy, has a pending request or
  does not offer the mode; `402` when the app's monthly quota is reached.
- `accept` creates the room and issues both peer tokens (`u:<user_id>` / `c:<consultant_id>`); `tokens` re-issues
  them; `decline`, `cancel`, `end` (`closed_by_api`) and `extend` (`add_seconds` after a top-up).
- Server-side meter: starts only when both parties are connected, `meter` frames to both peers at start,
  every 15 s, at the warning threshold, after an extension and at the end; hard stop at `max_seconds`
  (`balance_exhausted`, room closed); `user_hangup` / `consultant_hangup` when a side disconnects.
- Billing-grade end event `consultation.ended` with `reason`, `ended_by`, `billed_seconds` (whole seconds
  between both-connected and end, capped), `duration_ms`, `max_seconds`, `mode`; plus `consultation.requested`,
  `.accepted` (both tokens and `room_id`), `.started`, `.warning`, `.extended`, `.declined`, `.cancelled`,
  `.expired` and `consultant.status`.
- Analytics: consultation records (`GET /v1/consultations`, by app/consultant/user/status) and per-consultant
  usage (`requests`, `completed`, `declined`, `missed`, `balance_exhausted`, `billed_minutes`, `avg_seconds`).
- Consultation demo page `web/demo/consultation.html` (astrologer and user panels, live meter, top-up button).

### Accounts and billing

- Self-service developer portal (`/portal/`): `POST /v1/signup` creates an account, a sandbox app and a
  `ck_test_` key immediately; `POST /v1/login` with 24 h sessions; owners manage apps, webhooks, key rotation and usage.
  `SIGNUP_ENABLED=false` disables signup.
- Sandbox vs live: `ck_test_` keys are free, capped at `TEST_MONTHLY_MINUTES`, have smaller room limits, tag
  events with `"mode":"test"` and are never invoiced; live apps require `live_enabled` on the account (admin approval).
- Per-app settings: `max_peers_per_room`, `max_rooms`, `token_ttl_seconds`, `monthly_minutes` (hard cap, 0 = unlimited),
  `included_minutes`, `price_per_minute`, `currency`, with defaults from `PRICE_PER_MINUTE`, `INCLUDED_MINUTES`, `CURRENCY`.
- Usage = relayed room time from first join to last leave, accumulated per calendar month; `usage.quota_reached`
  webhook fires once at the cap and new rooms/consultations return `402` (existing calls are not cut).
- Invoices: `billed_minutes = ceil(used_seconds / 60)`, `billable = max(0, billed − included)`,
  `amount = billable × price_per_minute`; per app (`/usage`, `/invoice?month=`) and across the platform
  (`GET /v1/admin/invoices?month=`).
- Admin dashboard (`/admin/`, `X-Admin-Key`): accounts (approve live mode, disable), apps (create, patch
  settings/webhook, disable, rotate key), usage, invoices, `GET /v1/admin/stats`.

### Deployment

- Docker Compose stack (`deploy/docker-compose.yml`): nginx TLS edge, signaling, Go TURN relay (or coturn via
  `--profile coturn`), analytics; Dockerfiles per service.
- Ubuntu bootstrap `deploy/scripts/setup-server.sh`: installs Docker, opens 80/443, 3478 (udp/tcp), 5349 and
  49152–65535/udp, applies UDP buffer tuning, generates secrets into `.env`, starts all services.
- `make tls-init DOMAIN=` for certbot certificates; systemd units for bare-metal (`deploy/systemd/*.service`).
- Makefile targets: `dev-up` / `dev-down`, `run-signaling` / `run-turn` / `run-analytics`, `test`,
  `create-app`, `smoke`, `turncheck`, `build`.
- Configuration entirely by environment (`.env.example`); shared secrets `TURN_SECRET`, `ANALYTICS_SECRET`,
  `JWT_SECRET`; store is an in-memory map with a JSON snapshot (`DATA_DIR`).
- Documentation: `docs/API.md` (full reference), `docs/ARCHITECTURE.md` (call flow, consultation timers,
  scaling to Redis/Postgres/SFU, performance and security notes).
