server.md 3.1 KB

Server

Overview

src/lib/server/server.ts is the single entry point for all server-side initialization. It owns every stateful server component and is responsible for wiring them together in dependency order before any request is served.

The Server class is instantiated once as a module-level singleton. Its init() method is called immediately in hooks.server.ts and the resulting promise is awaited before any request is processed.

Architecture

Server (singleton)
├── WorkspaceManager   — workspace list
├── Auth               — credential validation + JWT
├── Catalog            — per-workspace GitClient + domain catalogs
│   └── Map<workspaceId, { workspace, gitClient, catalog: WorkspaceCatalog }>
│       └── WorkspaceCatalog: { models, tools, kbs, documents, agents, stacks, secrets }
└── Resources          — per-workspace K8sClient + domain resource watchers
    └── Map<workspaceId, { workspace, k8sClient?, resources: WorkspaceResources }>
          └── WorkspaceResources: { models, tools, kbs, documents, agents, stacks, secrets, subscribe }

Catalog and Resources are initialized after WorkspaceManager and receive its workspace list. Each workspace gets its own GitClient (for Catalog) and optionally a K8sClient (for Resources, only when workspace.deployment.type === 'k8s').

Lifecycle

process start
     │
     ▼
hooks.server.ts loads
     │  new Server()        (construction — no I/O)
     │  server.init()       (returns a Promise, stored as `ready`)
     ▼
handle() called per request
     │  await ready         (blocks until init resolves)
     │
     ├─ init still running → request waits
     └─ init complete      → request proceeds

Init order:

  1. WorkspaceManager.init() and Auth.init() — run in parallel
  2. Catalog(workspaces).init() and Resources(workspaces).init() — run in parallel after step 1

If init() rejects, every subsequent await ready re-throws, causing all requests to fail with 500.

Config

All config is read from environment variables. Components receive config from Server.init(); they do not read env vars themselves.

AUTH_JWT_SECRET   # optional — auth disabled when missing or when AUTH_USERS_FILE is missing
AUTH_USERS_FILE   # optional — auth disabled when missing or when AUTH_JWT_SECRET is missing
LOG_LEVEL         # optional — default: info
NODE_ENV          # optional — "development" enables pretty log format; otherwise JSON

Workspace-level configuration (git endpoints, K8s API URL, namespace, credentials) is embedded in the workspace definition managed by WorkspaceManager. See specs/designs/00-foundation/multi-tenancy.md.

Design Rationale

  • Workspace-centric: each workspace owns its storage backend and deployment target; the server just coordinates initialization.
  • Fail fast: init failures prevent request handling rather than silently degrading.
  • No lazy init: all I/O happens in init(); request latency is predictable.
  • SSO migration path: swapping Auth for an OIDC implementation only requires updating Server.init().