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, theproxy.tsmiddleware, the@securemesh/realtime-coreRealtimeEngine, 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
str0mSFU).
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-webfor browser compatibility); the pages consume realtime through the shared@securemesh/realtime-coreUnified 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.tsmiddleware 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 thatproxy.tsmaintains.- Route pages —
groups/page.tsx,groups/layout.tsx,groups/[id]/page.tsx,status/page.tsx,status/[id]/page.tsx, andcalls/page.tsxrender list/detail views. They are thin: data comes from the backend viaproxy.ts, live updates via the RealtimeEngine. proxy.tsmiddleware — 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-webserves the single realtime plane (no WebSocket); RedisPSUBSCRIBE call_signals:*fans call signals out to per-user gRPC streams; the embeddedstr0mSFU 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 topage.tsxand detail rendering to the[id]page.groups/page.tsx— renders the group list. Data flows throughproxy.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 transport —
proxy.tsmiddleware 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.cssas 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:
- Route load. The App Router renders the
(app)shell and mounts the matching page (groups,status, orcalls). - 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. - 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).
- 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. - Call media (calls surface only). For an active call, the page additionally drives a WebRTC peer connection toward the embedded
str0mSFU; 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.tsinjects 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-governorrate limiting withSmartIpKeyExtractorbehind 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]andcallspages. - 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
str0mSFU, 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-rsfor 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
str0mSFU 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-coreis the extension seam for realtime behavior; it is compiled into the web client viatranspilePackagesand consumed by mobile via MetrowatchFolders. 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-webrtcandreact-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.
Related Links
- Chats UI — sibling
(app)route group:streaming-frontend/app/(app)/chats/(layout.tsx,page.tsx,[peerId]/page.tsx). - Auth flows —
streaming-frontend/app/(auth)/login/page.tsxandsignup/page.tsx; the redirect target when theproxy.tssession expires. - Settings —
streaming-frontend/app/(app)/settings/page.tsx, the remaining member of the(app)shell. - App shell & styles —
streaming-frontend/app/(app)/layout.tsx,streaming-frontend/app/globals.css, and the auth/token routestreaming-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.