Zum Hauptinhalt springen
Intelligence View
⚡ tsecurity.de Intelligence

Title For Cross Platform posting

Architecture Document

White-Label Multi-Tenant

Chat Platform

5 Implementation Phases

2 Database Layers

13 Mongoose Schemas

CASL Permissions

Redis + Socket.IO

01 —

Database Architecture

🏛 PLATFORM_DB  ·  Shared

Organization auth · branding · roles · plan

SuperAdmin email · role · mfa

DomainMapping domain · orgSlug · tls

AuditLog action · actor · TTL 1yr

slug

🗃 org_{slug}_db  ·  Per Tenant

User role · externalId · CASL

Channel type · visibility · dm_key · CASL

ChannelMembership channelId · userId · unread

Message content · reactions · CASL

Notification type · userId · TTL 30d

Pin channelId · messageId

File s3Path · fileType · size

Job purge · export · status

Setting _id key · Mixed value

Isolation Strategy: database-per-tenant — no tenantId fields needed. Each org gets org_{slug}_db. Resolved via req.orgDb (Connection) injected by orgResolver middleware. Models registered via registerOrgModels(conn) / getModels(conn) factory pattern.

02 —

Implementation Phases

PHASE 01

Remove Legacy Code

  • Delete Okta / reports APIs
  • Remove 5 stale models
  • Clean User + Channel schemas
  • Remove oktaJobs, scripts
  • Strip domain-specific logic

PHASE 02

Multi-Org Backend

  • connectionManager (LRU)
  • orgResolver middleware
  • Platform DB models
  • Refactor all controllers
  • Per-org JWT + S3 + SAML

PHASE 03

Frontend White-Label

  • OrgProvider context
  • Fetch /api/v1/org/config
  • CSS variable injection
  • Dynamic logo/title/favicon
  • Auth flow per provider

PHASE 04

Super-Admin Panel

  • Separate React app
  • admin.chatapp.com
  • Org provisioning wizard
  • Branding + feature editor
  • Role management UI

PHASE 05

Infrastructure

  • Nginx wildcard TLS
  • PM2 cluster mode
  • Redis Socket.IO adapter
  • Custom domain TLS (LE)
  • ip_hash sticky sessions

03 —

Platform DB — Schema Details

Organization

no-CASL platform_db

slugString unique

nameString

statusactive | suspended | provisioning

dbNameString unique

domains[]String[]

authokta | auth0 | saml subdoc

twilioenabled · flex · sid · token

brandingappName · logo · 12 colors

storageS3 / DO Spaces per-org

emailSendGrid per-org

roles[]name · level · permissions[]

channelTypes[]key · label · icon · autoCreate

features11 boolean flags

plantier · maxUsers · maxStorageMB

SuperAdmin platform_db

emailString unique

passwordHashString

displayNameString

rolesuper_admin | platform_admin

lastLoginAtDate

mfaEnabledBoolean

mfaSecretString

DomainMapping platform_db

domainString unique

orgSlugString

verifiedBoolean

verifiedAtDate

tlsCertPathString

dnsVerificationTokenString

AuditLog

TTL 1yr platform_db

orgSlugString

actionString

actorEmailString

actorTypeuser | superadmin | system

detailsMixed

ipAddressString

timestampDate 31,536,000s TTL

04 —

Org DB — Schema Details

User

CASL org_db

displayNameString

emailString unique

roleString (validated vs org.roles)

avatarUrlString

isProtectedBoolean

statusactive | inactive | suspended

lastLoginAtDate

lastActiveAtDate

externalIdString (generic SSO)

metadataMixed (timezone, locale)

Channel

CASL org_db

nameString

typeString (from org.channelTypes)

visibilitypublic | private | dm

protectedBoolean

createdBy→ User

members[]→ User[]

dm_keyString unique-partial

publicIdString

settingsthreading · slowMode · viewOnly

origin_typesystem | tag | invite | api

ChannelMembership org_db

channelId→ Channel compound-unique

userId→ User

joinedAtDate

origin_typesystem | tag | invite | api

invited_by→ User

lastReadAtDate

unreadCountNumber

mentionCountNumber

mutedBoolean

roleString (channel-level override)

Message

CASL org_db

channelId→ Channel

senderId→ User

contentString

attachments[]Mixed[]

parentMessageId→ Message (thread)

replyCountNumber (denormalized)

reactions[]emoji · users[]

editedAtDate

isDeletedBoolean

taggedUsers[]→ User[]

richContentMixed

Notification

TTL 30d org_db

userId→ User

typemention | message | invite | reply | reaction

triggeredBy→ User

channelId→ Channel

messageId→ Message

titleString

readBoolean

expiresAtDate (pre-save hook)

Pin / File / Job / Setting org_db

Pin.channelId + messageIdunique

Pin.isGlobalPinBoolean

File.s3PathString

File.fileType / fileSizeString / Number

File.thumbnailPathString

Job.jobTypepurge | export

Job.statuspending | running | completed | failed

Job.jobDateDate partial-unique

Setting._idString (key)

Setting.valueMixed

05 —

CASL Permission System

100

Admin

  • manage all (wildcard)
  • Full platform access
  • Role assignment
  • Channel management

50

Moderator

  • Channel CRUD
  • Delete any message
  • Pin any message
  • Upload files

10

Member

★ DEFAULT ROLE

  • Read public channels
  • Join public channels
  • Own message CRUD
  • React, upload, delete own files

Condition interpolation: Permissions use {"senderId": "${user.id}"} templates — resolved at ability-build time via buildAbilityForUser(org, user). Route guard: authorize('delete', 'Channel') middleware. Mongoose: .accessibleBy(req.ability, 'read'). Frontend: via AbilityContext.

Note: Use CASL's native subject('Message', doc) pattern for own-resource checks — do not rely on string interpolation alone. Guard all .accessibleBy() calls with ability.can('read', 'Message') to prevent ForbiddenError throws.

06 —

Redis & Infrastructure

Socket.IO Redis Adapter

  • @socket.io/redis-adapter with ioredis
  • Pub/sub client pair in server.ts
  • All io.to().emit() calls unchanged
  • Rooms: ${slug}:channel:${id}
  • Nginx ip_hash for sticky sessions

Redis Keys & Cache

  • Membership: membership:{channelId} TTL 5m
  • Presence: presence:{userId} HASH + online:{slug} SET
  • Heartbeat every 30s, TTL 35s
  • Org config pub/sub: org:config:updated
  • Rate limit key: ${slug}:${ip}

PM2 + Nginx

  • PM2 cluster, instances: 2
  • increment_var: PORT
  • Nginx upstream with ip_hash
  • Wildcard TLS + custom domain LE
  • Graceful Redis degradation → DB fallback

07 —

Review Findings & Gaps

🐛 Bug

Setting schema _id conflict

_id: {type: String} + {_id: false} is version-dependent in Mongoose 7+. Must be tested explicitly.

🐛 Bug

CASL condition interpolation fragile

JSON.stringify().replace() breaks for any variable other than ${user.id}. Switch to native subject() pattern for own-resource checks.

🐛 Bug

accessibleBy() throws on no permissions

Message.find().accessibleBy(ability) throws ForbiddenError (not empty). Guard with ability.can('read','Message') before the query.

⚠ Security

Sensitive fields stored plaintext

jwtSecret, twilio.authToken, secretAccessKey, email.apiKey need encryption. Decide: Atlas FLE or app-layer encrypt-on-write before implementation — retrofitting is painful.

⚠ Security

Org config cache invalidation missing

LRU cache (5-min TTL) means org suspension or jwtSecret rotation won't propagate for up to 5 min. Add Redis pub/sub bust on org:config:updated for security-sensitive changes.

⚠ Security

SAML RelayState not implemented

Shared /api/v1/auth/saml/callback needs org slug encoded in RelayState. Some IdPs strip it — need short-lived fallback session keyed by SAML request ID.

◎ Gap

Role validation is application-only

User.role is a plain string with no DB constraint. Add Mongoose pre-save hook validating against org.roles[].name — typos silently return empty CASL ability.

◎ Gap

ChannelMembership.role has no CASL integration

Channel-level role override field exists in schema but buildAbilityForUser only reads user.role. Implement or remove the field to avoid confusion.

◎ Gap

File storage quota not enforced

plan.maxStorageMB exists but no enforcement mechanism. Choose: aggregate-on-upload (expensive) or running counter in Setting (fast). Decide before upload service.

◎ Gap

autoCreate on ChannelType is ambiguous

"Auto-create for new users" — create channel once at org provisioning, or add each new user to it? These are very different behaviors. Must be defined.

08 —

Environment Variables

BACKEND .env

MONGO_URIShared cluster URI

PLATFORM_DB_NAMEplatform_db

PORTServer port

BASE_URL / FRONTEND_URLPublic URLs

CORS_ORIGINSAllowed origins

SUPER_ADMIN_JWT_SECRETPlatform-level JWT

REDIS_HOST / PORT / PASSWORDRedis connection

REDIS_TLSTLS flag

DO_SPACE_KEY / SECDefault S3 fallback

DO_SPACE_REGION / ENDPOINTDO Spaces region

FRONTEND .env

VITE_API_BASE_URLBackend API root

VITE_SOCKET_URLSocket.IO endpoint

All tenant-specific config (branding, auth provider, features) is fetched at runtime from /api/v1/org/config — not baked into env vars. OrgProvider injects CSS variables and adapts auth flow per org.

09 —

Technology Stack

🍃MongoDB / Mongoose 7+

🔌Socket.IO v4.6+

⚡Redis (ioredis)

🔐CASL v6

🔑Auth0 / Okta / SAML

📦PM2 Cluster

🌐Nginx + Let's Encrypt

☁DigitalOcean Spaces / S3

📧SendGrid (per-org)

📞Twilio Flex (per-org)

⚛React + Vite frontend

🟦TypeScript end-to-end

WHITE-LABEL SAAS CHAT PLATFORM · ARCHITECTURE v1.0 platform_db + org_{slug}_db · 13 schemas · 5 phases

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Title For Cross Platform posting

Thematisch verwandte Begriffe: Title, Cross, Platform, posting · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-61591 | djust provides Phoenix LiveView-style reactive server-side rendering for…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
News ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

↗ Original-Quelle