# beenp/lavoirs architecture

> Architecture diagram of the GitHub repository beenp/lavoirs, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-27). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/beenp/lavoirs
- Repository: https://github.com/beenp/lavoirs

## Overview

Lavoirs is a scaffold for interest-based small-group video meetups, from profile setup and matching through a LiveKit conversation, event suggestions, and consent-based contact exchange. The README describes intended responsibilities and flow, but says implementation files are empty placeholders. Edges therefore reflect documented product/API boundaries, not verified runtime calls; no database or event-provider implementation is evidenced.

Main entry points: [`apps/web/src/lib/api-client.ts`](https://github.com/beenp/lavoirs/blob/main/apps/web/src/lib/api-client.ts) (API client), [`apps/web/src/app/routes.tsx`](https://github.com/beenp/lavoirs/blob/main/apps/web/src/app/routes.tsx) (Page routes), [`apps/web/src/features/matching/MatchingPage.tsx`](https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/matching/MatchingPage.tsx) (Matching), [`apps/web/src/features/room/RoomPage.tsx`](https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/room/RoomPage.tsx) (Video room)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_web["Web experience"]
  node_app["Application shell<br/>[App.tsx]"]
  node_routes["Page routes<br/>[routes.tsx]"]
  node_profile_ui["Profile setup<br/>[ProfilePage.tsx]"]
  node_matching_ui["Matching<br/>[MatchingPage.tsx]"]
  node_match_preview["Group preview<br/>[MatchPreview.tsx]"]
  node_room_ui["Video room<br/>[RoomPage.tsx]"]
  node_room_client["LiveKit UI bridge<br/>[LiveKitRoom.tsx]"]
  node_event_ui["Event suggestions"]
  node_contact_ui["Contact exchange"]
  node_api_client["API client<br/>[api-client.ts]"]
  node_livekit_client["LiveKit client<br/>[livekit-client.ts]"]
end

subgraph group_api["API routes"]
  node_profile_api["Profile endpoints<br/>[index.ts]"]
  node_session_api["Session endpoint<br/>[session.ts]"]
  node_count_api["Nearby count<br/>[nearby-count.ts]"]
  node_queue_api["Match queue<br/>[queue.ts]"]
  node_groups_api["Matched groups<br/>[groups.ts]"]
  node_prompts_api["Prompt votes<br/>[prompts.ts]"]
  node_rooms_api["Room operations<br/>[rooms.ts]"]
  node_token_api["Room token<br/>[token.ts]"]
  node_participants_api["Participants<br/>[participants.ts]"]
  node_webhook_api["LiveKit events<br/>[webhook.ts]"]
  node_transcription_api["Transcription boundary<br/>[transcription.ts]"]
  node_recommend_api["Event recommendations<br/>[recommendations.ts]"]
  node_exchange_api["Consent exchange"]
end

subgraph group_controls["Request controls"]
  node_auth_middleware["Authentication<br/>[auth.ts]"]
  node_privacy_middleware["Location privacy"]
  node_validation_middleware["Request validation"]
  node_rate_middleware["Rate limiting<br/>[rate-limit.ts]"]
end

subgraph group_services["Service boundaries"]
  node_match_function["Matchmaking function<br/>[index.ts]"]
  node_event_function["Recommendation function<br/>[index.ts]"]
end

node_member(("Member"))
node_livekit{{"LiveKit Cloud"}}

node_member -->|"uses"| node_app
node_app -->|"routes to"| node_routes
node_routes -->|"opens"| node_profile_ui
node_routes -->|"opens"| node_matching_ui
node_routes -->|"opens"| node_room_ui
node_routes -->|"opens"| node_event_ui
node_profile_ui -->|"requests profile"| node_api_client
node_api_client -->|"calls"| node_profile_api
node_matching_ui -->|"requests matching"| node_api_client
node_api_client -->|"calls"| node_count_api
node_api_client -->|"calls"| node_queue_api
node_api_client -->|"calls"| node_groups_api
node_api_client -->|"calls"| node_prompts_api
node_matching_ui -->|"shows group"| node_match_preview
node_room_ui -->|"hosts"| node_room_client
node_room_client -->|"connects through"| node_livekit_client
node_livekit_client -->|"connects"| node_livekit
node_api_client -->|"calls"| node_rooms_api
node_api_client -->|"calls"| node_token_api
node_api_client -->|"calls"| node_participants_api
node_api_client -->|"calls"| node_transcription_api
node_livekit -->|"sends events"| node_webhook_api
node_room_ui -->|"continues to"| node_event_ui
node_event_ui -->|"requests events"| node_api_client
node_api_client -->|"calls"| node_recommend_api
node_contact_ui -->|"requests exchange"| node_api_client
node_api_client -->|"calls"| node_exchange_api
node_profile_api -->|"protected by"| node_auth_middleware
node_count_api -->|"uses boundary"| node_privacy_middleware
node_api_client -->|"checks session"| node_session_api

click node_app "https://github.com/beenp/lavoirs/blob/main/apps/web/src/app/App.tsx"
click node_routes "https://github.com/beenp/lavoirs/blob/main/apps/web/src/app/routes.tsx"
click node_profile_ui "https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/profile/ProfilePage.tsx"
click node_matching_ui "https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/matching/MatchingPage.tsx"
click node_match_preview "https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/matching/MatchPreview.tsx"
click node_room_ui "https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/room/RoomPage.tsx"
click node_room_client "https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/room/LiveKitRoom.tsx"
click node_event_ui "https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/events/EventRecommendations.tsx"
click node_contact_ui "https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/events/ContactExchange.tsx"
click node_api_client "https://github.com/beenp/lavoirs/blob/main/apps/web/src/lib/api-client.ts"
click node_livekit_client "https://github.com/beenp/lavoirs/blob/main/apps/web/src/lib/livekit-client.ts"
click node_profile_api "https://github.com/beenp/lavoirs/blob/main/api/profiles/index.ts"
click node_session_api "https://github.com/beenp/lavoirs/blob/main/api/auth/session.ts"
click node_count_api "https://github.com/beenp/lavoirs/blob/main/api/matching/nearby-count.ts"
click node_queue_api "https://github.com/beenp/lavoirs/blob/main/api/matching/queue.ts"
click node_groups_api "https://github.com/beenp/lavoirs/blob/main/api/matching/groups.ts"
click node_prompts_api "https://github.com/beenp/lavoirs/blob/main/api/matching/prompts.ts"
click node_rooms_api "https://github.com/beenp/lavoirs/blob/main/api/livekit/rooms.ts"
click node_token_api "https://github.com/beenp/lavoirs/blob/main/api/livekit/token.ts"
click node_participants_api "https://github.com/beenp/lavoirs/blob/main/api/livekit/participants.ts"
click node_webhook_api "https://github.com/beenp/lavoirs/blob/main/api/livekit/webhook.ts"
click node_transcription_api "https://github.com/beenp/lavoirs/blob/main/api/livekit/transcription.ts"
click node_recommend_api "https://github.com/beenp/lavoirs/blob/main/api/events/recommendations.ts"
click node_exchange_api "https://github.com/beenp/lavoirs/blob/main/api/connections/contact-exchange.ts"
click node_auth_middleware "https://github.com/beenp/lavoirs/blob/main/middleware/auth.ts"
click node_privacy_middleware "https://github.com/beenp/lavoirs/blob/main/middleware/location-privacy.ts"
click node_validation_middleware "https://github.com/beenp/lavoirs/blob/main/middleware/request-validation.ts"
click node_rate_middleware "https://github.com/beenp/lavoirs/blob/main/middleware/rate-limit.ts"
click node_match_function "https://github.com/beenp/lavoirs/blob/main/supabase/functions/matchmaking/index.ts"
click node_event_function "https://github.com/beenp/lavoirs/blob/main/supabase/functions/event-recommendations/index.ts"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_app,node_routes,node_profile_ui,node_matching_ui,node_match_preview,node_room_ui,node_room_client,node_event_ui,node_contact_ui,node_api_client,node_livekit_client toneBlue
class node_profile_api,node_session_api,node_count_api,node_queue_api,node_groups_api,node_prompts_api,node_rooms_api,node_token_api,node_participants_api,node_webhook_api,node_transcription_api,node_recommend_api,node_exchange_api toneAmber
class node_auth_middleware,node_privacy_middleware,node_validation_middleware,node_rate_middleware toneMint
class node_match_function,node_event_function toneRose
class node_member,node_livekit toneIndigo
```

## Components

### Web experience

- [Application shell](https://github.com/beenp/lavoirs/blob/main/apps/web/src/app/App.tsx): `apps/web/src/app/App.tsx`
- [Page routes](https://github.com/beenp/lavoirs/blob/main/apps/web/src/app/routes.tsx): `apps/web/src/app/routes.tsx`
- [Profile setup](https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/profile/ProfilePage.tsx): `apps/web/src/features/profile/ProfilePage.tsx`
- [Matching](https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/matching/MatchingPage.tsx): `apps/web/src/features/matching/MatchingPage.tsx`
- [Group preview](https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/matching/MatchPreview.tsx): `apps/web/src/features/matching/MatchPreview.tsx`
- [Video room](https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/room/RoomPage.tsx): `apps/web/src/features/room/RoomPage.tsx`
- [LiveKit UI bridge](https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/room/LiveKitRoom.tsx): `apps/web/src/features/room/LiveKitRoom.tsx`
- [Event suggestions](https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/events/EventRecommendations.tsx): `apps/web/src/features/events/EventRecommendations.tsx`
- [Contact exchange](https://github.com/beenp/lavoirs/blob/main/apps/web/src/features/events/ContactExchange.tsx): `apps/web/src/features/events/ContactExchange.tsx`
- [API client](https://github.com/beenp/lavoirs/blob/main/apps/web/src/lib/api-client.ts): `apps/web/src/lib/api-client.ts`
- [LiveKit client](https://github.com/beenp/lavoirs/blob/main/apps/web/src/lib/livekit-client.ts): `apps/web/src/lib/livekit-client.ts`

### API routes

- [Profile endpoints](https://github.com/beenp/lavoirs/blob/main/api/profiles/index.ts): `api/profiles/index.ts`
- [Session endpoint](https://github.com/beenp/lavoirs/blob/main/api/auth/session.ts): `api/auth/session.ts`
- [Nearby count](https://github.com/beenp/lavoirs/blob/main/api/matching/nearby-count.ts): `api/matching/nearby-count.ts`
- [Match queue](https://github.com/beenp/lavoirs/blob/main/api/matching/queue.ts): `api/matching/queue.ts`
- [Matched groups](https://github.com/beenp/lavoirs/blob/main/api/matching/groups.ts): `api/matching/groups.ts`
- [Prompt votes](https://github.com/beenp/lavoirs/blob/main/api/matching/prompts.ts): `api/matching/prompts.ts`
- [Room operations](https://github.com/beenp/lavoirs/blob/main/api/livekit/rooms.ts): `api/livekit/rooms.ts`
- [Room token](https://github.com/beenp/lavoirs/blob/main/api/livekit/token.ts): `api/livekit/token.ts`
- [Participants](https://github.com/beenp/lavoirs/blob/main/api/livekit/participants.ts): `api/livekit/participants.ts`
- [LiveKit events](https://github.com/beenp/lavoirs/blob/main/api/livekit/webhook.ts): `api/livekit/webhook.ts`
- [Transcription boundary](https://github.com/beenp/lavoirs/blob/main/api/livekit/transcription.ts): `api/livekit/transcription.ts`
- [Event recommendations](https://github.com/beenp/lavoirs/blob/main/api/events/recommendations.ts): `api/events/recommendations.ts`
- [Consent exchange](https://github.com/beenp/lavoirs/blob/main/api/connections/contact-exchange.ts): `api/connections/contact-exchange.ts`

### Request controls

- [Authentication](https://github.com/beenp/lavoirs/blob/main/middleware/auth.ts): `middleware/auth.ts`
- [Location privacy](https://github.com/beenp/lavoirs/blob/main/middleware/location-privacy.ts): `middleware/location-privacy.ts`
- [Request validation](https://github.com/beenp/lavoirs/blob/main/middleware/request-validation.ts): `middleware/request-validation.ts`
- [Rate limiting](https://github.com/beenp/lavoirs/blob/main/middleware/rate-limit.ts): `middleware/rate-limit.ts`

### Service boundaries

- [Matchmaking function](https://github.com/beenp/lavoirs/blob/main/supabase/functions/matchmaking/index.ts): `supabase/functions/matchmaking/index.ts`
- [Recommendation function](https://github.com/beenp/lavoirs/blob/main/supabase/functions/event-recommendations/index.ts): `supabase/functions/event-recommendations/index.ts`

### Other components

- **Member**
- **LiveKit Cloud**

## Connections

- Member → Application shell: uses
- Application shell → Page routes: routes to
- Page routes → Profile setup: opens
- Page routes → Matching: opens
- Page routes → Video room: opens
- Page routes → Event suggestions: opens
- Profile setup → API client: requests profile
- API client → Profile endpoints: calls
- Matching → API client: requests matching
- API client → Nearby count: calls
- API client → Match queue: calls
- API client → Matched groups: calls
- API client → Prompt votes: calls
- Matching → Group preview: shows group
- Video room → LiveKit UI bridge: hosts
- LiveKit UI bridge → LiveKit client: connects through
- LiveKit client → LiveKit Cloud: connects
- API client → Room operations: calls
- API client → Room token: calls
- API client → Participants: calls
- API client → Transcription boundary: calls
- LiveKit Cloud → LiveKit events: sends events
- Video room → Event suggestions: continues to
- Event suggestions → API client: requests events
- API client → Event recommendations: calls
- Contact exchange → API client: requests exchange
- API client → Consent exchange: calls
- Profile endpoints → Authentication: protected by
- Nearby count → Location privacy: uses boundary
- API client → Session endpoint: checks session
