Lightweight replacement for the upstream La Suite Numérique drive (Django/Celery/Next.js) built as a single Deno binary. Server (Deno + Hono): - S3 file operations via AWS SigV4 (no SDK) with pre-signed URLs - WOPI host for Collabora Online (CheckFileInfo, GetFile, PutFile, locks) - Ory Kratos session auth + CSRF protection - Ory Keto permission model (OPL namespaces, not yet wired to routes) - PostgreSQL metadata with recursive folder sizes - S3 backfill API for registering files uploaded outside the UI - OpenTelemetry tracing + metrics (opt-in via OTEL_ENABLED) Frontend (React 19 + Cunningham v4 + react-aria): - File browser with GridList, keyboard nav, multi-select - Collabora editor iframe (full-screen, form POST, postMessage) - Profile menu, waffle menu, drag-drop upload, asset type badges - La Suite integration service theming (runtime CSS) Testing (549 tests): - 235 server unit tests (Deno) — 90%+ coverage - 278 UI unit tests (Vitest) — 90%+ coverage - 11 E2E tests (Playwright) - 12 integration service tests (Playwright) - 13 WOPI integration tests (Playwright + Docker Compose + Collabora) MIT licensed.
6.9 KiB
Deployment
How Drive runs in production as part of the SBBB Kubernetes stack.
Where This Fits
Drive replaces the upstream suitenumerique/drive Helm chart in the SBBB stack. Same role, one binary instead of Django + Celery + Redis queues + Next.js.
Sits alongside the other La Suite apps and shares the Ory identity stack (Kratos + Hydra) for auth.
The Binary
deno task build
Produces a single driver binary via deno compile:
deno compile --allow-net --allow-read --allow-env --include ui/dist -o driver main.ts
Bundles:
- Deno runtime
- All server TypeScript (compiled to JS)
- The entire
ui/distdirectory (React SPA)
No Node.js, no npm, no node_modules at runtime. Copy the binary into a container and run it. That's the deployment.
Environment Variables
Everything is configured via environment variables. No config files.
| Variable | Default | Required | Description |
|---|---|---|---|
PORT |
3000 |
No | Server listen port |
PUBLIC_URL |
http://localhost:3000 |
Yes | Public-facing URL. Used in WOPI callback URLs, redirects, and CSRF. Must be the URL users see in their browser. |
DATABASE_URL |
postgres://driver:driver@localhost:5432/driver_db |
Yes | PostgreSQL connection string |
SEAWEEDFS_S3_URL |
http://seaweedfs-filer.storage.svc.cluster.local:8333 |
No | S3 endpoint |
SEAWEEDFS_ACCESS_KEY |
(empty) | No | S3 access key (empty = unauthenticated) |
SEAWEEDFS_SECRET_KEY |
(empty) | No | S3 secret key |
S3_BUCKET |
sunbeam-driver |
No | S3 bucket name |
S3_REGION |
us-east-1 |
No | S3 region for signing |
VALKEY_URL |
redis://localhost:6379/2 |
No | Valkey/Redis URL for WOPI locks. Falls back to in-memory if unavailable. Set to your Valkey cluster URL in production. |
KRATOS_PUBLIC_URL |
http://kratos-public.ory.svc.cluster.local:80 |
No | Kratos public API for session validation |
KETO_READ_URL |
http://keto-read.ory.svc.cluster.local:4466 |
No | Keto read API for permission checks |
KETO_WRITE_URL |
http://keto-write.ory.svc.cluster.local:4467 |
No | Keto write API for tuple management |
COLLABORA_URL |
http://collabora.lasuite.svc.cluster.local:9980 |
No | Collabora Online for WOPI discovery |
WOPI_JWT_SECRET |
dev-wopi-secret-change-in-production |
Yes | HMAC secret for WOPI access tokens. Change this in production. |
CSRF_COOKIE_SECRET |
dev-secret-change-in-production |
Yes | HMAC secret for CSRF double-submit cookies. Change this in production. |
DRIVER_TEST_MODE |
(unset) | No | Set to 1 to bypass auth. Never set this in production. |
Health Check
GET /health
Returns:
{ "ok": true, "time": "2026-03-25T10:30:00.000Z" }
No auth required. Use this for Kubernetes liveness and readiness probes:
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 3
periodSeconds: 5
Collabora Configuration
Collabora needs to know which hosts can make WOPI callbacks. This is the aliasgroup1 env var on the Collabora deployment. Get this wrong and Collabora will reject every callback with a "Not allowed" error.
# Collabora environment
aliasgroup1: "https://drive\\.example\\.com"
The value is a regex — dots must be escaped. Multiple hosts:
aliasgroup1: "https://drive\\.example\\.com|https://drive\\.staging\\.example\\.com"
In the Docker Compose test stack, this is:
aliasgroup1: "http://host\\.docker\\.internal:3200"
OIDC Client Setup
Drive authenticates users via Ory Kratos sessions. For OIDC flows, you need a client registered in Ory Hydra.
Client credentials go in a Kubernetes secret:
apiVersion: v1
kind: Secret
metadata:
name: oidc-drive
type: Opaque
stringData:
client-id: "drive"
client-secret: "your-secret-here"
The Kratos identity schema needs standard OIDC claims (email, name). Drive reads from session traits:
const givenName = traits.given_name ?? traits.name?.first ?? "";
const familyName = traits.family_name ?? traits.name?.last ?? "";
Supports both OIDC-standard (given_name/family_name) and legacy (name.first/name.last) formats. You probably won't need to think about this.
SeaweedFS Bucket
Create the bucket before first deploy (or don't — SeaweedFS creates buckets on first write with default config):
# Using AWS CLI pointed at SeaweedFS
aws --endpoint-url http://seaweedfs:8333 s3 mb s3://sunbeam-driver
Bucket name defaults to sunbeam-driver, configurable via S3_BUCKET.
Keto Deployment
Keto is new to the SBBB stack — Drive introduced it. You need to deploy:
- Keto server with read (4466) and write (4467) APIs
- OPL namespaces from
keto/namespaces.tsloaded at deploy time
The namespace file defines the permission model (User, Group, Bucket, Folder, File). See permissions.md for the details.
Typical Keto config:
# Keto config
dsn: postgres://keto:keto@postgres:5432/keto_db
namespaces:
location: file:///etc/keto/namespaces.ts
serve:
read:
host: 0.0.0.0
port: 4466
write:
host: 0.0.0.0
port: 4467
Read API: accessible from Drive pods. Write API: accessible from Drive pods + admin tooling. Neither should be exposed to the internet.
Database Migration
Run migrations before first deploy and after updates that add new ones:
DATABASE_URL="postgres://..." ./driver migrate
Or, if running from source:
DATABASE_URL="postgres://..." deno run -A server/migrate.ts
Idempotent — running them multiple times is safe. A _migrations table tracks what's been applied.
Observability
OpenTelemetry tracing and metrics are built in (server/telemetry.ts) — every request is instrumented automatically.
Full picture:
- OpenTelemetry — tracing and metrics via OTLP. Especially useful for debugging WOPI callback chains.
/healthendpoint for uptime monitoring- PostgreSQL query logs for database performance
- S3 access logs from SeaweedFS
- Container stdout/stderr
Deployment Checklist
- Build the binary:
deno task build - Set
WOPI_JWT_SECRETto a random secret (32+ characters) - Set
CSRF_COOKIE_SECRETto a different random secret - Set
PUBLIC_URLto the actual user-facing URL - Set
DATABASE_URLand run migrations - Ensure SeaweedFS bucket exists
- Configure Collabora
aliasgroup1to allow WOPI callbacks fromPUBLIC_URL - Register the OIDC client in Hydra (or use the
oidc-drivesecret) - Deploy Keto with the namespace file from
keto/namespaces.ts - Verify:
curl https://drive.example.com/health - Do not set
DRIVER_TEST_MODE=1in production. It disables all auth. You will have a bad time.