Compose is de ondersteunde manier om Appsweet te draaien. Het bestand hieronder is het minimale voorbeeld: het geeft je Appsweet plus een persistente Postgres, en verder niets.
Dit voorbeeld is geen complete deployment waarop iemand kan inloggen. Het definieert
twee services, en geen van beide is een identity provider — APPSWEET_OIDC_ISSUER hieronder
wijst naar een placeholder die je moet vervangen door een Zitadel-instantie die je zelf al
draait. Vul een echte issuer in en de backend start; laat de placeholder staan en hij start
ook, maar niemand kan inloggen. Draai je er nog geen, lees dan eerst "Je hebt nog steeds
Zitadel nodig" verderop.
Het Compose-bestand
Zet dit in een docker-compose.yml en draai docker compose up -d. Images worden gekozen
op exacte versie plus digest, overgenomen uit het gevalideerde release-manifest voor die
versie — er is geen latest-tag, en geen enkele veranderlijke tag geldt als bron van
waarheid.
services: appsweet: image: ghcr.io/blendable-dev/appsweet-backend:0.1.0-beta.1@sha256:<digest-from-the-release-manifest> ports: ["3000:3000"] # Long form, not `depends_on: [db]` — see "Waiting for Postgres" below. depends_on: db: condition: service_healthy environment: APPSWEET_DATABASE_URL: postgres://appsweet:secret@db:5432/appsweet # The default 127.0.0.1 would not be reachable through the port mapping. APPSWEET_BIND_ADDR: 0.0.0.0:3000 APPSWEET_PUBLIC_BASE_URL: http://localhost:3000 # Read during startup, not on the first sign-in. Without the pair the # container exits before it binds a port. Point both at your issuer. APPSWEET_OIDC_ISSUER: https://auth.example.com APPSWEET_OIDC_CLIENT_ID: your-client-id db: # Not plain postgres:16 — the first migration runs # CREATE EXTENSION IF NOT EXISTS vector, which the stock image cannot do. image: pgvector/pgvector:pg16 environment: POSTGRES_USER: appsweet POSTGRES_PASSWORD: secret POSTGRES_DB: appsweet volumes: [pgdata:/var/lib/postgresql/data] healthcheck: test: ["CMD-SHELL", "pg_isready -U appsweet -d appsweet"] interval: 5s timeout: 5s retries: 12 volumes: { pgdata: {} }
Appsweet bewaart zelf geen state op zijn bestandssysteem, dus het heeft geen eigen volume nodig: werkplekdata staat in Postgres en geüploade bestanden in S3-compatibele objectopslag.
Je hebt nog steeds Zitadel nodig
Appsweet beheert geen wachtwoorden, dus de twee services hierboven kunnen niemand inloggen.
https://auth.example.com is een placeholder: vervang die door de issuer-URL en client-ID
van een Zitadel-instantie die je zelf draait of afneemt, en registreer
$APPSWEET_PUBLIC_BASE_URL/auth/callback daar als redirect-URI.
Zitadel is vandaag de enige identity provider die Appsweet ondersteunt. Standaard
OIDC-discovery spreken is niet genoeg. De backend vraagt elke issuer om de
Zitadel-specifieke scope urn:zitadel:iam:user:resourceowner, en bepaalt de werkplek van de
ingelogde gebruiker uit drie claims die het onder hun Zitadel-namen opzoekt:
urn:zitadel:iam:user:resourceowner:id, …:name en …:primary_domain. Ontbreken die
claims, dan wordt de sessie geweigerd: de terugkeer vanaf de provider lijkt te lukken,
waarna /v1/me antwoordt met 403 workspace_claims_required en er nooit een werkplek
laadt. Precies zover komt een standaardinstallatie van Keycloak, Authentik, Auth0 of Entra
ID. Er is geen instelling om claims om te zetten die dat gat dicht — die is nog niet
gebouwd.
Heb je er geen, dan levert de Appsweet-repository een stack die Zitadel opstart naast
Appsweet, Postgres en Garage-objectopslag, al aan elkaar geknoopt en vastgezet op versies
die wij testen: infra/docker-compose.yml. Gebruik die in plaats van het bestand hierboven.
Die stack staat hier niet, en losse fragmenten eruit kopiëren werkt niet: hij bouwt de backend en zijn bootstrap-containers uit de build-contexten van de repository zelf, en een Zitadel-instantie heeft een adminaccount, een project, een applicatie en een gebootstrapt client-ID nodig voordat hij ook maar één token uitgeeft. Elk Zitadel-fragment dat kort genoeg is voor deze pagina, zou een fragment zijn dat je niet aan de praat krijgt. De repository is privé tijdens de gesloten bèta, dus vraag om toegang als je de stack nodig hebt en niet hebt.
Wachten op Postgres
De depends_on hierboven staat bewust in zijn lange vorm met
condition: service_healthy, gekoppeld aan een healthcheck op db. De korte vorm —
depends_on: [db] — wacht alleen tot de database-container start, niet tot PostgreSQL
daarbinnen verbindingen accepteert. Op een vers volume duurt dat gat seconden, omdat de
image eerst de datadirectory moet initialiseren voordat hij luistert.
Appsweet verbindt één keer met de database tijdens het opstarten en probeert het niet
opnieuw, en de service hierboven kent geen restart-policy. Met de korte vorm kan de eerste
docker compose up -d op een nieuwe machine de backend dus definitief gestopt achterlaten
terwijl Postgres nog initialiseert, en antwoordt curl localhost:3000/ready helemaal niets.
Bij de tweede poging lijkt het meestal in orde, en juist daardoor is het makkelijk om dit
voor een flake aan te zien en de lange vorm "op te schonen" — doe dat alsjeblieft niet.
pg_isready is de gebruikelijke probe, en is wat de stack van Appsweet zelf gebruikt: hij
sluit alleen met nul af zodra de server verbindingen accepteert voor die gebruiker en
database.
Waarom pgvector en niet gewoon Postgres
De semantische zoekfunctie van Appsweet bewaart embeddings in dezelfde database als de data,
dus de allereerste migratie is CREATE EXTENSION IF NOT EXISTS vector. Op een standaard
postgres-image mislukt die opdracht, breken de migraties af en bereikt de backend nooit
zijn gereed-status.
pgvector/pgvector:pg16 is maar één manier om daaraan te voldoen. Zet je je eigen Postgres
in — een managed dienst, een bestaand cluster, een andere hoofdversie — behandel
"pgvector is geïnstalleerd en aan te maken" dan als een harde eis, niet als een detail van
de image die hier toevallig gekozen is. De meeste managed providers leveren het als een
extensie die je per database aanzet.
Het register is privé tijdens de bèta
Het pakket appsweet-backend is privé zolang de gesloten bèta loopt, dus
docker compose pull heeft een ghcr.io-credential nodig met leesrechten op packages.
Zonder credential mislukt de pull alsof de image niet bestaat.
Dit is het minimum dat opstart, niet het minimum dat bruikbaar is. Er ontbreken nog twee dingen: een Zitadel-issuer, zonder welke niemand inlogt, en een S3-compatibel endpoint voor uploads, zonder welk bestanden helemaal uitstaan. Allebei zijn een ontwikkelsluiproute en geen ondersteunde deployment — zie Configuratie.