Skip to main content

System Architecture

NeoSC implements a Zero Trust architecture using industry-standard open source components. This guide explains how the system works and how components interact.

Architecture Overview

Network Architecture

NeoSC uses a dual-network design following Zero Trust principles:

Public Network (proxy)

  • Only Pomerium is exposed to the internet
  • Handles SSL/TLS termination
  • Routes on ports 80 (redirect) and 443 (HTTPS)

Internal Network (internal)

  • Isolated from the internet
  • All application services (frontend, backend, MongoDB)
  • No direct external access
docker-compose.yml
networks:
  proxy:
    # Public network: Pomerium ↔ external world
    driver: bridge
  internal:
    # Internal network: services not exposed externally
    driver: bridge
    internal: true
This design ensures that even if an application service is compromised, it cannot be accessed directly from the internet.

Domain Structure

DomainServicePurpose
gate.kappa4.comPomerium AuthenticateOAuth/OIDC flow, session management
portal.kappa4.comReact FrontendUser interface (SPA)
api.portal.kappa4.comFastAPI BackendREST API, business logic
manager.kappa4.comZitadelIdentity provider, authentication
workspace.portal.kappa4.comFuture: VDI StreamingWorkspace viewer with WebSocket
admin.portal.kappa4.comFuture: Admin PanelAdmin-only interfaces

Component Deep Dive

1. Pomerium - Zero Trust Proxy

Role: Single point of entry, enforces authentication and authorization Container: pomerium/pomerium:latest Configuration:
image: pomerium/pomerium:latest
ports:
  - "443:443"
  - "80:80"
networks:
  - proxy    # External access
  - internal # Internal service communication
environment:
  ZITADEL_CLIENT_ID: ${ZITADEL_CLIENT_ID}
  ZITADEL_CLIENT_SECRET: ${ZITADEL_CLIENT_SECRET}
  POMERIUM_SHARED_SECRET: ${POMERIUM_SHARED_SECRET}
  POMERIUM_COOKIE_SECRET: ${POMERIUM_COOKIE_SECRET}
Key Features:
  • All-in-one mode (authenticate + authorize + proxy)
  • OIDC integration with Zitadel
  • Context-aware routing based on user identity and roles
  • Session cookie (.kappa4.com) for SSO across subdomains
  • Identity header injection
Access Policies:
- from: https://portal.kappa4.com
  to: http://frontend:3000
  allowed_idp_claims:
    urn:zitadel:iam:org:project:roles:
      - admin
      - neosc
      - user
  pass_identity_headers: true
  allow_websockets: true
  preserve_host_header: true

2. Zitadel - Identity Provider

Role: Authentication, user management, role-based access control Instance: External (on-premise or cloud) OIDC Configuration:
Authority: https://manager.kappa4.com
Project ID: 360327617871609860
Client ID: 360979728544301063
Grant Types:
  - authorization_code
  - refresh_token
Authentication Method: BASIC (client_id + client_secret)
Scopes:
  - openid
  - profile
  - email
  - offline_access
  - urn:zitadel:iam:org:projects:roles
JWT Token Claims:
{
  "sub": "360845682363341211",
  "email": "user@example.com",
  "name": "John Doe",
  "urn:zitadel:iam:org:project:360327617871609860:roles": {
    "admin": {},
    "neosc": {}
  },
  "groups": ["engineering", "sap-users"]
}
Zitadel supports multiple authentication methods: password, WebAuthn, TOTP, SMS, and external IdPs (Google, Microsoft, etc.)
Role Definitions:
  • admin - Full administrative access (all endpoints, can create/delete workspaces)
  • neosc - Standard user access (launch workspaces, view sessions)
  • user - Basic user access (limited workspace access)

3. React Frontend

Role: User interface for workspace management Technology Stack:
package.json
{
  "dependencies": {
    "react": "^19.0.0",
    "react-router-dom": "^7.5.1",
    "react-oidc-context": "^3.3.0",
    "oidc-client-ts": "^3.4.1",
    "axios": "^1.8.4",
    "tailwindcss": "^3.4.17",
    "@radix-ui/react-*": "Various UI components",
    "lucide-react": "^0.507.0"
  }
}
Container Configuration:
frontend:
  build:
    context: ../frontend
    dockerfile: Dockerfile
  container_name: neosc-frontend
  networks:
    - internal  # No external access!
  environment:
    REACT_APP_API_URL: https://api.portal.kappa4.com
    REACT_APP_ZITADEL_AUTHORITY: https://manager.kappa4.com
    REACT_APP_ZITADEL_CLIENT_ID: ${ZITADEL_CLIENT_ID}
    REACT_APP_PORTAL_URL: https://portal.kappa4.com
    REACT_APP_GATE_URL: https://gate.kappa4.com
Key Features:
  • Single Page Application (SPA) with React Router
  • OIDC client for SSO (fallback if Pomerium not used)
  • shadcn/ui component library for modern UI
  • Responsive design with Tailwind CSS
  • Real-time session management

4. FastAPI Backend

Role: Business logic, API endpoints, database operations Technology Stack:
backend/server.py
from fastapi import FastAPI, HTTPException, Depends, Header
from motor.motor_asyncio import AsyncIOMotorClient
import httpx  # HTTP client for Zitadel API
from pydantic import BaseModel, Field
Container Configuration:
backend:
  build:
    context: ../backend
    dockerfile: Dockerfile
  container_name: neosc-backend
  networks:
    - internal
  environment:
    MONGO_URL: mongodb://mongo:27017/neosc
    ZITADEL_AUTHORITY: https://manager.kappa4.com
    ZITADEL_CLIENT_ID: ${ZITADEL_CLIENT_ID}
    ZITADEL_CLIENT_SECRET: ${ZITADEL_CLIENT_SECRET}
    TRUST_POMERIUM_HEADERS: "true"
    JWT_SECRET: ${JWT_SECRET}
API Structure:
# Local authentication (JWT)
POST /api/auth/register
POST /api/auth/login
POST /api/auth/logout

# SSO authentication
POST /api/auth/sso           # Handle Zitadel SSO login
POST /api/auth/token-exchange # Backend-side OIDC token exchange
GET  /api/auth/me            # Get current user info
GET    /api/workspaces              # List all workspaces
POST   /api/workspaces              # Create workspace (admin)
PUT    /api/workspaces/{id}         # Update workspace (admin)
DELETE /api/workspaces/{id}         # Delete workspace (admin)
POST   /api/workspaces/{id}/launch  # Launch workspace
POST   /api/workspaces/{id}/stop    # Stop workspace
POST   /api/workspaces/reset        # Reset to defaults (admin)
GET  /api/sessions           # List user sessions
GET  /api/sessions/active    # List active sessions
POST /api/sessions/{id}/disconnect  # Disconnect session
GET   /api/audit-logs         # View audit logs
GET   /api/policies           # List security policies
PATCH /api/policies/{id}      # Toggle policy
GET   /api/organizations      # List organizations
GET   /api/stats              # System statistics
Authentication Middleware:
async def get_current_user(authorization: str = Header(None)):
    """Validate Bearer token from frontend or Pomerium headers"""
    if not authorization or not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Not authenticated")
    
    token = authorization.replace("Bearer ", "")
    if token not in active_tokens:
        raise HTTPException(status_code=401, detail="Invalid token")
    
    return active_tokens[token]
In production with Pomerium, the backend can trust X-Pomerium-Email headers instead of validating JWTs, simplifying authentication logic.

5. MongoDB

Role: Persistent storage for users, workspaces, sessions, audit logs Container Configuration:
mongo:
  image: mongo:7
  container_name: neosc-mongo
  networks:
    - internal  # Not accessible externally
  volumes:
    - mongo_data:/data/db
  environment:
    MONGO_INITDB_DATABASE: neosc
Collections:
{
  "id": "uuid",
  "email": "user@example.com",
  "name": "John Doe",
  "organization": "Acme Corp",
  "role": "admin",
  "mfa_enabled": true,
  "sso_provider": "zitadel",
  "sso_sub": "360845682363341211",
  "created_at": "2026-03-05T10:00:00Z"
}

6. NetBird - Mesh VPN (Future Integration)

Role: Zero Trust network connectivity for workspace access Status: Visual integration in UI, actual connectivity planned for future releases Planned Architecture:
  • WireGuard-based encrypted tunnels
  • Peer-to-peer mesh networking
  • Dynamic access control lists
  • OIDC integration with Zitadel
  • Browser-based connection (future NetBird Web client)
NetBird integration is currently in planning phase. The UI shows connection status, but actual VPN tunneling is not yet implemented.

Authentication Flow

Detailed walkthrough of the Zero Trust authentication process:
1

User Accesses Portal

User navigates to https://portal.kappa4.com
GET https://portal.kappa4.com
2

Pomerium Intercepts

Pomerium checks for valid session cookie (_pomerium)No cookie found → Redirect to authenticate service
302 Found
Location: https://gate.kappa4.com/oauth2/sign_in
3

Redirect to Zitadel

Pomerium redirects to Zitadel OIDC authorize endpoint
302 Found
Location: https://manager.kappa4.com/oauth/v2/authorize?
  client_id=360979728544301063&
  redirect_uri=https://gate.kappa4.com/oauth2/callback&
  response_type=code&
  scope=openid+profile+email&
  state=random-state
4

User Authenticates

User enters credentials in Zitadel (email/password, WebAuthn, TOTP, etc.)Zitadel validates credentials and enforces MFA if configured
5

Authorization Code Returned

Zitadel redirects back to Pomerium with authorization code
302 Found
Location: https://gate.kappa4.com/oauth2/callback?
  code=authorization-code&
  state=random-state
6

Token Exchange

Pomerium exchanges authorization code for tokens
POST https://manager.kappa4.com/oauth/v2/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
client_id=360979728544301063&
client_secret=secret&
code=authorization-code&
redirect_uri=https://gate.kappa4.com/oauth2/callback
Response:
{
  "access_token": "eyJhbGc...",
  "id_token": "eyJhbGc...",
  "refresh_token": "refresh-token",
  "expires_in": 28800
}
7

Session Creation

Pomerium:
  1. Validates ID token signature
  2. Extracts user identity and roles
  3. Checks role against policy (admin, neosc, or user)
  4. Creates session cookie (_pomerium)
  5. Sets cookie domain to .kappa4.com (SSO across subdomains)
8

Redirect to Portal

Pomerium redirects back to original destination
302 Found
Location: https://portal.kappa4.com
Set-Cookie: _pomerium=encrypted-session-data;
            Domain=.kappa4.com;
            Secure;
            HttpOnly;
            SameSite=Lax;
            Max-Age=28800
9

Access Granted

User accesses portal with valid session cookiePomerium proxies request to frontend, injecting identity headers:
GET http://frontend:3000/
X-Pomerium-Email: user@example.com
X-Pomerium-User: 360845682363341211
X-Pomerium-Groups: admin,neosc

API Request Flow

How API requests are authenticated and authorized:

Security Model

NeoSC implements defense-in-depth security:

Layer 1: Network Isolation

  • Only Pomerium exposed to internet
  • Internal services on isolated Docker network
  • No direct access to backend/database

Layer 2: Identity Verification

  • Every request authenticated via Zitadel OIDC
  • MFA enforcement configurable per-user
  • Short-lived access tokens (8 hours)
  • Refresh tokens for seamless re-authentication

Layer 3: Authorization

  • Role-based access control (RBAC)
  • Granular policies per route/subdomain
  • Context-aware decisions (user, role, time, IP)

Layer 4: Encryption

  • TLS 1.3 for all external traffic
  • Encrypted session cookies (HttpOnly, Secure)
  • Future: WireGuard tunnels for workspace access

Layer 5: Audit & Compliance

  • Complete audit log of all actions
  • Immutable logs in MongoDB
  • Audit includes: user, action, resource, timestamp, IP, result
  • Session recording (planned feature)

Data Flow Diagram

Deployment Topologies

All-in-one deployment (development/small production)
[Server]
├── Pomerium (ports 80/443)
├── Frontend (internal)
├── Backend (internal)
└── MongoDB (internal)
Pros: Simple, low cost Cons: Single point of failure, limited scale

Performance Considerations

Latency Targets

  • Pomerium authentication: < 100ms
  • API response time: < 200ms
  • Workspace provisioning: < 25s (future)
  • Database queries: < 50ms

Scaling Limits

  • Single server: ~100 concurrent users
  • Multi-server: 1000+ concurrent users
  • Database: Sharding for >10M documents

Bottlenecks & Mitigations

ComponentBottleneckMitigation
PomeriumToken validationEnable caching, use Redis
BackendDatabase queriesAdd indexes, use connection pooling
MongoDBWrite throughputReplica set, sharding
FrontendBundle sizeCode splitting, lazy loading

Monitoring & Observability

Recommended monitoring stack (future implementation):
  • Metrics: Prometheus + Grafana
  • Logs: ELK Stack or Loki
  • Tracing: Jaeger or Zipkin
  • Alerting: Prometheus Alertmanager + PagerDuty
Key Metrics to Monitor:
# Pomerium
- pomerium_requests_total
- pomerium_request_duration_seconds
- pomerium_session_cache_hits

# Backend
- http_requests_total
- http_request_duration_seconds
- workspace_launches_total
- workspace_launch_duration_seconds

# MongoDB
- mongodb_connections_current
- mongodb_operations_total
- mongodb_query_latency_seconds

Next Steps

User Management

Learn how to manage users, roles, and permissions

Workspace Configuration

Configure and customize workspace environments

Security Policies

Configure MFA, session recording, and access policies

Monitoring Setup

Set up monitoring and alerting for production

Build docs developers (and LLMs) love