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.
228 lines
6.9 KiB
Markdown
228 lines
6.9 KiB
Markdown
# Deployment
|
|
|
|
How Drive runs in production as part of the SBBB Kubernetes stack.
|
|
|
|
---
|
|
|
|
## Where This Fits
|
|
|
|
Drive replaces the upstream [suitenumerique/drive](https://github.com/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
|
|
|
|
```bash
|
|
deno task build
|
|
```
|
|
|
|
Produces a single `driver` binary via `deno compile`:
|
|
|
|
```bash
|
|
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/dist` directory (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:
|
|
```json
|
|
{ "ok": true, "time": "2026-03-25T10:30:00.000Z" }
|
|
```
|
|
|
|
No auth required. Use this for Kubernetes liveness and readiness probes:
|
|
|
|
```yaml
|
|
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.
|
|
|
|
```yaml
|
|
# Collabora environment
|
|
aliasgroup1: "https://drive\\.example\\.com"
|
|
```
|
|
|
|
The value is a regex — dots must be escaped. Multiple hosts:
|
|
|
|
```yaml
|
|
aliasgroup1: "https://drive\\.example\\.com|https://drive\\.staging\\.example\\.com"
|
|
```
|
|
|
|
In the Docker Compose test stack, this is:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```typescript
|
|
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):
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
1. **Keto server** with read (4466) and write (4467) APIs
|
|
2. **OPL namespaces** from `keto/namespaces.ts` loaded at deploy time
|
|
|
|
The namespace file defines the permission model (User, Group, Bucket, Folder, File). See [permissions.md](permissions.md) for the details.
|
|
|
|
Typical Keto config:
|
|
|
|
```yaml
|
|
# 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:
|
|
|
|
```bash
|
|
DATABASE_URL="postgres://..." ./driver migrate
|
|
```
|
|
|
|
Or, if running from source:
|
|
|
|
```bash
|
|
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.
|
|
- `/health` endpoint for uptime monitoring
|
|
- PostgreSQL query logs for database performance
|
|
- S3 access logs from SeaweedFS
|
|
- Container stdout/stderr
|
|
|
|
---
|
|
|
|
## Deployment Checklist
|
|
|
|
1. Build the binary: `deno task build`
|
|
2. Set **`WOPI_JWT_SECRET`** to a random secret (32+ characters)
|
|
3. Set **`CSRF_COOKIE_SECRET`** to a different random secret
|
|
4. Set **`PUBLIC_URL`** to the actual user-facing URL
|
|
5. Set **`DATABASE_URL`** and run migrations
|
|
6. Ensure SeaweedFS bucket exists
|
|
7. Configure Collabora `aliasgroup1` to allow WOPI callbacks from `PUBLIC_URL`
|
|
8. Register the OIDC client in Hydra (or use the `oidc-drive` secret)
|
|
9. Deploy Keto with the namespace file from `keto/namespaces.ts`
|
|
10. Verify: `curl https://drive.example.com/health`
|
|
11. **Do not** set `DRIVER_TEST_MODE=1` in production. It disables all auth. You will have a bad time.
|