Files
dezky/docs/SERVICES.md
T
Ronni Baslund 4b71b5751f
ci / changes (push) Successful in 4s
ci / tc_operator (push) Has been skipped
ci / tc_website (push) Has been skipped
ci / test_platform_api (push) Has been skipped
ci / tc_platform_api (push) Has been skipped
ci / build_portal (push) Has been skipped
ci / build_booking (push) Has been skipped
ci / build_operator (push) Has been skipped
ci / build_platform_api (push) Has been skipped
ci / tc_portal (push) Has been skipped
ci / tc_booking (push) Has been skipped
ci / build_zpush (push) Has been skipped
ci / deploy (push) Successful in 28s
fix(mail): chown zpush state on pod start — root-owned files break sync
A root-run z-push-admin (kubectl exec defaults to root) left a
root-owned 'users' file on the state PVC; Apache runs as www-data, so
every request 500'd with 'Not possible to write to the configured
state directory'. An initContainer now normalizes ownership on every
start (state is disposable, ownership isn't precious), and the docs
say to exec z-push-admin as www-data.
2026-06-12 15:46:31 +02:00

307 lines
7.5 KiB
Markdown

# Services Reference
Per-service details: what each one does, where its config lives, and how to debug it.
## Traefik
**Image:** `traefik:v3.2`
**Container:** `dezky-traefik`
**URL:** https://traefik.dezky.local (dashboard)
**Purpose:** Reverse proxy, TLS termination, service discovery via Docker labels
**Config:**
- Static: `configs/traefik/traefik.yml`
- Dynamic: `configs/traefik/dynamic.yml` (TLS certs)
- Certs: `certs/dezky.local.pem` + `certs/dezky.local-key.pem`
**Debug:**
```bash
docker compose logs -f traefik
# Open https://traefik.dezky.local for dashboard
```
---
## PostgreSQL
**Image:** `postgres:16-alpine`
**Container:** `dezky-postgres`
**Internal hostname:** `postgres`
**Purpose:** Shared RDBMS for Authentik and OCIS (future)
**Databases:**
- `authentik` (owner: `authentik`)
- `ocis` (owner: `ocis`, reserved for future use)
**Debug:**
```bash
# Shell access
docker compose exec postgres psql -U postgres
# Check users
\du
# Check databases
\l
# Connect to specific DB
\c authentik
```
---
## MongoDB
**Image:** `mongo:7`
**Container:** `dezky-mongo`
**Internal hostname:** `mongo`
**Purpose:** Portal application data
**Connection:**
```
mongodb://root:${MONGO_ROOT_PASSWORD}@mongo:27017/dezky?authSource=admin
```
**Debug:**
```bash
docker compose exec mongo mongosh -u root -p $(grep MONGO_ROOT_PASSWORD .env | cut -d= -f2)
```
---
## Redis
**Image:** `redis:7-alpine`
**Container:** `dezky-redis`
**Internal hostname:** `redis`
**Purpose:** Cache and session store (used by Authentik)
**Debug:**
```bash
docker compose exec redis redis-cli -a $(grep REDIS_PASSWORD .env | cut -d= -f2)
> KEYS *
> INFO
```
---
## Authentik
**Image:** `ghcr.io/goauthentik/server:2025.10`
**Containers:** `dezky-authentik` (server) + `dezky-authentik-worker`
**URL:** https://auth.dezky.local
**Purpose:** Identity provider, SSO, MFA
**First-time setup:**
- URL: https://auth.dezky.local/if/flow/initial-setup/
- Email: `admin@dezky.local`
- Password: `AUTHENTIK_BOOTSTRAP_PASSWORD` from `.env`
**API:**
- Base: https://auth.dezky.local/api/v3
- Auth: `Authorization: Bearer <AUTHENTIK_BOOTSTRAP_TOKEN>`
- Docs: https://auth.dezky.local/api/v3/
**Debug:**
```bash
docker compose logs -f authentik-server authentik-worker
# Check API health
curl https://auth.dezky.local/-/health/ready/
```
See `docs/AUTHENTIK-SETUP.md` for OIDC configuration steps.
---
## Stalwart Mail
**Image:** `stalwartlabs/mail-server:latest`
**Container:** `dezky-stalwart`
**URL:** https://mail.dezky.local
**Purpose:** Mail server (SMTP/IMAP/JMAP/CalDAV/CardDAV — ActiveSync comes
from the separate zpush gateway, see below)
**Ports exposed:**
- 25 (SMTP)
- 465 (SMTPS)
- 587 (Submission)
- 143 (IMAP)
- 993 (IMAPS)
- 4190 (ManageSieve)
**Config:** `configs/stalwart/config.toml`
**Data:** Docker volume `dezky_stalwart_data`
**Admin login:**
- User: `admin`
- Password: `STALWART_ADMIN_PASSWORD` from `.env`
**Debug:**
```bash
docker compose logs -f stalwart
# Test SMTP
swaks --to test@dezky.local --from sender@example.com --server mail.dezky.local:25
# Check ports
docker compose port stalwart 25
```
---
## Z-Push (EAS gateway)
**Image:** built from `services/zpush` (Z-Push 2.6.4, AGPLv3 — see
`services/zpush/LICENSE-NOTES.md`)
**Container:** `dezky-zpush`
**URL:** https://mail.dezky.local/Microsoft-Server-ActiveSync (+ EAS
autodiscover on https://autodiscover.dezky.local)
**Purpose:** Exchange ActiveSync gateway in front of Stalwart — "Exchange"
accounts on iOS/Android native Mail/Calendar get two-way mail + calendar
sync (IMAP + CalDAV fan-out via BackendCombined). Contacts are NOT bundled
yet: the combined login is all-or-nothing and Stalwart 404s addressbook
homes that were never created — re-enable CardDAV once platform-api
provisions a default address book at mailbox creation.
**Protocol reality check:** EAS 14.1. Covers native mobile clients; NOT the
Outlook mobile app (requires EAS 16.1) and not new Outlook for Windows (no
EAS at all). Classic Outlook on Windows syncs calendars against `/dav` with
the free Outlook CalDAV Synchronizer add-in instead.
**Auth:** pure passthrough — the device's Basic credentials (mailbox
password or app password) go straight to Stalwart. No secrets stored;
`zpush_state` volume holds only resyncable device state.
**Debug:**
```bash
docker compose logs -f zpush
# Unauthenticated probe (expect 401 with realm="ZPush")
curl -k -i -X OPTIONS https://mail.dezky.local/Microsoft-Server-ActiveSync
# Authenticated: advertised EAS versions in MS-ASProtocolVersions header
curl -k -i -u user@tenant.tld:app-password -X OPTIONS \
https://mail.dezky.local/Microsoft-Server-ActiveSync
# Per-device sync state. ALWAYS run as www-data — a root-run z-push-admin
# leaves root-owned state files that 500 every request ("Not possible to
# write to the configured state directory"). The prod pod has an
# initContainer that re-chowns the state dir on start as a backstop.
docker exec -u www-data dezky-zpush php /usr/share/z-push/z-push-admin.php -a list
```
---
## OCIS
**Image:** `owncloud/ocis:7.0`
**Container:** `dezky-ocis`
**URL:** https://files.dezky.local
**Purpose:** File storage, sharing, sync
**OIDC config:**
- Issuer: `https://auth.dezky.local/application/o/ocis/`
- Client ID: `ocis-web` (configured in Authentik)
- Auto-provision: enabled (creates OCIS user on first SSO login)
**Admin login:**
- User: `admin`
- Password: `OCIS_ADMIN_PASSWORD` from `.env`
**Storage backend:**
- Dev: local filesystem inside volume `dezky_ocis_data`
- Prod: will switch to S3 (Hetzner Object Storage)
**Debug:**
```bash
docker compose logs -f ocis
# Health check
curl -k https://files.dezky.local/
```
---
## Collabora
**Image:** `collabora/code:latest`
**Container:** `dezky-collabora`
**URL:** https://office.dezky.local
**Purpose:** Office document editing inside OCIS
**Integration with OCIS:**
- OCIS must be configured to use Collabora as its office editor
- See: OCIS app config → "wopiserver"
**Debug:**
```bash
docker compose logs -f collabora
# Discovery endpoint (used by OCIS)
curl -k https://office.dezky.local/hosting/discovery
```
---
## Portal (Nuxt 3)
**Container:** `dezky-portal`
**URL:** https://app.dezky.local
**Source:** `apps/portal/`
**Purpose:** Customer-facing portal, launcher, custom webmail
**Stack:**
- Nuxt 3
- Vue 3 + TypeScript
- Vite dev server
- pnpm for dependencies
**Hot reload:**
- File changes in `apps/portal/` trigger HMR automatically
- Vite watches via polling (configured in `nuxt.config.ts`)
**Environment:**
- `NUXT_PUBLIC_AUTH_URL`: Authentik URL (client-side)
- `NUXT_API_BASE`: platform-api URL (server-side)
- `MONGODB_URI`: MongoDB connection string
**Debug:**
```bash
docker compose logs -f portal
# Shell into container
docker compose exec portal sh
> pnpm dev
```
---
## Platform API (NestJS)
**Container:** `dezky-platform-api`
**Port:** 3001 (also exposed via Traefik at `api.dezky.local`)
**Source:** `services/platform-api/`
**Purpose:** Platform control plane — tenants, partners, users, subscriptions, provisioning orchestration, billing webhooks
**Endpoints to implement:**
- `POST /tenants` — Create tenant
- `GET /tenants/:id` — Get tenant
- `PATCH /tenants/:id` — Update tenant
- `POST /tenants/:id/users` — Add user to tenant
- `POST /webhooks/stripe` — Billing events
**Environment:**
- `MONGODB_URI`: Portal data store
- `AUTHENTIK_API_URL` + `AUTHENTIK_API_TOKEN`
- `STALWART_API_URL` + `STALWART_ADMIN_USER/PASSWORD`
- `OCIS_API_URL`
**Debug:**
```bash
docker compose logs -f platform-api
# Test health endpoint
docker compose exec platform-api wget -qO- http://localhost:3001/health
```