Loading Documentation Hub...
Scanning documentation library
Loading Documentation Hub...
Scanning documentation library
Documentation
📚 Documentation
Loading Documentation Hub...
Scanning documentation library
Scanning documentation library
Scanning documentation library
Scanning documentation library
Підготовка контенту платформи Ring
Підготовка контенту платформи Ring
Підготовка контенту платформи Ring
Ring's Multi-Transport Real-Time Communication
Tunnel Protocol is Ring Platform's intelligent real-time communication system - the Ring analog to Firebase Realtime Database. It provides unified real-time messaging across WebSocket, Server-Sent Events (SSE), Supabase Realtime, and Long-Polling transports with automatic fallback and Edge Runtime compatibility.
Firebase Realtime Database provides excellent real-time features but creates vendor lock-in:
✅ Transport Abstraction: Unified API across 5+ transports
✅ Edge Runtime Compatible: SSE support for Vercel Edge
✅ Zero Vendor Lock-in: Use any backend (PostgreSQL, Supabase, Firebase)
✅ Intelligent Fallback: Automatic cascade on transport failures
✅ Cost Optimized: Self-hosted options, pay only for infrastructure
✅ Anonymous Support: Real-time features for unauthenticated users
Ring's Multi-Transport Real-Time Communication
Tunnel Protocol is Ring Platform's intelligent real-time communication system - the Ring analog to Firebase Realtime Database. It provides unified real-time messaging across WebSocket, Server-Sent Events (SSE), Supabase Realtime, and Long-Polling transports with automatic fallback and Edge Runtime compatibility.
Firebase Realtime Database provides excellent real-time features but creates vendor lock-in:
✅ Transport Abstraction: Unified API across 5+ transports
✅ Edge Runtime Compatible: SSE support for Vercel Edge
✅ Zero Vendor Lock-in: Use any backend (PostgreSQL, Supabase, Firebase)
✅ Intelligent Fallback: Automatic cascade on transport failures
✅ Cost Optimized: Self-hosted options, pay only for infrastructure
✅ Anonymous Support: Real-time features for unauthenticated users
Ring's Multi-Transport Real-Time Communication
Tunnel Protocol is Ring Platform's intelligent real-time communication system - the Ring analog to Firebase Realtime Database. It provides unified real-time messaging across WebSocket, Server-Sent Events (SSE), Supabase Realtime, and Long-Polling transports with automatic fallback and Edge Runtime compatibility.
Firebase Realtime Database provides excellent real-time features but creates vendor lock-in:
✅ Transport Abstraction: Unified API across 5+ transports
✅ Edge Runtime Compatible: SSE support for Vercel Edge
✅ Zero Vendor Lock-in: Use any backend (PostgreSQL, Supabase, Firebase)
✅ Intelligent Fallback: Automatic cascade on transport failures
✅ Cost Optimized: Self-hosted options, pay only for infrastructure
✅ Anonymous Support: Real-time features for unauthenticated users
Tunnel Protocol is Ring's equivalent to Firebase Realtime Database, providing the same real-time capabilities with multi-transport flexibility and zero vendor lock-in.
Tunnel Protocol is Ring's equivalent to Firebase Realtime Database, providing the same real-time capabilities with multi-transport flexibility and zero vendor lock-in.
Tunnel Protocol is Ring's equivalent to Firebase Realtime Database, providing the same real-time capabilities with multi-transport flexibility and zero vendor lock-in.
// Single API works across ALL transports
import { publishToTunnel, subscribeToTunnel } from '@/lib/tunnel/publisher'
// Publish message (works on any transport)
await publishToTunnel(channel, eventType, data)
// Subscribe to updates (works on any transport)
const unsubscribe = subscribeToTunnel(channel, (message) => {
console
// Single API works across ALL transports
import { publishToTunnel, subscribeToTunnel } from '@/lib/tunnel/publisher'
// Publish message (works on any transport)
await publishToTunnel(channel, eventType, data)
// Subscribe to updates (works on any transport)
const unsubscribe = subscribeToTunnel(channel, (message) => {
console
// Single API works across ALL transports
import { publishToTunnel, subscribeToTunnel } from '@/lib/tunnel/publisher'
// Publish message (works on any transport)
await publishToTunnel(channel, eventType, data)
// Subscribe to updates (works on any transport)
const unsubscribe = subscribeToTunnel(channel, (message) => {
console
| Transport | Latency | Throughput | Use Case |
|---|---|---|---|
| WebSocket | <50ms | 10K msg/s | Self-hosted, K8s clusters |
| SSE | <100ms | 1K msg/s | Vercel Edge Runtime |
| Supabase Realtime | <50ms | 5K msg/s | Supabase deployments |
| Firebase Realtime | <200ms | 1K msg/s | Firebase-full mode |
| Long-Polling | 500ms-2s | 100 msg/s | Ultimate fallback |
| Transport | Latency | Throughput | Use Case |
|---|---|---|---|
| WebSocket | <50ms | 10K msg/s | Self-hosted, K8s clusters |
| SSE | <100ms | 1K msg/s | Vercel Edge Runtime |
| Supabase Realtime | <50ms | 5K msg/s | Supabase deployments |
| Firebase Realtime | <200ms | 1K msg/s | Firebase-full mode |
| Long-Polling | 500ms-2s | 100 msg/s | Ultimate fallback |
| Transport | Latency | Throughput | Use Case |
|---|---|---|---|
| WebSocket | <50ms | 10K msg/s | Self-hosted, K8s clusters |
| SSE | <100ms | 1K msg/s | Vercel Edge Runtime |
| Supabase Realtime | <50ms | 5K msg/s | Supabase deployments |
| Firebase Realtime | <200ms | 1K msg/s | Firebase-full mode |
| Long-Polling | 500ms-2s | 100 msg/s | Ultimate fallback |
Replace Firebase RTDB calls with Tunnel:
Before (Firebase RTDB):
Replace Firebase RTDB calls with Tunnel:
Before (Firebase RTDB):
Replace Firebase RTDB calls with Tunnel:
Before (Firebase RTDB):
import { getAdminRtdb } from '@/lib/firebase-admin.server'
const rtdb = getAdminRtdb()
await rtdb.ref(`messages/${messageId}`).set(messageData)import { getAdminRtdb } from '@/lib/firebase-admin.server'
const rtdb = getAdminRtdb()
await rtdb.ref(`messages/${messageId}`).set(messageData)import { getAdminRtdb } from '@/lib/firebase-admin.server'
const rtdb = getAdminRtdb()
await rtdb.ref(`messages/${messageId}`).set(messageData)After (Tunnel Protocol):
After (Tunnel Protocol):
After (Tunnel Protocol):
import { publishToTunnel } from '@/lib/tunnel/publisher'
await publishToTunnel(`conversation:${conversationId}`, 'message:new', messageData)import { publishToTunnel } from '@/lib/tunnel/publisher'
await publishToTunnel(`conversation:${conversationId}`, 'message:new', messageData)import { publishToTunnel } from '@/lib/tunnel/publisher'
await publishToTunnel(`conversation:${conversationId}`, 'message:new', messageData)Before (Firebase RTDB):
Before (Firebase RTDB):
Before (Firebase RTDB):
const ref = rtdb.ref(`messages/${messageId}`)
ref.on('value', (snapshot) => {
const data = snapshot.val()
handleUpdate(data)
})const ref = rtdb.ref(`messages/${messageId}`)
ref.on('value', (snapshot) => {
const data = snapshot.val()
handleUpdate(data)
})const ref = rtdb.ref(`messages/${messageId}`)
ref.on('value', (snapshot) => {
const data = snapshot.val()
handleUpdate(data)
})After (Tunnel Protocol):
After (Tunnel Protocol):
After (Tunnel Protocol):
import { subscribeToTunnel } from '@/lib/tunnel/client'
const unsubscribe = subscribeToTunnel(`conversation:${conversationId}`, (message) => {
if (message.type === 'message:new') {
handleUpdate(message.payload)
import { subscribeToTunnel } from '@/lib/tunnel/client'
const unsubscribe = subscribeToTunnel(`conversation:${conversationId}`, (message) => {
if (message.type === 'message:new') {
handleUpdate(message.payload)
import { subscribeToTunnel } from '@/lib/tunnel/client'
const unsubscribe = subscribeToTunnel(`conversation:${conversationId}`, (message) => {
if (message.type === 'message:new') {
handleUpdate(message.payload)
Server-side (publish):
Server-side (publish):
Server-side (publish):
'use server'
import { initializeDatabase, getDatabaseService } from '@/lib/database/DatabaseService'
import { publishToTunnel } from '@/lib/tunnel/publisher'
import { revalidatePath } from 'next/cache'
export async function sendMessage(conversationId: string, content: string
'use server'
import { initializeDatabase, getDatabaseService } from '@/lib/database/DatabaseService'
import { publishToTunnel } from '@/lib/tunnel/publisher'
import { revalidatePath } from 'next/cache'
export async function sendMessage(conversationId: string, content: string
'use server'
import { initializeDatabase, getDatabaseService } from '@/lib/database/DatabaseService'
import { publishToTunnel } from '@/lib/tunnel/publisher'
import { revalidatePath } from 'next/cache'
export async function sendMessage(conversationId: string, content: string
Client-side (subscribe):
Client-side (subscribe):
Client-side (subscribe):
'use client'
import { useEffect, useState } from 'react'
import { subscribeToTunnel } from '@/lib/tunnel/client'
export function ChatMessages({ conversationId }) {
const [messages, setMessages] = useState([])
useEffect
'use client'
import { useEffect, useState } from 'react'
import { subscribeToTunnel } from '@/lib/tunnel/client'
export function ChatMessages({ conversationId }) {
const [messages, setMessages] = useState([])
useEffect
'use client'
import { useEffect, useState } from 'react'
import { subscribeToTunnel } from '@/lib/tunnel/client'
export function ChatMessages({ conversationId }) {
const [messages, setMessages] = useState([])
useEffect
Publish typing status:
Publish typing status:
Publish typing status:
await publishToTunnel(`conversation:${conversationId}`, 'typing:start', {
userId,
userName,
timestamp: Date.now()
})await publishToTunnel(`conversation:${conversationId}`, 'typing:start', {
userId,
userName,
timestamp: Date.now()
})await publishToTunnel(`conversation:${conversationId}`, 'typing:start', {
userId,
userName,
timestamp: Date.now()
})Subscribe to typing:
Subscribe to typing:
Subscribe to typing:
subscribeToTunnel(`conversation:${conversationId}`, (message) => {
if (message.type === 'typing:start') {
showTypingIndicator(message.payload.userName)
}
})subscribeToTunnel(`conversation:${conversationId}`, (message) => {
if (message.type === 'typing:start') {
showTypingIndicator(message.payload.userName)
}
})subscribeToTunnel(`conversation:${conversationId}`, (message) => {
if (message.type === 'typing:start') {
showTypingIndicator(message.payload.userName)
}
})Publish like update:
Publish like update:
Publish like update:
await publishToTunnel(`entity:${entityId}`, 'like:toggled', {
likeCount: newCount,
liked: true,
userId
})await publishToTunnel(`entity:${entityId}`, 'like:toggled', {
likeCount: newCount,
liked: true,
userId
})await publishToTunnel(`entity:${entityId}`, 'like:toggled', {
likeCount: newCount,
liked: true,
userId
})Tunnel automatically selects optimal transport based on deployment:
// Detected: postgres.*.svc.cluster.local
// Selected: WebSocket (wss://)
// Fallback: Long-Polling// Detected: VERCEL=1
// Selected: SSE (Edge compatible)
// Fallback: Long-Polling// Detected: SUPABASE_URL configured
// Selected: Supabase Realtime
// Fallback: SSE → Long-PollingTunnel automatically selects optimal transport based on deployment:
// Detected: postgres.*.svc.cluster.local
// Selected: WebSocket (wss://)
// Fallback: Long-Polling// Detected: VERCEL=1
// Selected: SSE (Edge compatible)
// Fallback: Long-Polling// Detected: SUPABASE_URL configured
// Selected: Supabase Realtime
// Fallback: SSE → Long-PollingTunnel automatically selects optimal transport based on deployment:
// Detected: postgres.*.svc.cluster.local
// Selected: WebSocket (wss://)
// Fallback: Long-Polling// Detected: VERCEL=1
// Selected: SSE (Edge compatible)
// Fallback: Long-Polling// Detected: SUPABASE_URL configured
// Selected: Supabase Realtime
// Fallback: SSE → Long-Polling// Detected: DB_BACKEND_MODE=firebase-full
// Selected: Firebase Realtime
// Fallback: SSE → Long-Polling// Detected: DB_BACKEND_MODE=firebase-full
// Selected: Firebase Realtime
// Fallback: SSE → Long-Polling// Detected: DB_BACKEND_MODE=firebase-full
// Selected: Firebase Realtime
// Fallback: SSE → Long-Polling# Transport selection (optional - auto-detected)
NEXT_PUBLIC_TUNNEL_TRANSPORT=websocket # or sse, supabase, firebase
# WebSocket configuration
TUNNEL_WEBSOCKET_URL=wss://your-domain.com/api/tunnel/ws
# SSE configuration (Edge Runtime)
TUNNEL_SSE_URL=https://your-domain.com/api/tunnel/sse
# Fallback settings
TUNNEL_ENABLE_FALLBACK=true
TUNNEL_FALLBACK_TIMEOUT=5000 # 5 seconds# Transport selection (optional - auto-detected)
NEXT_PUBLIC_TUNNEL_TRANSPORT=websocket # or sse, supabase, firebase
# WebSocket configuration
TUNNEL_WEBSOCKET_URL=wss://your-domain.com/api/tunnel/ws
# SSE configuration (Edge Runtime)
TUNNEL_SSE_URL=https://your-domain.com/api/tunnel/sse
# Fallback settings
TUNNEL_ENABLE_FALLBACK=true
TUNNEL_FALLBACK_TIMEOUT=5000 # 5 seconds# Transport selection (optional - auto-detected)
NEXT_PUBLIC_TUNNEL_TRANSPORT=websocket # or sse, supabase, firebase
# WebSocket configuration
TUNNEL_WEBSOCKET_URL=wss://your-domain.com/api/tunnel/ws
# SSE configuration (Edge Runtime)
TUNNEL_SSE_URL=https://your-domain.com/api/tunnel/sse
# Fallback settings
TUNNEL_ENABLE_FALLBACK=true
TUNNEL_FALLBACK_TIMEOUT=5000 # 5 secondsTunnel respects your database backend mode:
Tunnel respects your database backend mode:
Tunnel respects your database backend mode:
// lib/database/backend-mode-config.ts
export const BACKEND_MODES = {
'k8s-postgres-fcm': {
tunnel: 'websocket' // Primary transport
},
'firebase-full': {
tunnel: 'firebase' // Use Firebase Realtime
},
'supabase-fcm': {
// lib/database/backend-mode-config.ts
export const BACKEND_MODES = {
'k8s-postgres-fcm': {
tunnel: 'websocket' // Primary transport
},
'firebase-full': {
tunnel: 'firebase' // Use Firebase Realtime
},
'supabase-fcm': {
// lib/database/backend-mode-config.ts
export const BACKEND_MODES = {
'k8s-postgres-fcm': {
tunnel: 'websocket' // Primary transport
},
'firebase-full': {
tunnel: 'firebase' // Use Firebase Realtime
},
'supabase-fcm': {
WebSocket: <50ms // Best for self-hosted
SSE: <100ms // Best for Edge Runtime
Supabase Realtime: <50ms // Best for Supabase deployments
Firebase Realtime: <200ms // When using Firebase mode
Long-Polling: 500ms-2s // Universal fallbackWebSocket: <50ms // Best for self-hosted
SSE: <100ms // Best for Edge Runtime
Supabase Realtime: <50ms // Best for Supabase deployments
Firebase Realtime: <200ms // When using Firebase mode
Long-Polling: 500ms-2s // Universal fallbackWebSocket: <50ms // Best for self-hosted
SSE: <100ms // Best for Edge Runtime
Supabase Realtime: <50ms // Best for Supabase deployments
Firebase Realtime: <200ms // When using Firebase mode
Long-Polling: 500ms-2s // Universal fallbackWebSocket: 10,000 messages/second
SSE: 1,000 messages/second
Supabase Realtime: 5,000 messages/second
Firebase Realtime: 1,000 messages/second
Long-Polling: 100 messages/secondWebSocket: 10,000 messages/second
SSE: 1,000 messages/second
Supabase Realtime: 5,000 messages/second
Firebase Realtime: 1,000 messages/second
Long-Polling: 100 messages/secondWebSocket: 10,000 messages/second
SSE: 1,000 messages/second
Supabase Realtime: 5,000 messages/second
Firebase Realtime: 1,000 messages/second
Long-Polling: 100 messages/secondAll transports support encryption:
All transports support encryption:
All transports support encryption:
await publishToTunnel(`secure:${userId}`, 'sensitive:data', {
encrypted: true,
data: encryptedPayload
})await publishToTunnel(`secure:${userId}`, 'sensitive:data', {
encrypted: true,
data: encryptedPayload
})await publishToTunnel(`secure:${userId}`, 'sensitive:data', {
encrypted: true,
data: encryptedPayload
})Built-in protection against abuse:
Built-in protection against abuse:
Built-in protection against abuse:
// Automatic rate limiting per user/IP
Max 100 messages/minute per user
Max 1000 messages/minute per IP// Automatic rate limiting per user/IP
Max 100 messages/minute per user
Max 1000 messages/minute per IP// Automatic rate limiting per user/IP
Max 100 messages/minute per user
Max 1000 messages/minute per IPTransport-level protection:
Transport-level protection:
Transport-level protection:
| Firebase RTDB | Tunnel Protocol |
|---|---|
rtdb.ref(path).set(data) | publishToTunnel(channel, type, data) |
rtdb.ref(path).on('value', cb) | subscribeToTunnel(channel, cb) |
rtdb.ref(path).off() | unsubscribe() |
rtdb.ref(path).onDisconnect() | Handled by transport layer |
| Firebase RTDB | Tunnel Protocol |
|---|---|
rtdb.ref(path).set(data) | publishToTunnel(channel, type, data) |
rtdb.ref(path).on('value', cb) | subscribeToTunnel(channel, cb) |
rtdb.ref(path).off() | unsubscribe() |
rtdb.ref(path).onDisconnect() | Handled by transport layer |
| Firebase RTDB | Tunnel Protocol |
|---|---|
rtdb.ref(path).set(data) | publishToTunnel(channel, type, data) |
rtdb.ref(path).on('value', cb) | subscribeToTunnel(channel, cb) |
rtdb.ref(path).off() | unsubscribe() |
rtdb.ref(path).onDisconnect() | Handled by transport layer |
Before:
Before:
Before:
const rtdb = getAdminRtdb()
await rtdb.ref(`typing/${conversationId}/${userId}`).set(typingData)
await rtdb.ref(`typing/${conversationId}/${userId}`).const rtdb = getAdminRtdb()
await rtdb.ref(`typing/${conversationId}/${userId}`).set(typingData)
await rtdb.ref(`typing/${conversationId}/${userId}`).const rtdb = getAdminRtdb()
await rtdb.ref(`typing/${conversationId}/${userId}`).set(typingData)
await rtdb.ref(`typing/${conversationId}/${userId}`).After:
After:
After:
await publishToTunnel(`conversation:${conversationId}`, 'typing:start', {
userId,
userName,
timestamp: Date.now()
})
// Disconnect handling automatic in Tunnel transport layerawait publishToTunnel(`conversation:${conversationId}`, 'typing:start', {
userId,
userName,
timestamp: Date.now()
})
// Disconnect handling automatic in Tunnel transport layerawait publishToTunnel(`conversation:${conversationId}`, 'typing:start', {
userId,
userName,
timestamp: Date.now()
})
// Disconnect handling automatic in Tunnel transport layerTunnel supports typed messages:
Tunnel supports typed messages:
Tunnel supports typed messages:
type TunnelMessageType =
| 'DATA' // Generic data
| 'NOTIFICATION' // Push notifications
| 'MESSAGE' // Chat messages
| 'PRESENCE' // User presence/typing
| 'HEARTBEAT' // Keep-alive
| 'ACK' // Acknowledgments
| '
type TunnelMessageType =
| 'DATA' // Generic data
| 'NOTIFICATION' // Push notifications
| 'MESSAGE' // Chat messages
| 'PRESENCE' // User presence/typing
| 'HEARTBEAT' // Keep-alive
| 'ACK' // Acknowledgments
| '
type TunnelMessageType =
| 'DATA' // Generic data
| 'NOTIFICATION' // Push notifications
| 'MESSAGE' // Chat messages
| 'PRESENCE' // User presence/typing
| 'HEARTBEAT' // Keep-alive
| 'ACK' // Acknowledgments
| '
Organize channels by feature:
Organize channels by feature:
Organize channels by feature:
`conversation:${id}` // Chat conversations
`entity:${id}` // Entity updates
`opportunity:${id}` // Opportunity updates
`user:${id}` // User-specific channel
`typing:${conversationId}` // Typing indicators
`presence:${
`conversation:${id}` // Chat conversations
`entity:${id}` // Entity updates
`opportunity:${id}` // Opportunity updates
`user:${id}` // User-specific channel
`typing:${conversationId}` // Typing indicators
`presence:${
`conversation:${id}` // Chat conversations
`entity:${id}` // Entity updates
`opportunity:${id}` // Opportunity updates
`user:${id}` // User-specific channel
`typing:${conversationId}` // Typing indicators
`presence:${
feature:id patternfeature:id patternfeature:id patternBuilt by Legion Commander for Emperor Ray
Ring analog to Firebase RTDB - Zero vendor lock-in - Maximum flexibility
🔥⚔️👑
Built by Legion Commander for Emperor Ray
Ring analog to Firebase RTDB - Zero vendor lock-in - Maximum flexibility
🔥⚔️👑
Built by Legion Commander for Emperor Ray
Ring analog to Firebase RTDB - Zero vendor lock-in - Maximum flexibility
🔥⚔️👑