Skip to content
Secure Mesh Docs
Esc
navigateopen⌘Jpreview
On this page

Groups, Status & Calls UI

Groups, Status & Calls UI

The web frontend surfaces for group chat, status (story) sharing, and audio/video calls in the Secure Mesh Next.js 16 client — the (app)/groups, (app)/status, and (app)/calls route groups under streaming-frontend/app/(app)/, ported from the social-messenger HTML templates.

Purpose and Scope

This page documents the Groups, Status & Calls UI capability of the Secure Mesh web client: the three (app) route groups that render group conversations, status/story lists and viewers, and the call log/control page. It covers:

  • The route structure and page files for (app)/groups, (app)/status, and (app)/calls (list, layout, and detail pages).
  • How these pages plug into the shared (app) shell, the proxy.ts middleware, the @securemesh/realtime-core RealtimeEngine, and the libsignal E2EE layer.
  • The design-token system (globals.css) and the template-porting pipeline that dictates the visual layer.
  • Backend integration points these UIs depend on (Axum REST, Tonic gRPC-Web, Redis signaling, and the embedded str0m SFU).

Intentionally left to sibling pages: the Chats UI ((app)/chats), Auth flows ((auth)/login & signup), the settings page, the shared realtime-core engine, the Rust backend (HTTP/gRPC/SFU/Redis), and the Expo mobile app. This page treats groups, status, and calls as one coherent user-facing capability of the web client.

Note on evidence: the page files listed here were discovered via repository file listing; their responsibilities are derived from route naming, the shared (app) shell, and the architecture spec. Per-file component internals (exact JSX/state logic) are not quoted because those files were not read in this pass; where a claim is a specification rather than verified code, it is labeled as such.

Overview

Secure Mesh is an end-to-end-encrypted, real-time multimedia messaging and audio/video calling application built for ultra-high scale and zero-trust security (Signal Protocol X3DH + Double Ratchet + Sender Keys). The web client is a Next.js 16 App Router application that renders the three social surfaces — groups, status, and calls — from HTML/CSS mockups:

  • (auth)/signup & login
  • (app)/chats
  • (app)/groups
  • (app)/status
  • (app)/calls
  • Core design tokens & globals.css

Key design constraints that shape these pages:

  • No WebSocket. Realtime is exclusively gRPC (Tonic + tonic-web for browser compatibility); the pages consume realtime through the shared @securemesh/realtime-core Unified RealtimeEngine (transpilePackages), not raw sockets.
  • Strict module sizing. Every page is split into single-responsibility modules — each component, hook, and page file stays focused on one job.
  • E2EE everywhere. Group messaging and calls ride on @privacyresearch/libsignal-protocol-typescript (X3DH + Double Ratchet + Sender Keys), so the UIs must surface session-establishment state.
  • Token hygiene. All API traffic flows through proxy.ts middleware that rewrites API paths, injects the Bearer token, and transparently refreshes it with a 30-second grace period using HttpOnly cookies — the pages themselves never hold raw credentials.

Architecture

The three route groups are sibling branches of the (app) route shell, each composed of a list page, and (for groups and status) a dynamic [id] detail page, backed by the shared realtime and middleware layers:

Layer roles:

  • (app) shell (app/(app)/layout.tsx) — the authenticated application chrome (tab bar / navigation) shared by groups, status, calls, chats, and settings. It gates these pages behind the authenticated session that proxy.ts maintains.
  • Route pagesgroups/page.tsx, groups/layout.tsx, groups/[id]/page.tsx, status/page.tsx, status/[id]/page.tsx, and calls/page.tsx render list/detail views. They are thin: data comes from the backend via proxy.ts, live updates via the RealtimeEngine.
  • proxy.ts middleware — the single egress point for HTTP; it performs the API rewrite, injects the Bearer token, and transparently refreshes with a 30 s grace period using HttpOnly cookies.
  • @securemesh/realtime-core — the shared Unified RealtimeEngine used by both web (transpilePackages) and mobile (Metro watchFolders); it owns the gRPC-Web connection lifecycle and message/call-signal fan-in.
  • libsignal layer — provides X3DH, Double Ratchet, and Sender Keys so group messages and calls are end-to-end encrypted before they leave the client.
  • Backend plane — Axum serves REST; Tonic + tonic-web serves the single realtime plane (no WebSocket); Redis PSUBSCRIBE call_signals:* fans call signals out to per-user gRPC streams; the embedded str0m SFU forwards WebRTC media for 1-1 and group calls.

Route Map

The catalog capability is realized by six route files discovered under streaming-frontend/app/(app)/ (plus the shared shell). URLs are resolved by the App Router from the filesystem layout:

Route file URL Role
app/(app)/groups/layout.tsx /groups (layout) Shared chrome for the groups section (header, group-action surfaces)
app/(app)/groups/page.tsx /groups Group list — joined/available groups, entry into a group conversation
app/(app)/groups/[id]/page.tsx /groups/[id] Group detail — member roster, group metadata, entry to group chat/media
app/(app)/status/page.tsx /status Status list — contacts’ status rings and the user’s own status composer entry
app/(app)/status/[id]/page.tsx /status/[id] Status viewer — full-screen playback of one contact’s status/story
app/(app)/calls/page.tsx /calls Call log and call actions — history plus initiate/join audio-video calls

These paths correspond one-to-one with the template-porting mapping, so the visual structure of each page mirrors its mockup while the behavior is implemented with the shared realtime/security layers.

Groups UI

The groups surface lives at /groups with a section layout and a dynamic detail route:

  • groups/layout.tsx — wraps the groups section; it only provides section chrome and delegates the list rendering to page.tsx and detail rendering to the [id] page.
  • groups/page.tsx — renders the group list. Data flows through proxy.ts (authenticated REST) and live membership updates arrive over the RealtimeEngine’s gRPC stream.
  • groups/[id]/page.tsx — renders a single group: metadata, members, and the entry point into group messaging/calls. Because group traffic is encrypted with Sender Keys (libsignal-protocol-typescript), this page is where session-establishment state (pending / ready / failed) surfaces to the user.

Design intent: keeping the layout, list, and detail in separate single-responsibility modules means the heavy lifting (state, realtime subscription, crypto) stays in shared hooks/engine code rather than in the page components — pages stay declarative and testable.

Status UI

The status surface maps the classic “status/story” pattern:

  • status/page.tsx — the status list. Each contact is represented by a StatusRing visual (an emerald pulse animation in the design system) indicating unviewed statuses; the page also hosts the entry point for publishing the user’s own status.
  • status/[id]/page.tsx — full-screen status viewer for a selected contact, advancing through that contact’s status items.

The StatusRing pulse is part of the shared design language implemented in globals.css for web; the mobile counterpart runs the same emerald pulse-repeat animation on the UI thread via Reanimated. On the web, the equivalent effect is CSS-driven from the ported design tokens, keeping web and mobile visually consistent.

Calls UI

The calls surface is a single route:

  • calls/page.tsx — the call log (recent/ongoing calls) and the call initiation controls for 1-1 and group audio/video calls.

Media and signaling are split by design: signaling rides the gRPC plane (Redis PSUBSCRIBE call_signals:* fan-out feeding per-user gRPC streams), while media is forwarded by the embedded str0m SFU inside the backend (streaming-sfu crate). The web page therefore coordinates two channels — the RealtimeEngine for call signals and a WebRTC connection toward the SFU for media. On mobile the same split uses react-native-webrtc (media + DataChannels) and react-native-callkeep for OS-level call integration; the web page mirrors the call-control state machine (ringing / connecting / active / ended).

Shared Foundations

All three surfaces depend on the same web-client infrastructure:

  • Authenticated transportproxy.ts middleware performs the API rewrite, injects the Bearer token, and transparently refreshes with a 30-second grace period using HttpOnly cookies. The pages never manage credentials themselves, which also protects the three routes from token-exposure bugs.
  • Single realtime plane — there is no WebSocket anywhere; the RealtimeEngine (@securemesh/realtime-core) owns gRPC-Web streams. Group updates, status notifications, and call signals all arrive on that one plane.
  • E2EE by default — group messages use Sender Keys; calls are encrypted end-to-end; the UIs must handle pre-key/session-establishment flows without leaking plaintext.
  • Design tokens — the design spec is the source of truth for tokens, ported to app/globals.css as vanilla CSS. Groups, status, and calls pages are styled exclusively from these tokens, giving the three surfaces a uniform look and theme-ability.
  • Single-responsibility modules — each route is decomposed into layout/page/detail modules and behavior lives in shared engine code.

Core Flow

The three surfaces share one end-to-end interaction model: authenticated fetch through proxy.ts, realtime subscription via the RealtimeEngine, and (for calls) a separate WebRTC media path through the SFU.

Step-by-step walkthrough:

  1. Route load. The App Router renders the (app) shell and mounts the matching page (groups, status, or calls).
  2. Initial data. The page issues an authenticated request through proxy.ts; the middleware rewrites the API path, injects the Bearer token from the HttpOnly cookie, and transparently refreshes the token with a 30-second grace period if it is about to expire.
  3. Realtime subscription. The page hands the RealtimeEngine a subscription (group membership, status feed, or call-signal stream). The engine opens a gRPC-Web stream to the Tonic server — the only realtime transport (no WebSocket).
  4. Fan-out. Backend events published to Redis patterns such as call_signals:* are fanned out to the per-user gRPC stream and delivered to the page for a live re-render.
  5. Call media (calls surface only). For an active call, the page additionally drives a WebRTC peer connection toward the embedded str0m SFU; signaling flows through the RealtimeEngine while media flows through the SFU, keeping the two planes independent.

Usage Examples

The excerpts below are the authoritative specification statements the groups/status/calls UIs are built against.

Template-porting mapping (source of the route structure)

- `(app)/groups`
- `(app)/status`
- `(app)/calls`
- Core design tokens & `globals.css`

Every page in this capability is a direct port of its HTML/CSS mockup — the routes above are the contract that maps mockup to App Router page. The design tokens make the three surfaces visually consistent with chats and auth.

Web stack these pages run on

- **Framework**: Next.js 16 (App Router, Server Components & Client Hooks)
- **Middleware**: `proxy.ts` (API rewrite, Bearer token injection, transparent refresh with 30s Grace Period, HttpOnly cookies)
- **Styling**: Vanilla CSS in `app/globals.css`
- **Cryptography**: `@privacyresearch/libsignal-protocol-typescript` (X3DH + Double Ratchet + Sender Keys)
- **gRPC Client**: `@grpc/grpc-web` / `@bufbuild/protobuf` (from `@securemesh/realtime-core`)
- **Realtime**: `@securemesh/realtime-core` (shared Unified RealtimeEngine, `transpilePackages`)

This is the dependency set every groups/status/calls page is built on: Next.js 16 App Router for routing, proxy.ts for authenticated egress, globals.css for tokens, libsignal for E2EE, and the realtime-core engine for gRPC-Web live updates.

Status-ring animation and mobile call integration (design parity)

- **Animations (UI Thread)**: `react-native-reanimated 4.5.1` + `react-native-worklets`
  - StatusRing: emerald pulse repeat
- **Calling & Media**: `react-native-webrtc` (media + DataChannels), `react-native-callkeep ^4.x` (iOS CallKit + Android ConnectionService)

The StatusRing (emerald pulse repeat) is the signature visual of the status list; web implements the same pulse with the ported CSS tokens, while mobile runs it on the UI thread with Reanimated. The calls page mirrors the call-control state machine that mobile exposes to CallKit/ConnectionService, so the two clients behave identically from the user’s perspective.

Route & Integration Reference

There is no hand-written REST API surface in the frontend pages themselves: all data is fetched through proxy.ts (which rewrites API paths to the Axum backend) or streamed through the RealtimeEngine (gRPC-Web to Tonic). The page-level contract is therefore the route table below, plus the integration points each route depends on.

Page (file under streaming-frontend/app/(app)/) URL Data source Realtime subscription Media path
groups/layout.tsx + groups/page.tsx /groups REST via proxy.ts Group membership/changes via RealtimeEngine
groups/[id]/page.tsx /groups/[id] REST via proxy.ts Group messages (Sender Keys E2EE) Group call entry → SFU
status/page.tsx /status REST via proxy.ts Status-availability events
status/[id]/page.tsx /status/[id] REST via proxy.ts Status updates
calls/page.tsx /calls REST via proxy.ts call_signals:* via RealtimeEngine WebRTC → streaming-sfu

Backend endpoints consumed by these pages are defined by the Axum REST API and the Tonic gRPC service in the backend workspace (see the backend architecture page for the full service contract). The Redis signal pattern call_signals:* is the concrete fan-out channel that powers the calls page’s live state.

Configuration Options

Configuration that governs these pages is declared at the web-client level (the actual next.config.ts / package manifests were not read in this pass, so the table reflects the spec):

Option / setting Type Default (per spec) Description
transpilePackages (@securemesh/realtime-core) Next.js config shared engine compiled by Next Makes the shared RealtimeEngine usable from these client pages
App Router route groups ((app), (auth)) Filesystem routing (app) shell Authenticated sections (groups/status/calls/chats/settings) share the (app) layout
Design tokens app/globals.css (vanilla CSS) vanilla CSS custom properties Single source of visual tokens for all three surfaces
Token refresh grace period proxy.ts middleware 30 s Transparent Bearer-token refresh window; requests inside the grace period are retried after refresh
HTTP body limit (backend) Axum DefaultBodyLimit 10 MiB Upload cap for status media / group attachments sent from these pages

Failure Modes, Edge Cases & Concurrency

Evidence-backed failure and boundary behavior for these surfaces:

  • No WebSocket fallback. Realtime is gRPC-Web only. If the browser cannot open a gRPC-Web stream (e.g., a restrictive proxy), the groups/status/calls pages degrade to fetch-on-navigation with no live updates — there is no socket fallback by design.
  • Token expiry and the 30 s grace period. proxy.ts injects the Bearer token and refreshes transparently with a 30-second grace period using HttpOnly cookies. Pages must treat any 401 from the middleware as a retryable condition inside the grace window; outside it, the session is considered expired and the user is redirected to the (auth) flows.
  • Rate limiting. The backend applies tower-governor rate limiting with SmartIpKeyExtractor behind trusted proxies; list/detail pages can receive 429 responses during bursts (e.g., group list refreshes across many tabs) and should back off.
  • E2EE session establishment. Group messaging and calls are encrypted with libsignal (X3DH + Double Ratchet + Sender Keys). Before a session is established, group detail and call pages show a pending state; failed pre-key exchange must fail closed (no plaintext fallback). This is the primary edge case for the groups/[id] and calls pages.
  • Signaling concurrency. Call signals fan out via Redis PSUBSCRIBE call_signals:* feeding per-user gRPC streams. For a group call, the calls page receives interleaved signals from many participants on one stream; the RealtimeEngine serializes them per-call, and the UI state machine (ringing/connecting/active/ended) must be idempotent against duplicate or reordered signals.
  • Media-plane independence. Media flows through the str0m SFU, not through gRPC. A signaling failure does not imply media failure and vice versa; the calls page must reconcile the two planes (e.g., show “connecting” until both are up).
  • Module decomposition. A page that grows beyond a single responsibility is a spec violation and must be split into layout/page/detail modules plus shared hooks.

Performance & Operational Considerations

  • Single realtime stream per client. All three surfaces share one RealtimeEngine gRPC-Web connection; groups, status, and calls pages subscribe to the same stream rather than opening per-page connections. This keeps connection count flat as users navigate between the three surfaces.
  • Redis-pattern fan-out scales with subscribers. Signaling uses Redis pattern subscription (PSUBSCRIBE call_signals:*) feeding per-user gRPC streams, so group-call signal load is distributed by the backend rather than multiplied across the SFU.
  • Hot-path serialization. Backend JSON uses sonic-rs for hot-path serialization, which bounds the latency of the list/detail REST payloads these pages fetch.
  • Media forwarding, not mesh. Group calls use the embedded str0m SFU for media forwarding, so each participant uploads/downloads one stream regardless of group size — the calls page only ever manages one peer connection toward the SFU.
  • Upload limits. Status media and group attachments are capped at 10 MiB by the backend multipart limit; the UIs should enforce the same limit client-side to avoid failed uploads.

Extension Points

  • Template-porting pipeline. New groups/status/calls variants are added by porting a new mockup to the matching (app) route. The mapping list is the authoritative registry.
  • Design tokens. All three surfaces are styled purely from tokens in globals.css; theming or rebranding is a token change, not a per-page change.
  • Shared RealtimeEngine. @securemesh/realtime-core is the extension seam for realtime behavior; it is compiled into the web client via transpilePackages and consumed by mobile via Metro watchFolders. New live features for groups/status/calls are implemented in the engine, then surfaced by the pages.
  • Call/media integration. On mobile, calling extends through react-native-webrtc and react-native-callkeep (CallKit/ConnectionService); the web calls page mirrors that call-control state machine, so new call states must be added to both clients in lockstep.

Tests

No test files were discovered under streaming-frontend/app/ in this pass — the file listing for the frontend app surfaced only pages, layouts, routes, and globals.css. No web testing harness is specified for these routes. Consequently, test coverage for the groups/status/calls pages is not evidenced in the repository material reviewed; the pages are decomposed into small single-responsibility components that lend themselves to targeted unit tests once a harness is added. Treat test coverage for this capability as an open gap.

  • Chats UI — sibling (app) route group: streaming-frontend/app/(app)/chats/ (layout.tsx, page.tsx, [peerId]/page.tsx).
  • Auth flowsstreaming-frontend/app/(auth)/login/page.tsx and signup/page.tsx; the redirect target when the proxy.ts session expires.
  • Settingsstreaming-frontend/app/(app)/settings/page.tsx, the remaining member of the (app) shell.
  • App shell & stylesstreaming-frontend/app/(app)/layout.tsx, streaming-frontend/app/globals.css, and the auth/token route streaming-frontend/app/api/auth/token/route.ts.
  • Backend architecture — Axum/Tonic/Redis/SFU stack and the database migrations 002_groups.up.sql.
  • Complete system spec — Secure Mesh v19 master spec, including the full system topology.

Was this page helpful?