SpecsModelExplore
Overview
IOModel Stack
Reverse ProxyIOModel StudioYjs Realtime ServerKeycloakPostgreSQLRedis
Why it's needed
Routing map
Deployment
SpecsModelExplore
Reverse Proxy
OverviewReverse Proxy GuideLinks and Communications

The Reverse Proxy is the single front door of the stack. Every request from the outside world hits it first — it terminates TLS and forwards traffic to the right internal service over the private network.

Role in the stack

It routes HTTPS to Studio, proxies authentication to Keycloak, and upgrades WebSocket connections for the Yjs Realtime Server. Internal services never need to be exposed directly.

Why it’s needed

  • One TLS boundary — certificates and HTTPS live in one place instead of every service.
  • Routing — path/host-based routing to Studio, the Yjs server, and Keycloak.
  • WebSocket upgrades — realtime collaboration needs Upgrade/Connection headers passed through correctly.
  • Cross-cutting policy — rate limiting, request size limits, and security headers are enforced at the edge.

Routing map

IncomingForwarded toNotes
https://<host>/StudioWeb UI + API
https://<host>/authKeycloakOIDC endpoints
wss://<host>/collabYjs Realtime ServerWebSocket upgrade required

Deployment

On Kubernetes this is the Ingress controller (e.g. NGINX Ingress or Traefik), not a pod you write yourself. You declare an Ingress and the controller programs the edge:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: iomodel
  namespace: iomodel
  annotations:
    nginx.ingress.kubernetes.io/proxy-read-timeout: '3600'
    nginx.ingress.kubernetes.io/proxy-send-timeout: '3600'
spec:
  tls:
    - hosts: ['iomodel.example.com']
      secretName: iomodel-tls
  rules:
    - host: iomodel.example.com
      http:
        paths:
          - path: /collab
            pathType: Prefix
            backend: { service: { name: yjs-server, port: { number: 3400 } } }
          - path: /
            pathType: Prefix
            backend: { service: { name: studio, port: { number: 3200 } } }

The long proxy timeouts matter for the /collab WebSocket route — without them the Ingress will drop idle realtime connections.

Terminate TLS here, not in the app

Studio and the Yjs server expect to run behind this proxy. Keep TLS, HSTS, and certificate renewal at the edge so the internal services stay simple.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: iomodel
  namespace: iomodel
  annotations:
    nginx.ingress.kubernetes.io/proxy-read-timeout: '3600'
    nginx.ingress.kubernetes.io/proxy-send-timeout: '3600'
spec:
  tls:
    - hosts: ['iomodel.example.com']
      secretName: iomodel-tls
  rules:
    - host: iomodel.example.com
      http:
        paths:
          - path: /collab
            pathType: Prefix
            backend: { service: { name: yjs-server, port: { number: 3400 } } }
          - path: /
            pathType: Prefix
            backend: { service: { name: studio, port: { number: 3200 } } }