# Server Architecture **Verified against:** commit `5630aa1`, 2026-08-04 Single Go binary (`github.com/J3vb/OwnCord/Server`, Go 1.26). Pure-Go SQLite (`modernc.org/sqlite`, no CGO), chi router, `github.com/coder/websocket`, LiveKit for voice, Wazero for plugins (build-tag gated), optional OpenTelemetry (`-tags otel`). Roughly 42k LOC of production code and 71k LOC of tests. ## D2 — Package map ```mermaid flowchart TB subgraph entry ["Process entry"] MAIN["main.go
config, TLS, DB open+migrate,
signal handling, maintenance loop"] end subgraph http ["HTTP layer"] API["api
router, middleware, REST handlers,
WAF, LiveKit proxy, uploads"] ADMIN["admin
embedded SPA + admin REST,
SSE log stream"] end subgraph realtime ["Real-time"] WS["ws
Hub, pub/sub, replay,
typed command dispatch, LiveKit, E2EE relay"] end subgraph domain ["Domain"] SVC["service
Message/Channel/Permission/User/
DM/Invite/Block/Moderation/Voice"] PERM["permissions
bitfield + Checker"] PLUGIN["plugin
wazero runtime, registry,
manifest, host APIs"] end subgraph data ["Data"] DB[("db
query methods (sqlc-backed),
migration runner, models")] DBGEN["db/dbgen
sqlc-generated queries"] MIG["migrations
001–028 embedded SQL"] end subgraph support ["Support"] AUTH["auth
bcrypt, tokens, TOTP,
rate limiting, TLS"] CFG["config
koanf: defaults→YAML→env"] STORAGE["storage
upload files on disk"] TEL["telemetry
OTel (no-op default)"] UPD["updater
minisign-verified self-update"] end MAIN --> CFG MAIN --> API MAIN --> DB API --> ADMIN API --> WS API --> SVC API --> AUTH API --> STORAGE API --> UPD API --> PLUGIN SVC --> DB SVC --> PERM DB --> DBGEN DB --> MIG WS --> SVC WS --> PLUGIN %% Residual layering seam (dashed): raw *db.DB used above the service layer API -.->|"handlers take *db.DB directly"| DB ADMIN -.->|"raw *db.DB"| DB ``` **What this shows.** The layering is `api → service → db` (D3 removed the former `store` seam). The `service` layer depends on a narrow `service.Store` interface that `*db.DB` satisfies; `ws` and `plugin` depend on their own small interfaces (`ws.EventStore`, `plugin.PluginStore`) the same way. The `db` package's query methods delegate to the sqlc-generated `db/dbgen` code (D2), so sqlc is now the type-checked query layer rather than dead generated code. Two dashed edges mark the residual seam: many REST handlers still receive a raw `*db.DB` alongside `svc`, and the `admin` package operates on `*db.DB` almost exclusively — consolidating those behind the service layer is the remaining work (audit A-2026-07-06 — resolved for the store seam itself; the residual consolidation is its backlog item 12). See [data-model.md](data-model.md). `api.NewRouter` (`Server/api/router.go`) is the composition root: it constructs the rate limiter, TOTP key, storage, `service.New`, the `ws.Hub`, the LiveKit client/subprocess, the updater, the admin handler, and the plugin admin handler; spawns background goroutines; and mounts all routes. `main.go` performs only process-level wiring (config, TLS, DB, event persistence, HTTP server, shutdown). **Source of truth:** `Server/main.go`, `Server/api/router.go`, package import graph (`go list -deps`), `Server/service/datastore.go`, `sqlc.yaml`. ## D3 — REST request lifecycle ```mermaid sequenceDiagram autonumber participant C as Client participant MW as Global middleware
(api/router.go) participant RT as Route mount
(Mount*Routes) participant H as Handler participant S as service.* participant ST as service.Store
(*db.DB) participant DB as SQLite C->>MW: HTTPS request Note over MW: RequestID → Recoverer → requestLogger
→ telemetry → SecurityHeadersWithTLS
→ MaxBodySizeUnless(uploads exempt)
→ optional Coraza WAF MW->>RT: routed by chi Note over RT: AuthMiddleware(database)
+ per-route rate limits
+ RequirePermission(...) where mounted RT->>H: authenticated request H->>S: domain call (svc.Messages, svc.Permissions, …) S->>ST: narrow Store interface method ST->>DB: SQL (db.DB query method → sqlc dbgen) DB-->>C: JSON response (errorResponse envelope on failure) rect rgba(200,120,120,0.15) Note over H,DB: Deviation — auth routes: MountAuthRoutes(r, database, …)
bypasses the service layer and queries *db.DB directly.
Admin REST (Server/admin) does the same behind
AdminIPRestrict + RequireAdminAuth. end ``` **What this shows.** The global chain is assembled in `NewRouter`; note that chi's `middleware.RealIP` is deliberately omitted — client IP is resolved via `clientIPWithProxies` against configured trusted proxies instead, so spoofed `X-Real-IP`/`X-Forwarded-For` headers are not trusted by default. Authentication is bearer-token (SHA-256-hashed opaque tokens); authorization is enforced at two deliberate scopes (D13): `RequirePermission` middleware gates the two channel-less routes on server-wide role permissions via `permissions.HasServerPerm` (channel overrides deliberately not consulted — a per-channel allow must never open a server-wide gate), while anything channel-scoped is checked in the service layer through `svc.Permissions` / `permissions.Checker`, which resolves overrides and fails closed if they cannot be fetched. The shaded region marks the two documented bypass paths of the domain layer. **Source of truth:** `Server/api/router.go`, `Server/api/middleware.go`, `Server/api/auth_handler.go`, `Server/admin/middleware.go`, `Server/permissions/`, `Server/service/`.