Skip to content

directus

apps/directus is a self-hosted Directus instance providing content management for tenant sites. It stores blog posts, landing page content, and media assets. Staff and business owners access it via WorkOS OIDC — their WorkOS organization maps to a Directus role on login.

Attribute Value
Type Headless CMS (Node.js)
Port 8055
Database PostgreSQL (Neon, separate project)
Storage Cloudflare R2 (MinIO locally)
Auth WorkOS OIDC (custom extension)
API REST /api/items/* + GraphQL /graphql
Admin UI /admin

Directus does not natively understand WorkOS orgs. A custom extension (workos-oidc-mapper) handles the mapping:

sequenceDiagram
    participant U as User
    participant D as Directus
    participant WOS as WorkOS OIDC
    participant Ext as workos-oidc-mapper extension

    U->>D: log in via SSO
    D->>WOS: OIDC auth request
    WOS-->>D: id_token (includes org_id)
    D->>Ext: on login hook
    Ext->>Ext: map org_id → Directus role
    Ext->>D: set user role
    D-->>U: authenticated with org-scoped role

This means each WorkOS org gets a distinct Directus role, scoping what content they can read/write.

Extension What it does
workos-oidc-mapper Maps WorkOS org → Directus role on every OIDC login
preview-url-syncer On boot, rewrites the host of each collection’s preview_url in directus_collections to match SITE_PREVIEW_BASE_URL, so editor “Preview” links point at the correct site deploy per environment

Directus manages its own schema via snapshots. The snapshot file is committed to the repo and applied on deploy:

Terminal window
# Apply schema from snapshot
npx directus schema apply ./snapshots/latest.yaml
# Generate a new snapshot after schema changes
npx directus schema snapshot ./snapshots/latest.yaml

Sequential migration files in apps/directus/migrations/. Key migrations:

Migration What it sets up
20260523A Multi-tenancy roles and permissions structure
20260524A Bootstrap admin user to WorkOS
20260524B Ensure admin user exists post-bootstrap
20260525A Preview URL site ID wiring
graph LR
    subgraph "Inbound"
        Sites[site-* / directus-sdk]
        SA_WH[scheduler-api\nconvergence webhooks]
        Editors[Content editors\nbrowser /admin]
    end

    D[directus]

    subgraph "Outbound"
        ND[(Neon\nDirectus DB)]
        R2[(Cloudflare R2\nmedia assets)]
        WOS[WorkOS OIDC]
    end

    Sites --> D
    SA_WH --> D
    Editors --> D

    D --> ND
    D --> R2
    D --> WOS

When Directus content changes, a webhook nudges scheduler-api to start a convergence workflow. This keeps Directus data in sync with the Vendure catalog and the scheduler’s mirror caches. See Convergence flow.

The convergence backfill jobs write the mirror caches; the daily scanner reads them. These are layered, not redundant — do not retire the backfills.