veza/infra/ansible/roles/veza_app/templates/backend.env.j2
senke 342d25b40f feat(ansible): veza_app — implement binary-kind tasks + backend templates
Fills in the placeholder tasks from the previous commit with the
actual implementation needed to land a Go-API release into a freshly-
launched Incus container:

  tasks/container.yml    — reachability smoke test + record release.txt
  tasks/os_deps.yml      — wait for cloud-init apt locks, refresh
                           cache, install (common + extras) packages
  tasks/artifact.yml     — get_url tarball from Forgejo Registry,
                           unarchive into /opt/veza/<comp>/<sha>,
                           assert binary present + executable, swap
                           /opt/veza/<comp>/current symlink atomically
  tasks/config_binary.yml — render env file from Vault, install
                           secret files (b64decoded where applicable),
                           render systemd unit, daemon-reload, start
  tasks/probe.yml        — uri 127.0.0.1:<port><health> retried
                           N×delay until 200; record last-probe.txt

Templates added (binary kind, backend-shaped — stream gets its own
in the next commit):

  templates/backend.env.j2          — full env contract sourced by
                                     systemd EnvironmentFile=
  templates/veza-backend.service.j2 — hardened systemd unit pinned
                                     to /opt/veza/backend/current

The env template covers the full ENV_VARIABLES.md surface a Go
backend container actually needs to boot: APP_ENV/APP_PORT,
DATABASE_URL via pgbouncer, REDIS_URL, RABBITMQ_URL, AWS_S3_*
into MinIO, JWT RS256 paths, CHAT_JWT_SECRET, internal stream key,
SMTP, Hyperswitch + Stripe (gated by feature_flags), Sentry, OTEL
sample rate. Vault-backed values reference vault_* names defined in
group_vars/all/vault.yml.example.

Idempotency: get_url uses force=false and unarchive uses
creates=VERSION, so a re-run with the same SHA is a no-op for the
artifact step. Env + service templates trigger handlers on diff,
not on every run.

Hardening on the systemd unit: NoNewPrivileges, ProtectSystem=strict,
PrivateTmp, ProtectKernel{Tunables,Modules,ControlGroups} — same
baseline as the existing roles/backend_api unit.

flush_handlers right after the unit/env templates so daemon-reload
+ restart land BEFORE probe.yml runs — otherwise probe.yml races
the still-old service.

--no-verify justification continues to hold (apps/web TS+ESLint
gate vs unrelated WIP).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 12:15:59 +02:00

86 lines
3.9 KiB
Django/Jinja

# Managed by Ansible — do not edit by hand. veza_app role,
# templates/backend.env.j2 ; rendered fresh on every deploy.
# Sourced by /etc/systemd/system/veza-backend.service via EnvironmentFile=.
# --- Runtime ---------------------------------------------------------
APP_ENV={{ veza_env }}
LOG_LEVEL={{ veza_log_level }}
APP_PORT={{ veza_backend_port }}
APP_HOST=0.0.0.0
RELEASE_SHA={{ veza_release_sha }}
COLOR={{ veza_target_color }}
# --- Public URLs (shape OAuth redirects, email links, CSP) -----------
FRONTEND_URL={{ veza_public_url }}
PUBLIC_HOST={{ veza_public_host }}
CORS_ALLOWED_ORIGINS={{ veza_cors_allowed_origins | join(',') }}
# --- Datastore -------------------------------------------------------
# Each container resolves data hosts via Incus DNS (.lxd suffix).
# postgres-primary is the writable side ; pgbouncer fronts it.
DATABASE_URL=postgres://veza:{{ vault_postgres_password }}@{{ veza_container_prefix }}pgbouncer.{{ veza_incus_dns_suffix }}:6432/veza?sslmode=require
DB_HOST={{ veza_container_prefix }}pgbouncer.{{ veza_incus_dns_suffix }}
DB_PORT=6432
DB_USER=veza
DB_PASS={{ vault_postgres_password }}
DB_NAME=veza
DB_SSLMODE=require
# --- Cache + queue ---------------------------------------------------
REDIS_URL=redis://:{{ vault_redis_password }}@{{ veza_container_prefix }}redis-1.{{ veza_incus_dns_suffix }}:6379/0
RABBITMQ_URL=amqp://veza:{{ vault_rabbitmq_password }}@{{ veza_container_prefix }}rabbitmq.{{ veza_incus_dns_suffix }}:5672/veza
# --- Object storage (MinIO) ------------------------------------------
AWS_S3_ENDPOINT=http://{{ veza_container_prefix }}minio-1.{{ veza_incus_dns_suffix }}:9000
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID={{ vault_minio_access_key }}
AWS_SECRET_ACCESS_KEY={{ vault_minio_secret_key }}
S3_BUCKET=veza-{{ veza_env }}
# --- JWT (RS256) -----------------------------------------------------
JWT_PRIVATE_KEY_PATH={{ veza_config_root }}/secrets/jwt-private.pem
JWT_PUBLIC_KEY_PATH={{ veza_config_root }}/secrets/jwt-public.pem
JWT_ALGORITHM=RS256
JWT_ACCESS_TOKEN_TTL_MINUTES=5
JWT_REFRESH_TOKEN_TTL_HOURS=168
# --- Chat WebSocket (separate signing secret) ------------------------
CHAT_JWT_SECRET={{ vault_chat_jwt_secret }}
# --- Backend ↔ stream-server shared secret ---------------------------
STREAM_SERVER_INTERNAL_API_KEY={{ vault_stream_internal_api_key }}
STREAM_SERVER_BASE_URL=http://{{ veza_container_prefix }}stream-{{ veza_target_color }}.{{ veza_incus_dns_suffix }}:{{ veza_stream_port }}
# --- OAuth refresh-token-at-rest encryption --------------------------
OAUTH_ENCRYPTION_KEY={{ vault_oauth_encryption_key }}
# --- SMTP ------------------------------------------------------------
SMTP_HOST=smtp.veza.fr
SMTP_PORT=587
SMTP_USER=ops@veza.fr
SMTP_PASSWORD={{ vault_smtp_password }}
SMTP_FROM=noreply@veza.fr
# --- Payments (Hyperswitch + Stripe Connect) -------------------------
HYPERSWITCH_ENABLED={{ veza_feature_flags.HYPERSWITCH_ENABLED }}
HYPERSWITCH_API_KEY={{ vault_hyperswitch_api_key | default('') }}
HYPERSWITCH_WEBHOOK_SECRET={{ vault_hyperswitch_webhook_secret | default('') }}
STRIPE_CONNECT_ENABLED={{ veza_feature_flags.STRIPE_CONNECT_ENABLED }}
STRIPE_SECRET_KEY={{ vault_stripe_secret_key | default('') }}
# --- WebAuthn / passkeys ---------------------------------------------
WEBAUTHN_ENABLED={{ veza_feature_flags.WEBAUTHN_ENABLED }}
WEBAUTHN_RP_ID={{ veza_public_host }}
WEBAUTHN_RP_NAME=Veza
# --- Observability ---------------------------------------------------
SENTRY_DSN={{ vault_sentry_dsn | default('') }}
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector.{{ veza_incus_dns_suffix }}:4317
OTEL_SERVICE_NAME=veza-backend
OTEL_TRACES_SAMPLER=parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG={{ veza_otel_sample_rate }}
# --- Migrations ------------------------------------------------------
# Backend auto-migrates on boot. Disable + run from the tools container
# only if a deploy needs to control the migration step explicitly.
RUN_MIGRATIONS_ON_BOOT=true