Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Multi-tenancy and auth

ScaiFlow is multi-tenant from the ground up. Every flow, every catalog package, every credential, every event is scoped to a tenant. Auth flows through ScaiKey.

Auth flows#

Canvas → backend (OIDC + PKCE)#

sequenceDiagram participant User participant Canvas participant ScaiKey participant API as ScaiFlow API User->>Canvas: clicks Sign in Canvas->>Canvas: generates PKCE verifier + challenge Canvas->>ScaiKey: redirect to /authorize?challenge=... ScaiKey->>User: authenticate ScaiKey-->>Canvas: redirect back with code Canvas->>API: POST /v1/auth/exchange { code, verifier } API->>ScaiKey: POST /oauth/token { code, verifier, client_secret } ScaiKey-->>API: { access_token, refresh_token } API-->>Canvas: { access_token, refresh_token }

The single ScaiKey WEB client is shared by canvas (PKCE, browser-side) and backend (BFF, server-side). The client_secret never reaches the browser. Audience for issued tokens is client_id (ScaiKey's default), so verification doesn't need any special audience config.

When ScaiKey isn't configured, the canvas falls back to a dev-token shared-secret bearer that only enables POST /v1/dev/deploy (deprecated legacy path).

Backend → ScaiGrid (token forwarding, with API-key fallback)#

When the user calls POST /v1/flows/{id}/deploy, the backend forwards their ScaiKey access token to ScaiGrid directly — ScaiGrid accepts the audience-client_id JWT natively.

For non-user contexts (the background ScaiKey sync loop, scheduled jobs, dev environments without an authenticated user), the backend falls back to a per-tenant API key stored in ScaiVault under tenants/{tenant_id}/scaigrid_api_key. Tenant admins set this via POST /v1/tenants/me/scaigrid_credentials.

You can pin the choice via SCAIFLOW_DEPLOY_AUTH=user_jwt|api_key for debugging.

ScaiKey → backend (webhooks)#

When users join/leave ScaiKey groups, ScaiKey fires signed webhooks at POST /v1/webhooks/scaikey. The backend verifies the HMAC-SHA256 signature against SCAIFLOW_SCAIKEY_WEBHOOK_SECRET, then updates its local user/group mirror.

The user/group mirror#

The backend maintains a local copy of every user and group in the tenant. This is what powers per-flow ACLs, super_admin role checks, and the tenant admin's "all users / all flows" views.

Sync runs on:

  • Webhook delivery — incremental, near-real-time on join/leave events.
  • Hourly fallbackSCAIFLOW_SYNC_INTERVAL_MINUTES (default 60) runs a full sweep.
  • Manual trigger — super admins can hit POST /v1/admin/sync.
  • CLI bootstrapscaiflow scaikey-register provisions the ScaiKey application + key, then triggers an initial sync.

The sync prefers applications.get_effective_users (platform-tier, returns every user with access regardless of group); falls back to a member-walk over groups.list_members for tenant-tier credentials that can't reach the platform API. The fallback is slower (O(N_groups × N_members) vs O(N_users)) but doesn't require platform-tier scope.

Permissions and roles#

ScaiFlow uses two canonical ScaiKey permissions:

  • scaicore:view — read-only access. Required for: listing flows, reading a flow, live preview, validation, browsing catalog, listing ScaiQueue scopes/queues/ScaiBunker bunkers, watching ScaiCore events.
  • scaicore:manage — write access. Required for: create/update/delete flows, deploy, run tests, publish to catalog, resolve checkpoints, invoke a deployed Core.

There are no scaiflow:* permission variants — ScaiFlow piggy-backs on the ScaiCore permission pair so a single ScaiKey role config covers both.

Admin roles (ScaiFlow-local)#

Two roles maintained in ScaiFlow's DB (not ScaiKey):

  • tenant_admin — can see every flow in their tenant, manage tenant ScaiGrid credentials, grant tenant_admin to others in their tenant.
  • super_admin — platform-wide; can grant either role to anyone, sees all tenants.

Super_admin can be granted in three ways:

  1. ManualPOST /v1/admin/users/{user_id}/role with role=super_admin, source=manual. Survives reconciliation.
  2. Group-derived — set SCAIFLOW_SUPER_ADMIN_GROUPS=group_id_1,group_name_2. Members of any listed group are auto-elevated on sync (source=group). Removed from the group → auto-demoted.
  3. First-user bootstrap — initial CLI sync; the first user with admin scopes can self-promote.

Per-flow ACLs#

Each flow has an ACL list:

jsonc
[
  { "principal_type": "user", "principal_id": "usr_alice", "level": "edit" },
  { "principal_type": "group", "principal_id": "grp_devs", "level": "view" }
]

Levels are a strict hierarchy: view < edit < deploy < admin. Higher levels imply lower ones.

  • Owner: implicit admin — can't be removed.
  • Super admin: implicit admin on every flow.
  • Tenant admin: implicit admin on every flow in their tenant.
  • Catalog publish action: requires deploy ACL or higher.

Edit ACLs via POST /v1/flows/{id}/acls and DELETE /v1/flows/{id}/acls/{acl_id}.

Catalog visibility#

CatalogPackage.visibility is one of:

  • tenant — visible to anyone in the tenant.
  • groups — restricted to specific group IDs (visibility_group_ids).

The author of a package implicitly sees it regardless of visibility. Super admins see all packages across all tenants.

Tenant ScaiGrid credentials#

A tenant admin can configure their ScaiGrid API key via:

bash
1
2
3
4
5
6
7
curl -X POST "https://scaiflow.example/api/v1/tenants/me/scaigrid_credentials" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "api_key": "sgk_...",
    "base_url": "https://scaigrid.scailabs.ai"
  }'

The key + base URL are written to ScaiVault at tenants/{tenant_id}/scaigrid_api_key and .../scaigrid_base_url. Background jobs and dev-token deploys read from there.

Status endpoint: GET /v1/tenants/me/scaigrid_credentials returns {configured, base_url, masked_key} (the key is masked as sgk_…last4).

Updated 2026-05-18 16:05:18 View source (.md) rev 3