Changelog

What's new in CallSee.me

RSS
security

Locks on every door, and a way through the firewall

This one is mostly the security team quietly walking the building and checking that every door actually locks. Magic-link tokens now claim themselves with an atomic `updateMany`, so two clicks racing the same

security

v1 API ships, first-run polish, security backfill

The headline is the v1 REST API. Notes, pins, and files all got proper public endpoints under `/api/v1/meetings/:id/*` — the three shipped on main right after v1.1.57's release cut, so this is the first prod build that exposes them. Notes are atomic. Pins are a

security

Security hardening + admin UX overhaul

The headline is security. A meeting routes audit turned up a cross-org leak — meetings could be read across organization boundaries when the caller knew the URL — plus an SSRF in the URL preview path (relative URLs were being resolved against origin, which

fix

TURN/TLS on 443 + Prisma 7 build fix

v1.1.53 never actually shipped. The release build failed because Prisma 7 wants `DATABASE_URL` resolved at config load instead of at runtime, and the docker image didn't ship the `prisma.config.ts` file

improvement

SDK 0.2.3: actually-correct ESM resolve + publish guard

0.2.2 renamed the dist outputs (`dist/index.js` → `dist/index.cjs`, `dist/index.mjs` stayed) to match what each package's exports map advertised — but I only updated the `require` side. `module` and `exports['.'].import` still pointed at `dist/index.js`, which no

fix

SDK 0.2.2: dist filenames match package.json exports

Hojt's first `npm install` of `@callsee/*` hit a packaging bug. Each package's `package.json` advertised:

improvement

Readable kiosk-mode control bar

Kiosks live across a reception desk or at the back of a meeting room. The control-bar buttons under `?mode=kiosk` were already sized for distance (64×64 with 32px icons) — the labels weren't. `text-xs` (12px)

improvement

Pre-join card no longer stretches

The pre-join `Gå med i mötet` card sat in a flex-row beside the camera preview. Default `align-items: stretch` made the card grow to match the preview's `aspect-video` height (~326px on a normal width)

improvement

Readable connection-status badge

The center-screen `Ansluter` (Connecting) badge in the meeting view sat on a 15%-translucent amber backdrop with same-hue text and border. On amber wind-down backgrounds — or any tinted backdrop — that

improvement

Structured logging for signaling side-effect failures

`server.ts` had a swarm of fire-and-forget `.catch` handlers — DB writes, webhook publishes, status updates, warm-handoff checks, participant-minute tracking — all of them logging free-form prose to stderr and continuing. Operationally that means a brief DB blip during

improvement

TURN: comma-separated URLs for TCP/TLS fallback

Code-side prep for the parent task `callsee-tr5a6` (TURN over TCP + TLS for networks that block UDP entirely — corporate firewalls, hotel WiFi, etc.). Coturn config + the TLS cert for `turn.callsee.me:5349`

fix

Cluster-wide WS client metadata

The sibling fix to v1.1.44 — the same per-pod-Map-as-cluster-control trap, but for read-side metadata instead of write-side counters.

security

Cluster-wide WS connection cap

`WS_MAX_CONNECTIONS_PER_IP = 20` was enforced via an in-memory Map in `server.ts`. Each pod kept its own counter, so the practical cap was `20 × replicas` — running 3 replicas in prod meant a single IP could

feature

SDK 0.2.1: live event subscriber

Hojt's Past Calls panel polls `/api/calls/channel/{id}/past` today because they have no other way to know a decision was recorded or a pinboard item was added. The server-side SSE endpoint (`GET /api/v1/events/stream`) has been live for a while; 0.2.1 ships

improvement

SDK 0.2.0: onJoin / onError callbacks

Hojt's React-component pivot needed lifecycle callbacks beyond `onLeave`. Today they get a silent black box on token failures and have to poll their own backend to learn when the meeting actually connected. Two new props

improvement

Self-Hosted Private npm + Version-Aware SDK

Hojt is migrating off the iframe to a React-component embed. Owner explicitly didn't want a public-npm dependency, so we stood up our own: **Verdaccio in-cluster at `npm.callsee.me`**, hosting only `@callsee/*`,

fix

Self-View in Narrow Iframes

Hojt reported that callsee.me's iframe finally loaded after the v1.1.38 embed-origin fix, but their self-view disappeared the moment they joined a meeting. Pre-join camera worked, in-meeting camera vanished.

improvement

Iframe Embed Actually Works

The iframe embed allow-list shipped in v1.1.37 looked correct from every angle except the one that mattered: integration partners still got a white page. Confirmed live by curling `/meeting/x` — every response came back with `frame-ancestors 'self'` regardless of what was

improvement

TURN for Guests, Iframe Origin Wildcards, Duplicate-Meeting Guard

Some peer-to-peer calls were quietly falling back to STUN-only and failing behind symmetric NAT. Two bugs were stacked. First, the TURN credentials endpoint required a JWT cookie — guests joining via `/meeting/[roomCode]` got a 401 and never received TURN at all. It now

feature

Webhook Wiring and Visual Polish

Two new webhook events (`decision.recorded` and `pinboard.item.added`) now actually fire. They were declared in the schema for a while but never `publishEvent`d — so anyone subscribed was getting a quiet

improvement

Compliance Housekeeping

A tidy-up pass on legal and privacy surfaces. Billing UsageEvents now get purged after seven years — matching Swedish bookkeeping law retention without letting them linger forever. The ToS now carries a clickable link

improvement

PiP Mode and Embed Hardening

The video meeting UI is now fully responsive to its container size. When callsee.me runs inside a 340x192px PiP panel (or any iframe narrower than 400px), it automatically switches to a stripped-down layout: single speaker

improvement

No Cookies in Embed Mode

Third-party cookie restrictions in modern browsers mean an iframe can't reliably set cookies anyway — so when `?embed=true` is in the URL, we now skip the cookie consent banner entirely and return `false` from `hasPreferenceConsent()`. No device preference cookies, no guest name cookies. The meeting still works fine without them; device selection just won't remember your last pick across sessions.

improvement

Streamlined Embedded Lobby

When a partner like Hojt embeds a meeting via iframe with `?displayName=...`, the pre-join screen now skips everything the partner already handled: no name input (the name came from the URL), no privacy/terms text (the parent app has its own), and the button says "Join" instead of "Request to Join." Most importantly, embedded guests join directly without the WebSocket approval flow — no waiting in the lobby.

feature

Partner Pricing Tiers and CSP for Embeds

Two features in this release, both driven by the Hojt partnership.

fix

Redis Pub/Sub Fix and Webhook Auth

This one was a production incident fix. We had 2 Redis replicas behind a ClusterIP, which meant pub/sub messages were landing on whichever replica the load balancer picked — so WebSocket broadcasts between pods were silently dropped about half the time. Scaled Redis to 1 replica, and cross-pod signaling works again.

feature

Screen Sharing and Embed Events

The big addition here is LiveKit SFU screen sharing. Previously, screen sharing was peer-to-peer only, which meant it degraded fast with more than 3-4 participants. With the LiveKit integration, screen shares now route through the SFU, so a 20-person meeting gets the same crisp shared screen as a 1-on-1.