Skip to content

First start

This page takes a host that already has Docker and an NVIDIA runtime all the way to a deployment that answers a real recall. It helps to know which services exist from Deployment topology, and that the machine is big enough per Hardware and cost.

The run is two tracks that meet at the health check. The default profile gives you a private engine you can already probe. The Logto and public track adds the internet-facing surface on top.

fill .env ─▶ up -d default ─┬──────────────────────────▶ admin health
│ ▲
└▶ Logto apps ─▶ auth apply ─▶ up -d public

Compose reads one file, the project .env at the package root, and every command names it explicitly. Follow the steps in order.

  1. Fill the environment. Start from the committed example and generate a fresh secret for each blank.

    Terminal window
    cp src/deploy/.env.example .env
    openssl rand -base64 32

    Six values are hard requirements. Compose interpolates them with :?, so it refuses to start at all while any of them is blank.

    Terminal window
    AIZK_ADMIN_PASSWORD=
    AIZK_APP_PASSWORD=
    AIZK_LOGTO_DB_PASSWORD=
    AIZK_OBJECT_STORE_ACCESS_KEY=
    AIZK_OBJECT_STORE_SECRET_KEY=
    AIZK_DOCLING_API_KEY=
  2. Start the local stack, the default profile, which is the engine with no public surface.

    Terminal window
    docker compose --env-file .env -f src/deploy/docker-compose.yml up -d

    The first start is slow, because the three vLLM lanes come up one after another and each one downloads and loads weights. setup runs the migrations once and exits. Nothing is exposed to the network yet.

  3. Create the four Logto applications. The public profile needs Logto to already know about aizk, so make these in the Logto console and paste their credentials into .env.

    Application Kind Setting pair
    Management API client machine to machine, role carrying the Management API all permission AIZK_LOGTO_CLIENT_ID, AIZK_LOGTO_CLIENT_SECRET
    MCP OAuth upstream traditional web, redirect is the aizk server callback AIZK_OAUTH_CLIENT_ID, AIZK_OAUTH_CLIENT_SECRET
    Browser app traditional web, redirect is exactly ${AIZK_WEB_PUBLIC_URL}/auth/sign-in-callback AIZK_WEB_CLIENT_ID, AIZK_WEB_CLIENT_SECRET
    AIZK Admin traditional web, redirect is exactly ${AIZK_ADMIN_PUBLIC_URL}/oauth2/callback AIZK_ADMIN_CLIENT_ID, AIZK_ADMIN_CLIENT_SECRET

    Set the AIZK Admin application’s Mandatory MFA to passkey. That setting is per application, so it asks operators for a second factor without changing how anyone signs in to the memory itself. Add AIZK_ADMIN_COOKIE_SECRET as its own fresh 32 bytes, since oauth2-proxy signs the console session cookie with it.

    Then set the URLs. AIZK_LOGTO_URL is where Logto answers, and AIZK_MCP_PUBLIC_URL, AIZK_WEB_PUBLIC_URL and AIZK_API_PUBLIC_URL are the one public origin the tunnel serves. All of them must be HTTPS. Add AIZK_WEB_SESSION_SECRET of at least 32 bytes, generated separately, since Settings.independent_session_secret rejects it when it matches the web, Management API or OAuth client secret. Finally add AIZK_TUNNEL_TOKEN from Cloudflare. Caddy keeps the browser and the API same-origin by routing, so neither needs its own published port.

  4. Reconcile the authorization policy. aizk owns a small committed slice of the Logto policy.

    Terminal window
    aizk admin auth audit
    aizk admin auth apply

    audit reports drift and exits nonzero when the live tenant does not match. apply reconciles it and leaves unrelated roles and permissions alone. Both are idempotent, and The Logto boundary explains what aizk does and does not own here.

    apply creates two global roles, aizk-user for everyone and aizk-admin for operators. Assign aizk-admin to yourself, then confirm it with aizk admin auth roles, because that role is the only thing the operator console admits.

  5. Move the Logto console to its own hostname, on its own, before anything else changes.

    Logto’s console must own an origin, so it cannot live under a path on the operator console host. Add a Cloudflare public hostname console.phvv.me pointing at logto:3002, set AIZK_LOGTO_ADMIN_ENDPOINT=https://console.phvv.me, and recreate the logto service. Logto bakes that endpoint into the console’s own redirect URIs, so a half-finished move locks the console out and the only fast repair is putting the old line back.

  6. Add the two remaining Cloudflare hostnames, aizk.phvv.me to web:8081 and admin.phvv.me to web:8082. Every public name is a first-level subdomain, which is what keeps them inside the free Universal SSL certificate. Deployment topology has the full routing table and explains the optional Cloudflare Access layer in front of the console.

  7. Start the public profile, which adds everything internet-facing.

    Terminal window
    docker compose --profile public --env-file .env -f src/deploy/docker-compose.yml up -d

    The ordering fails closed. cloudflared must report ready before logto-setup runs, because the tunnel is what publishes Logto’s canonical issuer. public-check then runs admin auth check-public with AIZK_REQUIRE_AUTH=1, which builds Settings and so fails when the Logto URL, the public URLs or either OAuth client is missing or half filled. web-check does the same for the browser settings, and the MCP server waits until both gates pass.

  8. Check it works with the health command, run inside the private worker.

    Terminal window
    docker compose --env-file .env -f src/deploy/docker-compose.yml exec -T worker aizk admin health

Run the health command in the private worker, never in server. The public process has no migration-owner credential on purpose, so a compromised request path cannot turn this diagnostic into an RLS bypass.

A healthy deployment reports an up-to-date migration, an empty rls_violations list, Logto identity mode, all four model endpoints reachable with matching served aliases, no retained queue failures, and a recall block with candidates and no error. That last field is the one that matters, because it is a real retrieval through the real models rather than a ping.