Bootstrap dependency map

The diagram below captures what must exist before each step can succeed. Work top-to-bottom; each node is blocked until its predecessors are complete.

flowchart TD
    SA["prodLab SA has read access to the Lab vault (1Password service account settings)"]

    HOST["provision-host.sh writes prodLab SA token → ~/.op_env"]
    SA --> HOST

    DEPLOY["provision-service.sh docker compose up"]
    HOST --> DEPLOY

    RACE{"postgres healthy before n8n connects?"}
    DEPLOY --> RACE

    WIPE["wipe /opt/n8n/postgres/* restart stack"]
    RACE -- "race condition (ECONNREFUSED during migrations)" --> WIPE
    WIPE --> MIGRATE

    MIGRATE["DB migrations run to completion (all tables created)"]
    RACE -- "healthy" --> MIGRATE

    UI["n8n UI accessible at :5678"]
    MIGRATE --> UI

    USER["Create initial user (first-run wizard)"]
    UI --> USER

    LICENSE["Activate license (Settings → License)"]
    USER --> LICENSE

    APIKEY["Create API key (Settings → API → Create API key)"]
    USER --> APIKEY

    STORE["Store key in 1Password service.n8n → env.N8N_API_KEY (prodLab vault)"]
    APIKEY --> STORE

    REPROVISION["Re-run provision-service.sh (regenerates .env, runs post-deploy hook)"]
    STORE --> REPROVISION
    SA --> REPROVISION

    CREDS["Credentials imported from Lab vault into n8n"]
    REPROVISION --> CREDS

    READY["N8N ready for business"]
    LICENSE --> READY
    CREDS --> READY

Notes

Race condition (the postgres blip): On a fresh host, docker compose up respects the postgres healthcheck but a brief connection loss mid-migration can leave the schema partially initialized. Symptoms: n8n starts but returns “Error connecting to n8n” in the browser; logs show relation "public.execution_entity" does not exist. Fix: stop the stack, wipe /opt/n8n/postgres/*, restart.

prodLab SA vault access: The prodLab 1Password service account must have read access to the Lab vault (not just prodLab). Without it, import-credentials.sh silently finds no credentials — the op error was previously suppressed by 2>/dev/null and is now visible in the provision log.

API key lifecycle: The API key is stored in the postgres database. If postgres is wiped, the key is gone and a new one must be created, stored in 1Password, and provision-service.sh re-run.