Skip to content

Latest commit

 

History

History
296 lines (204 loc) · 13 KB

File metadata and controls

296 lines (204 loc) · 13 KB

Fresh Clone Starter Guide

This guide starts the marketplace API, PostgreSQL, Redis, and self-hosted ZITADEL from a fresh clone. Authentication is not embedded in NestJS: the browser signs in through ZITADEL Hosted Login and sends its access token to the API.

For the IAM design and production runbook, also read ZITADEL Authentication and Self-Hosting.

1. Requirements

Install:

  • Node.js 20.19+ (Node 22 LTS is the recommended development baseline);
  • Corepack and pnpm 10.14;
  • Docker Engine 24+ with Docker Compose v2;
  • Terraform 1.8+;
  • Git.

Docker should have at least 2 GB available for ZITADEL in addition to the marketplace services.

Verify the tools:

node --version
corepack --version
docker --version
docker compose version
terraform version
git --version

Default local ports are:

Service URL/port
Marketplace API http://localhost:3000
Swagger http://localhost:3000/docs
ZITADEL http://auth.localhost:8080
Marketplace PostgreSQL localhost:5432
Redis localhost:6379

2. Install dependencies and create local configuration

From the repository root:

corepack enable
corepack pnpm install --frozen-lockfile
Copy-Item .env.example .env
Copy-Item infra/zitadel/.env.example infra/zitadel/.env
New-Item -ItemType Directory -Force infra/zitadel/bootstrap | Out-Null
New-Item -ItemType Directory -Force secrets/zitadel | Out-Null

The root .env, ZITADEL .env, bootstrap files, runtime keys, Terraform state, and migration exports are ignored. Do not force-add them.

Before starting ZITADEL for the first time, replace these values in infra/zitadel/.env:

  • ZITADEL_MASTERKEY: a randomly generated value of exactly 32 characters; it cannot be changed after initialization;
  • ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: a strong temporary administrator password;
  • POSTGRES_ADMIN_PASSWORD and the matching password in ZITADEL_DATABASE_POSTGRES_DSN.

The local stack uses plain HTTP. Never expose it beyond an isolated development machine.

3. Start and provision ZITADEL

Start the pinned ZITADEL, Login, Traefik, and independent PostgreSQL stack:

Set-Location infra/zitadel
docker compose --env-file .env -f compose.yml up -d --wait
docker compose --env-file .env -f compose.yml ps
Set-Location ../..

Open http://auth.localhost:8080/ui/console/. The initial login normally follows zitadel-admin@zitadel.auth.localhost. Use the temporary password from the ZITADEL .env and change it.

Provision the marketplace organizations, project, capability roles, browser/API applications, default customer-registration actions, grants, and runtime machine keys:

Set-Location infra/zitadel/terraform
Copy-Item terraform.tfvars.example terraform.tfvars
terraform init
terraform plan
terraform apply
terraform output
terraform output -raw web_client_id
terraform output -raw api_client_id
Set-Location ../../..

Copy the output values to the corresponding root .env variables:

ZITADEL_ISSUER=http://auth.localhost:8080
ZITADEL_PROJECT_ID=<project_id>
ZITADEL_WEB_CLIENT_ID=<web_client_id>
ZITADEL_API_CLIENT_ID=<api_client_id>
ZITADEL_PLATFORM_ORG_ID=<platform_organization_id>
ZITADEL_CUSTOMER_ORG_ID=<customer_organization_id>
ZITADEL_API_PRIVATE_KEY_PATH=./secrets/zitadel/marketplace-api-key.json
ZITADEL_PROVISIONER_KEY_PATH=./secrets/zitadel/provisioner-key.json
ZITADEL_PROVISIONING_ENABLED=true

Leave ZITADEL_ROLE_CLAIM and ZITADEL_WEB_SCOPES empty to derive the project-specific defaults. The generated key paths must exist and match their configured client/user IDs.

4. Start marketplace infrastructure and database

Start the marketplace PostgreSQL and Redis services:

docker compose up -d postgres redis
docker compose ps

Generate Prisma Client, apply every checked-in migration, and load deterministic commerce fixtures:

corepack pnpm prisma:generate
corepack pnpm prisma:validate
corepack pnpm migrate:deploy
corepack pnpm seed

migrate:deploy is for checked-in migrations. Use migrate:dev -- --name <name> only while authoring a new local migration. Never use prisma db push as a production deployment strategy, and never reset a database containing valuable data.

pnpm seed also runs migrate:deploy before loading fixtures. If an older database is missing User.zitadelUserId even though Prisma reports the original ZITADEL migration as applied, the checked-in reconciliation migration adds the missing ZITADEL columns and provisioning records without deleting marketplace or rollback data.

The seed creates no credentials or ZITADEL assignments. Its seed-* subjects are deterministic references for automated tests, not accounts you can log into.

5. Start and verify the API

corepack pnpm start:dev

In another terminal:

Invoke-RestMethod http://localhost:3000/api/v1/health/live
Invoke-RestMethod http://localhost:3000/api/v1/health/ready
Invoke-RestMethod http://localhost:3000/api/v1/auth/config

Readiness verifies marketplace PostgreSQL, Redis when queues are enabled, ZITADEL discovery, and the introspection application key. Liveness intentionally verifies only the API process.

Open Swagger at http://localhost:3000/docs. Protected calls need a ZITADEL access token in Swagger's Bearer authorization field. There is no /auth/login, /auth/register, or backend login UI.

6. Create usable local identities

For customer registration, a frontend OIDC client should:

  1. read /api/v1/auth/config;
  2. run Authorization Code with PKCE against the returned issuer/client ID;
  3. add the returned customerOrganizationScope for customer login/registration;
  4. use prompt=create for the sign-up entry point;
  5. send the access token—not the ID token—to the API.

Terraform configures the customer organization as the default and attaches fail-closed internal/external post-creation actions that assign the customer-safe capability bundle.

To test platform operations without a frontend, use ZITADEL Console to create a human user under marketplace-platform, then create a marketplace project role assignment for the required capabilities. Persona bundles are arrays in config/zitadel-capabilities.json; the role group labels make batch selection easier in Console. ZITADEL instance/Console administrator roles do not automatically grant marketplace permissions.

The first valid API request JIT-creates a minimal local User keyed by ZITADEL sub. Email/profile changes remain in ZITADEL; addresses and marketplace preferences remain local.

7. Representative flow

With suitable customer, vendor, moderator, and platform-admin tokens:

  1. A customer submits a vendor application and verification metadata.
  2. A platform administrator approves it. The vendor remains APPROVED while an IAM job creates the ZITADEL vendor organization/project grant/owner assignment.
  3. Successful IAM provisioning moves the vendor to ACTIVE.
  4. The owner creates a product, variants, media references, and inventory and submits it.
  5. A platform moderator approves/publishes the product.
  6. A customer adds products to a cart and checks out with an Idempotency-Key.
  7. The local mock capture endpoint creates a signed webhook; authoritative handling commits stock and creates parent/vendor orders.
  8. Each vendor processes only its scoped order, creates a shipment, and advances tracking in development.
  9. Delivery records commission/vendor earnings and allows settlement eligibility and verified review.

Staff is assigned with POST /api/v1/vendors/{vendorId}/staff using a ZITADEL user ID and revoked with DELETE /api/v1/vendors/{vendorId}/staff/{zitadelUserId}. Durable operation status/retry is under /api/v1/admin/iam/provisioning.

8. Fully containerized API

Keep the ZITADEL stack running, then from the repository root:

docker compose --profile application up --build -d
docker compose --profile application run --rm migrate pnpm seed
docker compose ps

The API container maps auth.localhost to the Docker host gateway, so both browser and container use the exact issuer http://auth.localhost:8080. Generated key files are mounted read-only from ZITADEL_KEYS_DIR (default ./secrets/zitadel).

Inspect logs:

docker compose logs -f api
docker compose -f infra/zitadel/compose.yml --env-file infra/zitadel/.env logs -f zitadel-api zitadel-login

9. Existing installation migration

Back up and restore-test both databases. After applying the additive ZITADEL migration, run the read-only legacy IAM report:

corepack pnpm zitadel:migration:export

Resolve unknown capability keys before writing sensitive artifacts:

corepack pnpm zitadel:migration:export -- --output ./secrets/zitadel/migration

Follow the reconciliation/cutover procedure in docs/zitadel.md. The old physical credential/token/session/RBAC/vendor-membership data is deliberately retained for rollback. Dropping it is a separate, explicitly approved post-soak migration.

10. Verification commands

corepack pnpm format:check
corepack pnpm lint
corepack pnpm typecheck
corepack pnpm prisma:validate
corepack pnpm test
corepack pnpm build

The database-backed suite is destructive to its disposable fixtures (it creates orders and consumes inventory), so use only a development/test database:

$env:RUN_DATABASE_E2E='true'
corepack pnpm test:e2e

Unit/e2e tests replace network introspection with deterministic fakes. A real-ZITADEL Hosted Login/provisioning integration test remains a pre-launch environment check.

11. Operations and shutdown

Show state and logs:

docker compose ps --all
docker compose logs -f postgres redis
docker compose -f infra/zitadel/compose.yml --env-file infra/zitadel/.env ps

Stop containers while retaining volumes:

docker compose --profile application down
docker compose -f infra/zitadel/compose.yml --env-file infra/zitadel/.env down

Do not add -v unless permanent deletion of the explicitly inspected local database volumes is intended.

12. Production minimum

  • Use a real FQDN, trusted TLS, correct external host/port/secure settings, and HTTP/2-capable proxying.
  • Use non-default credentials, encrypted Terraform remote state, a secret manager, restricted runtime machine roles, and key-expiry/rotation procedures.
  • Configure/test SMTP, MFA/recovery policy, abuse controls, privacy/retention, and emergency account/session revocation.
  • Back up marketplace and ZITADEL PostgreSQL independently; custody and restore-test the immutable ZITADEL master key.
  • Rehearse ZITADEL upgrades in staging with the next pinned version and a database snapshot.
  • Monitor certificate/key expiry, discovery/introspection latency and errors, failed IAM operations, ZITADEL/PostgreSQL capacity, queue failures, and security audit events.
  • Treat Compose as single-host/semi-production. Select an orchestrated highly available ZITADEL topology when availability requirements demand it.

13. Troubleshooting

pnpm is unavailable

Use corepack pnpm ... or run corepack enable; the repository pins its package manager version.

auth.localhost does not resolve

It should resolve to loopback on modern systems. Add a local hosts entry for development only if your resolver does not implement .localhost. Do not change the API to an internal issuer: browser and API must observe the exact same public issuer.

Readiness reports ZITADEL unavailable

Verify the issuer's /.well-known/openid-configuration, exact scheme/host/port, ZITADEL container health, API key path/mount, and that the key's clientId matches ZITADEL_API_CLIENT_ID.

Token returns 401

Use an access token, request the project audience/roles scopes from /auth/config, and verify issuer, project ID, expiry, and role-claim name. An inactive or malformed token is always rejected.

Token returns 403

Inspect the role assignment organization. Platform capabilities are accepted only under the platform organization; vendor capabilities must be under the resource vendor's exact organization. A role with the correct name under the wrong organization is intentionally denied.

Vendor remains approved but inactive

Inspect /api/v1/admin/iam/provisioning and logs. Correct ZITADEL credentials/permissions/network/configuration, then invoke the explicit retry endpoint. Do not manually set the vendor active without the organization/grant/owner assignment.

Terraform cannot authenticate

Confirm infra/zitadel/bootstrap/admin-machine-key.json was created on first ZITADEL initialization and bootstrap_key_file points to it. Do not regenerate first-instance state by deleting a populated ZITADEL database.

ZITADEL reports “Instance not found”

ZITADEL_DOMAIN, ZITADEL_EXTERNALPORT, and ZITADEL_EXTERNALSECURE must describe the public URL exactly. Preserve the original host through the reverse proxy.