Skip to content

harsh082ip/zmeet

Repository files navigation

Zmeet — meetings with a pulse

A Google-Meet-class video meeting app with a unique, classy / gen-z vibe. Marketing site + in-room app are built with Astro + Tailwind v4 (Vercel-inspired design language, see DESIGN.md). The entire backend — signaling, presence, chat, and serving the built frontend — is one Go binary.

┌─────────────────────────────┐        WebSocket /ws (signaling, chat, presence)
│  Browser (Astro + WebRTC)   │◀──────────────────────────────────────────────┐
│  • marketing site (/)       │        REST /api/rooms (create / lookup)       │
│  • in-room app (/room/<id>) │◀───────────────────────────────────────────┐  │
└──────────────┬──────────────┘        peer media flows P2P (WebRTC mesh)   │  │
               │ video/audio  ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░  │  │
               ▼              (direct peer-to-peer, never touches server)   │  │
        other participants ◀──────────────────────────────────────────────┘  │
                                                                               │
        ┌──────────────────────────────────────────────────────────────────┐ │
        │  Go backend (cmd/server)  — serves dist/ + relays signaling ───────┘ │
        └──────────────────────────────────────────────────────────────────────┘

Media is peer-to-peer (WebRTC mesh). The Go server only relays the SDP/ICE handshake + room events, so it stays light. For very large rooms this can later be swapped for a Pion SFU without touching the client protocol.

Features

  • Instant rooms — one tap, shareable link zmeet.online/<code>, no install, no sign-in.
  • 🎥 Camera / mic / screen share, device pickers, mirrored self-view.
  • 🟢 Speaking detection, active-speaker rings, pin-to-spotlight, auto grid layout.
  • 💬 In-room chat with unread badges.
  • 📝 Live captions (Web Speech API, broadcast to the room) — the recap story for the marketing site.
  • 🎉 Vibe reactions (floating emoji) + ✋ raise hand.
  • 👥 People panel with live mic/cam/hand presence.
  • ⏱️ No time limits, keyboard shortcuts (d mic, e cam, c chat).

Develop

Config

The backend reads backend/config.json (gitignored — copy from config.json.example):

cd backend && cp config.json.example config.json   # then edit
Field Purpose
addr listen address (:8080)
static_dir built frontend dir; "" = API/WS only (dev), ../dist = prod
public_url origin the browser uses — dev http://localhost:4322, prod https://zmeet.online (used to build the OAuth redirect URI)
log_format text (dev) or json (prod)
log_level debug / info / warn / error
database_url Postgres DSN; empty disables persistence + auth
session_secret long random string that signs the session cookie
google OAuth client_id + client_secret (empty = guest-only, no sign-in)
admin_emails emails that get host powers in any room
user_emails member allowlist (informational for now)

Host (admin) controls

A signed-in user whose email is in admin_emails joins any room as a host. In the People panel they get per-participant controls: mute / unmute, stop / start video, lock mic (prevent unmute), lock leave button, and remove (kick). Hosts show a HOST badge.

Roles are assigned server-side from the session cookie + admin_emails, and host commands are relayed only from verified admins — a regular user faking an admin message is dropped. (Forcing a camera on is honoured directly here since it's an invite-only environment; a public product would prompt the user first.)

Invite-only access

When OAuth is configured, Zmeet is members-only: you must be signed in and listed in admin_emails or user_emails to create or join a meeting. This is enforced server-side — guests/unlisted accounts get 403 on POST /api/rooms and /ws.

A signed-in non-member sees an invite-only screen with a Request an invite button (in the launcher and the room lobby). Clicking it records a row in the invite_requests table. Review them and grant access by editing config.json:

docker exec -it zmeet-postgres psql -U zmeet -d zmeet \
  -c "SELECT email, name, requests, updated_at FROM invite_requests ORDER BY updated_at DESC;"
# add the email to "user_emails" (or "admin_emails") in config.json, then restart the backend

Postgres (local, via Docker)

docker run -d --name zmeet-postgres \
  -e POSTGRES_USER=zmeet -e POSTGRES_PASSWORD=zmeet -e POSTGRES_DB=zmeet \
  -p 5432:5432 postgres:16

DSN: postgres://zmeet:zmeet@localhost:5432/zmeet?sslmode=disable. The users table is created automatically on first boot.

Google sign-in (optional)

  1. In Google Cloud Console → create an OAuth 2.0 Client ID (type: Web application).
  2. Add an Authorized redirect URI: {public_url}/api/auth/google/callback (dev: http://localhost:4322/api/auth/google/callback).
  3. Put the client id/secret into config.jsongoogle.

When configured, a "Sign in" button appears in the nav and the room uses the user's Google name + photo. Without it, everything still works as guests.

Run it (two terminals)

The Astro dev server proxies /api + /ws to the Go backend:

# 1) backend (config.json: static_dir "", public_url http://localhost:4322)
cd backend && go run ./cmd/server

# 2) frontend
npm install
npm run dev            # http://localhost:4322

Open two browser tabs, click New meeting in one, paste the link into the other. (Camera/mic require localhost or HTTPS.)

Deploy (Makefile)

Production is split: Go backend on the Oracle server (api.zmeet.online, systemd zmeet behind nginx) + static frontend on Cloudflare Pages (zmeet.online).

make                 # list targets
make deploy          # backend + frontend
make deploy-backend  # cross-compile arm64, scp binary, restart service, health-check
make deploy-frontend # build with PUBLIC_API_BASE, wrangler pages deploy
make health          # hit prod /healthz, /api/auth/me, and the web root
make logs-backend    # tail server logs

The backend config.json (secrets) lives only on the server and is not pushed by deploy-backend — edit it there (e.g. to add invite emails) and make restart-backend. Override host/key/etc. inline, e.g. make deploy-backend SSH_HOST=1.2.3.4.

Build & run (single binary)

npm run build                       # → dist/
cd backend
go build -o zmeet-server ./cmd/server
# config.json: static_dir "../dist", public_url "https://zmeet.online"
./zmeet-server                      # serves everything on :8080

Logging uses the stdlib log/slog. Each HTTP request is logged with method, path, status, duration and client IP; room create / join / leave events are logged with structured fields. /healthz and /_astro/* are logged at debug to keep info clean.

Notes

  • WebRTC NAT traversal uses public STUN. Cross-network calls behind strict NATs need a TURN server — add one to ICE_SERVERS in src/scripts/room.ts (e.g. a pion/turn instance).
  • Live captions use the browser Web Speech API (best in Chromium).

Layout

src/
  components/      marketing + shared UI (Nav, Hero, Features, …, Icon, Logo)
  layouts/         Layout.astro (fonts, meta, day/night theme)
  pages/
    index.astro    marketing landing
    room/[code].astro   in-room app shell (served for any code by Go)
  scripts/room.ts  WebRTC mesh + signaling client
  styles/global.css  Tailwind v4 theme tokens + components
backend/
  cmd/server/      entrypoint
  internal/signal/ hub (rooms/clients), ws server, message protocol

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors