Skip to main content

Integration Matrix: family-hub + devarno.cloud Services

Generated: 2026-02-19
Status: TASKSET 2 Deliverable

Overview

This document is the source of truth for all external service integrations from family-hub (casa.devarno.cloud) to surrounding devarno.cloud services. Every external call path maps to a documented contract.

Service Integration Matrix

ServiceStatusProtocolAuth ModelBase URL Env VarContract Source
chat-serviceImplementedHTTP REST + WebSocketCookie forwarding / Query tokenCHAT_SERVICE_URL, NEXT_PUBLIC_CHAT_WS_URLproto-contracts/proto/devarno/events/v1/chat.proto
media-serviceImplementedHTTP REST (multipart)Cookie forwardingMEDIA_SERVICE_URLproto-contracts/proto/devarno/content/v1/media.proto
auth-serviceScaffoldingHTTP RESTN/A (family-hub uses better-auth directly)N/Aproto-contracts/proto/devarno/shared/v1/user.proto
api-gatewayScaffoldingHTTPJWT (planned)N/AN/A
audit-serviceScaffoldingRedis Streams (planned)InternalN/Aproto-contracts/proto/devarno/events/v1/audit.proto
config-serviceScaffoldingHTTP + Redis pub/sub (planned)CookieN/AN/A

Detailed Endpoint Contracts

1. chat-service (Rust/Axum)

Base URLs:
  • REST: ${CHAT_SERVICE_URL} (default: http://localhost:8082)
  • WebSocket: ${NEXT_PUBLIC_CHAT_WS_URL} (default: ws://localhost:8082)
Authentication:
  • HTTP endpoints: Cookie header forwarded verbatim
  • WebSocket: Query parameter ?token=<better-auth.session_token>
  • Validation: GET ${AUTH_SERVICE_URL}/api/auth/get-session with Cookie header

Endpoints

MethodPathfamily-hub CallerRequestResponseErrors
GET/ws?token=<token>use-chat-socket.tsWebSocket upgradeWS connection401 invalid token
GET/api/v1/messages/api/chat-service/messages/route.ts?channel=<id>&before=<ts>&limit=<n>ChatMessage[]401, 400
GET/api/v1/presenceNot currently used?channel=<id>PresenceUpdate[]401

WebSocket Message Contract (JSON)

Client → Server:
// Send message
{ type: "message", content: string, channel: string }

// Typing indicator
{ type: "typing", channel: string, typing: boolean }

// Join/leave channel
{ type: "join", channel: string }
{ type: "leave", channel: string }

// Keepalive
{ type: "ping" }
Server → Client:
// New message
{ 
  type: "message", 
  id: string, 
  sender: string,        // user_id
  content: string, 
  channel: string, 
  timestamp: string      // ISO8601
}

// Presence update
{ 
  type: "presence", 
  user_id: string, 
  online: boolean, 
  channel: string 
}

// Typing indicator
{ 
  type: "typing", 
  user_id: string, 
  channel: string, 
  typing: boolean 
}

// Keepalive response
{ type: "pong" }

// Error
{ type: "error", code: string, message: string }

Proto Contract Mapping

Proto Messagechat-service JSON Fieldfamily-hub Type
ChatMessage.ididstring
ChatMessage.contentcontentstring
ChatMessage.sender_idsender (WS) / sender_id (REST)string
ChatMessage.channel_idchannel (WS) / channel_id (REST)string
ChatMessage.created_attimestamp (WS) / created_at (REST)string (ISO8601)
Gap: family-hub defines its own ChatMessage interface in modules/chat/service.ts:13-21 and hooks/use-chat-socket.ts:11-19 instead of importing from proto-contracts/gen/ts/.

2. media-service (Go/Gin)

Base URL: ${MEDIA_SERVICE_URL} (default: http://localhost:8081) Authentication: Cookie header forwarded verbatim

Endpoints

MethodPathfamily-hub CallerRequestResponseErrors
POST/api/v1/upload/api/media/upload/route.tsmultipart/form-data with file, optional typeUploadResponse401, 400, 413, 415
GET/api/v1/files/:idNot proxied-MediaMetadata401, 404
GET/api/v1/files/:id/downloadDirect URL in content-Binary file401, 404
GET/api/v1/files/:id/download?thumb=1Direct URL in content-Binary thumbnail401, 404
DELETE/api/v1/files/:idNot proxied-204401, 403, 404

Upload Response Contract

// POST /api/v1/upload response
interface UploadResponse {
  id: string;
  url: string;           // Download URL
  thumbnail_url: string; // Thumbnail URL (if applicable)
  sha256: string;        // Content hash
  size_bytes: number;
}

Proto Contract Mapping

Proto Messagemedia-service JSON FieldNotes
UploadResponse.ididMatch
UploadResponse.urlurlMatch
UploadResponse.thumbnail_urlthumbnail_urlMatch
UploadResponse.sha256sha256Match
UploadResponse.size_bytessize_bytesProto is int64, JSON is number

Restrictions

  • Allowed content types: image/jpeg, image/png, image/gif, image/webp, application/pdf, video/mp4, video/webm
  • Max file size: 10MB (configurable via MAX_FILE_SIZE)
  • Max avatar size: 2MB (configurable via MAX_AVATAR_SIZE)
  • Role child cannot upload files

3. better-auth (Internal to family-hub)

family-hub does not call an external auth-service. Authentication is handled internally via better-auth library with the following configuration: Session Validation Endpoint (exposed by better-auth):
GET /api/auth/get-session
Headers: Cookie: better-auth.session_token=<token>

Response (200):
{
  "user": {
    "id": string,
    "email": string,
    "name": string,
    "image": string | null,
    "role": "admin" | "member" | "child",
    "emailVerified": boolean,
    "banned": boolean,
    "createdAt": string,
    "updatedAt": string
  },
  "session": {
    "id": string,
    "userId": string,
    "expiresAt": string,
    "ipAddress": string,
    "userAgent": string
  }
}

Response (401):
{ "user": null, "session": null }
This endpoint is called by:
  • chat-servicesrc/auth/client.rs:24-46
  • media-serviceinternal/services/auth_client.go:27-55
Cross-subdomain cookies enabled:
  • Domain: .devarno.cloud
  • SameSite: lax
  • Secure: true (production)

Call Path Inventory

family-hub → chat-service

family-hub LocationTargetAuthPurpose
src/hooks/use-chat-socket.ts:236-238${CHAT_WS_URL}/ws?token=<token>Query param (session token)WebSocket realtime messages
src/hooks/use-chat-socket.ts:139-141/api/chat-service/messages (internal)CredentialsLoad message history
src/app/api/chat-service/messages/route.ts:37-44${CHAT_SERVICE_URL}/api/v1/messagesCookie forwardingProxy to chat-service

family-hub → media-service

family-hub LocationTargetAuthPurpose
src/app/api/media/upload/route.ts:24-30${MEDIA_SERVICE_URL}/api/v1/uploadCookie forwardingUpload files

family-hub Internal (Direct DB)

family-hub LocationData SourcePurpose
src/modules/chat/service.tsDrizzle → messages, users tablesPolling fallback message storage
src/modules/chat/presence.tsIn-memory Map (BROKEN on Vercel)Presence tracking
src/app/api/chat/messages/route.tsmodules/chat/service.tsPolling endpoint
src/app/api/chat/send/route.tsmodules/chat/service.tsSend message (polling fallback)
src/app/api/chat/presence/route.tsmodules/chat/presence.tsPresence heartbeat

Gap Analysis

Critical Gaps

GapImpactResolution
In-memory presencePresence data not shared across Vercel instancesTASKSET 4: Move to Upstash Redis
Duplicate message storageMessages stored in both family-hub DB and chat-service DBDecide single source of truth
No proto-contracts usagefamily-hub defines own types; drift riskImport from proto-contracts/gen/ts/
No request IDsCannot trace requests across servicesAdd X-Request-ID / traceparent header
No timeoutsProxy routes have no explicit fetch timeoutAdd 10s timeout to all outbound fetches
No retriesSingle failure = user errorAdd retry with exponential backoff for idempotent calls

Architecture Gaps

GapDescriptionResolution
WS auth via raw session tokenSession token passed in query param to chat-serviceTASKSET 1 decision: Implement ephemeral tokens
No unified error envelopeEach route returns different error shapesStandardize: { error: string, code?: string }
Polling without backoffFixed 3s interval regardless of errorsAdd exponential backoff + jitter
Direct cookie parsing in JSuse-chat-socket.ts:79-83 parses document.cookieFetch token from server endpoint

Environment Variables (Complete)

Required

VariableServiceDescription
DATABASE_URLfamily-hubNeon PostgreSQL connection string
BETTER_AUTH_SECRETfamily-hubAuth encryption key (min 32 chars)
BETTER_AUTH_URLfamily-hubAuth base URL (e.g., https://casa.devarno.cloud)
RESEND_API_KEYfamily-hubResend email service API key
NEXT_PUBLIC_APP_URLfamily-hubClient-side app URL
MEDIA_SERVICE_URLfamily-hubMedia service backend URL
CHAT_SERVICE_URLfamily-hubChat service REST URL
NEXT_PUBLIC_CHAT_WS_URLfamily-hubChat service WebSocket URL

New (Required for Production)

VariableServiceDescription
UPSTASH_REDIS_REST_URLfamily-hubUpstash Redis endpoint
UPSTASH_REDIS_REST_TOKENfamily-hubUpstash Redis auth token
WS_TOKEN_SECRETfamily-hubSecret for signing ephemeral WS tokens

External Service Dependencies

ServiceRequired Env Vars
chat-serviceDATABASE_URL, REDIS_URL, AUTH_SERVICE_URL, PORT
media-serviceDATABASE_URL, AUTH_SERVICE_URL, PORT, STORAGE_PATH, BASE_URL
Note: AUTH_SERVICE_URL for chat-service and media-service should point to family-hub’s /api/auth/get-session endpoint (e.g., https://casa.devarno.cloud).

CORS / Origins Configuration

family-hub Trusted Origins

From src/lib/auth.ts:77-81:
trustedOrigins: [
  'https://casa.devarno.cloud',
  'https://*.devarno.cloud',
  ...(process.env.NODE_ENV === 'development' ? ['http://localhost:3000'] : []),
]

Security Headers

From next.config.ts:
  • X-Frame-Options: DENY
  • Content-Security-Policy: connect-src 'self' wss: ws: (allows WebSocket)
  • Strict-Transport-Security: max-age=31536000; includeSubDomains

WebSocket CORS

chat-service must allow WebSocket upgrades from:
  • https://casa.devarno.cloud
  • http://localhost:3000 (development)

Proto Contract File Locations

DomainProto FileGenerated TypeScript
Chatproto-contracts/proto/devarno/events/v1/chat.protoproto-contracts/gen/ts/devarno/events/v1/chat_pb.ts
Mediaproto-contracts/proto/devarno/content/v1/media.protoproto-contracts/gen/ts/devarno/content/v1/media_pb.ts
Userproto-contracts/proto/devarno/shared/v1/user.protoproto-contracts/gen/ts/devarno/shared/v1/user_pb.ts
Auditproto-contracts/proto/devarno/events/v1/audit.protoproto-contracts/gen/ts/devarno/events/v1/audit_pb.ts

How to Regenerate

cd proto-contracts
buf generate

Failure Modes and Error Handling

chat-service Unavailable

ScenarioCurrent BehaviorCorrect Behavior
WS connection failsExponential backoff (1s, 2s, 4s), then pollingAcceptable
Polling 500 errorSilent failure, retry in 3sLog, exponential backoff
Auth validation failsWS upgrade rejectedRedirect to sign-in

media-service Unavailable

ScenarioCurrent BehaviorCorrect Behavior
Upload failsGeneric “Failed to upload file”Show service-specific error, suggest retry
TimeoutNo timeout configured30s timeout for uploads, user feedback

Database Unavailable

ScenarioCurrent BehaviorCorrect Behavior
Neon connection fails500 “Internal server error”Graceful degradation message
Health check failsReturns unhealthy statusAcceptable

Verification Checklist

  • Every external fetch() in family-hub documented above
  • Every external fetch() has explicit timeout
  • All required env vars listed
  • All proto-contracts mapped to actual payloads
  • Error envelopes consistent across routes
  • Request ID propagation implemented
  • Gaps identified have resolution tasks

Next Steps (TASKSET 3)

  1. Create unified service client layer (src/lib/services/)
  2. Import types from proto-contracts/gen/ts/
  3. Add request ID generation and propagation
  4. Implement timeouts and retry logic
  5. Create ephemeral WS token endpoint
  6. Replace in-memory presence with Upstash Redis