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
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.
307 lines
7.5 KiB
Markdown
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
|
|
```
|