Skip to content

synvirt-platform (web ecosystem)

Synced read-only from /home/synvirt/synvirt-platform/README.md. Edit at the source, not here.

Marketing site, customer portal, documentation, community and commerce for SynVirt — one server today, migratable to multi-host from day one.

The marketing site at synvirt.io runs on WordPress (apps/wordpress) since 2026-07-29. The portal, docs and API are unchanged — see that app’s README.

Repo language: code, paths, commands, commits, logs and docs are in English. Planning prose may be in Spanish (see docs-internal/).

A single Docker Compose stack behind one ingress (Caddy). Each service has its own logical database, so any piece can move to its own host later by changing env, not code. Uploads/media live on local disk for now; S3-compatible storage returns at M5 for entitlement-gated downloads (ADR-0003 amendment). See the ADRs in docs-internal/adr/.

Caddy (only ingress, 80/443)
├── synvirt.io → wordpress (marketing site, container)
├── portal / docs → web (Next.js, host in dev)
├── api.synvirt.test → portal-api (Rust/Axum, host in dev)
├── auth.synvirt.test → keycloak
├── community.synvirt.test → discourse (M7, separate install)
└── (internal) postgres · redis · meilisearch
Terminal window
# 1. Install the toolchain (Docker, Node 22, Rust, sops, age, Task).
# Idempotent; safe to re-run. Log out/in afterwards if it added you to docker.
bash infra/scripts/devbox-bootstrap.sh
# 2. Configure environment.
cp .env.example .env # then edit the change_me values
# 3. Bring up backing services (postgres, redis, meili, keycloak, caddy).
task up
# 4. Trust Caddy's internal CA so *.synvirt.test is valid HTTPS locally.
# (The *.synvirt.io names use real Let's Encrypt certs via Cloudflare
# DNS-01 — CLOUDFLARE_API_TOKEN in .env — nothing to trust manually.)
task trust-ca
# 5. Run database migrations.
task migrate
# 6. Run the apps on the host with hot reload:
task dev:web # Next.js web on :3000 (proxied at https://synvirt.test)
task dev:api # Rust portal-api on :8080
task storybook # design-system Storybook on :6006

Then open https://auth.synvirt.test (Keycloak), https://api.synvirt.test/health.

Path What
apps/wordpress WordPress marketing site (synvirt.io) — block theme + provisioning
apps/web Next.js — portal UI + Payload CMS + Fumadocs (no longer the apex)
apps/portal-api Rust + Axum + SQLx portal API
packages/ui Design system — tokens, Tailwind v4 theme, shadcn-style components, Storybook
packages/config Shared tsconfig / tooling config
packages/api-client TS client generated from the API’s OpenAPI
infra/compose compose.yaml + dev/prod overrides
infra/caddy Caddyfiles (dev: tls internal)
infra/postgres/init Multi-database bootstrap
infra/keycloak Realm import
infra/secrets SOPS-encrypted env (*.enc.env)
infra/scripts devbox-bootstrap.sh, seed.sh
docs-internal ADRs, roadmap, runbooks (English)

Run task (no args) to list every available command.

Phase 0 (devbox) and M0 (foundations: Next.js 16 + design system + i18n + Storybook) complete. Milestones M1–M8 tracked in docs-internal/roadmap.md.

  • Hostname: synvirt (the synvirt.io host alias is preserved).
  • Repository: /home/synvirt/synvirt-platform, owned by synvirt.
  • DevHub: /home/synvirt/synnet-devhub; legacy paths are compatibility symlinks.
  • WordPress and Caddy run in the existing synvirt-platform Compose project; database volumes are preserved. This host still uses the dev Compose override.
  • synvirt-web.service and synvirt-api.service run as synvirt and start at boot.
  • Runtime binaries: /opt/synvirt/bin/node and /opt/synvirt/bin/portal-api. API rebuilds require copying the newly built binary here and restarting the service.
  • API environment: /etc/synvirt/portal-api.env (root-readable only). /health checks PostgreSQL; business endpoints remain to be implemented.
  • WordPress backups: synvirt-backup.timer, daily, in /var/backups/synvirt.
  • Before migration, a fresh database/media backup was taken; original systemd units are in /var/backups/synvirt/server-migration-20260929.