Authentication
API keys explained properly: the key types and their prefixes, project-scoped keys, scopes, restrictions, expiry and rotation, connected apps, and end-user app auth
Overview
Every request to api.scalix.world carries a key in the Authorization header:
curl -H "Authorization: Bearer $SCALIX_API_KEY" \
https://api.scalix.world/v1/functionsKeys are created in the console at Org → API Keys. The full key is shown once, at creation. After that the console only shows its prefix, so store it somewhere real when you create it.
Key types and prefixes
The prefix on a key tells you what it is. There are five:
| Console name | Prefix | Made for | Scopes |
|---|---|---|---|
| Secret Key | scalix_sk_ | Server-side apps and SDKs | The scopes you select |
| Service Account | scalix_sa_ | CI/CD pipelines | The scopes you select |
| Full Access Key | scalix_at_ | Getting started, trusted automation | Every data-plane scope, fixed |
| Personal Access Token | scalix_pat_ | The CLI and personal tooling | The scopes you select |
| Publishable Key | scalix_pk_ | Browser bundles (end-user auth bootstrap) | None, by design |
Two of these behave differently from the rest, and it is worth knowing why:
- A Full Access Key (
scalix_at_) always carries every data-plane scope. The server enforces this at creation, so the type label always matches what the key can really do. It cannot carry platform scopes (admin:fulland similar), and you cannot trim it down. If you want fewer permissions, use a Secret Key. - A Publishable Key (
scalix_pk_) carries no scopes at all and never can. It exists to identify your project to the public/v1/auth/*routes from client-side code. On any other route the gateway rejects it. Safe to ship in a browser bundle; useless to an attacker who lifts it.
Project-scoped keys
A key is either org-wide or pinned to a single project, chosen at creation. Pin it.
Most of the platform acts within a project: databases, functions, storage, Run services, Computers. A project-scoped key matches that shape, and it keeps the blast radius of a leak to one project instead of your whole organization. A client's production app, for example, should run on its own project with its own key, not share one with everything else you operate.
Two rules that follow from how permissions work:
- Only Owners and Admins can create org-wide keys. Members with project restrictions must pick a project.
- Key management is for humans. Creating, rotating, and revoking keys requires a signed-in user session; an API key cannot mint, rotate, or revoke keys — not even keys weaker than itself. (A key can also never mint a key more powerful than its creator: the new key's scopes must be a subset of what the caller holds.)
Org-wide keys and X-Scalix-Project
Several surfaces refuse an org-wide key outright unless the request names a project: Storage, the container registry, databases, Functions, Shield, Prime analytics, and MCP. The error reads this operation requires a project-scoped API key (or names the service).
An org-wide key names the project per request:
curl -H "Authorization: Bearer $SCALIX_API_KEY" \
-H "X-Scalix-Project: <project-uuid>" \
https://api.scalix.world/v1/storage/bucketsThe CLI --project flag and the SDK project option send the same header, and IAM only honors it when the project belongs to the key's org. A project-bound key needs none of this — it is locked to its project.
Scopes
There are 64 named scopes. They follow a service:action pattern; the ones you will meet first:
| Scope | Grants |
|---|---|
database:read | SELECT/SHOW/EXPLAIN only — true read-only SQL |
database:query | Run SQL against your databases (includes writes; legacy behavior) |
database:branch | Create and manage database branches |
storage:read / storage:write | Read and write objects |
functions:deploy / functions:invoke / functions:logs | Deploy, call, and inspect functions |
services:deploy / services:read / services:logs | Manage Scalix Run services |
compute:deploy / compute:scale / compute:logs | Container workloads |
ai:infer / ai:models | AI inference and the model catalog |
kv:read / kv:write | Key-value store |
events:publish / events:read | Pub/Sub |
registry:pull / registry:push | Container registry |
search:read | Scalix Search |
metering:read | Your own usage data |
The console's key form shows the complete list with a description for each. Select the scopes the workload needs and no more; a dozen well-chosen scopes covers most production services.
When a request is refused: a 401 means the key itself was not accepted (wrong, revoked, or expired). A 403 means the key is real but lacks a scope the route requires, or it is a publishable key on a scoped route. The gateway's error strings are literal, and each maps to one fix:
| Error | Fix |
|---|---|
this operation requires a project-scoped API key | Bind a project at key creation, or send X-Scalix-Project (above) |
storage API requires a project-scoped API key | Same — Storage always needs the project named |
publishable keys cannot be used for resource operations | scalix_pk_ keys are auth-bootstrap only; use a server-side key for the API |
key is read-only — only SELECT, SHOW, and EXPLAIN are allowed | The key was created with Read-Only enabled; use a non-read-only key for writes |
missing scope: database:read or database:query | Add the database scope the route needs |
key management requires a signed-in user session | Create/rotate/revoke keys from the console while signed in, not with an API key |
Restrictions at creation
Three optional restrictions can be attached to any key:
- Read-only (console toggle): the key can never mutate state. The gateway default-denies POST/PUT/PATCH/DELETE across the whole API — with a small allow-list for endpoints that legitimately read via POST (SQL queries, search, AI inference, DB advice endpoints). On the SQL plane only SELECT/SHOW/EXPLAIN pass, and the Postgres session itself is set read-only as a second layer.
- IP allowlist (API field
ip_allowlist, CIDR entries): requests from any other address are refused, fail-closed. - Require approval (API field
require_approval): high-risk operations from this key pause until a human approves them — the agent cannot approve its own request. Every connected app (below) gets this automatically.
Connected apps (OAuth grants)
Third-party clients — ChatGPT, Claude, an IDE — connect through OAuth, not hand-minted keys. When a user approves the consent screen, Scalix mints an scalix_oat_ grant token:
- It counts against the org's connected-apps quota, not the raw API-key quota, and can be revoked by app name.
- It carries exactly the scopes shown on the consent screen — nothing broader.
- It expires on a fixed TTL and always requires human approval for high-risk operations.
Connected apps are managed in the console under Org → API Keys → Approvals, alongside approval requests.
Expiry and rotation
Keys default to a 90-day expiry unless you choose a different period or No expiration at creation. The console's API Keys page shows each key's last-used time. A key showing Never or a stale date is your first candidate for revocation.
Rotation mints a replacement and keeps the old secret working for a grace window (24 hours by default) so deployed services can swap without downtime. Revocation is immediate and permanent. If a key has leaked, revoke it, do not rotate it: rotation's grace window keeps the leaked secret alive.
CLI login
scalix-cloud loginThe CLI prompts for a key, validates it against the API, and stores it in the OS keychain on macOS and Windows, or an owner-only (0600) ~/.scalix/token file on Linux. It is never written to ~/.scalix/config.toml.
Any key type that validates works here: a Personal Access Token is the intended fit, but a Secret Key or Full Access Key logs in the same way. You can also pass the key directly: scalix-cloud login scalix_pat_your_key_here.
Environment Variables
The CLI reads these environment variables (the SDKs take the API key as an explicit constructor argument):
| Variable | Description |
|---|---|
SCALIX_TOKEN | API key the CLI uses for authentication |
SCALIX_API_URL | API endpoint (default: https://api.scalix.world) |
App authentication (your end users)
Everything above is about your access to the platform. For signing up and logging in your application's users, use the auth SDK against your project:
import { ScalixAuthClient } from "@scalix-world/auth";
const auth = new ScalixAuthClient({
url: "https://api.scalix.world",
});
// Sign up a new user
const { user, session } = await auth.signUp("user@example.com", "secure-password");
// Sign in
const result = await auth.signInWithPassword("user@example.com", "secure-password");
// MFA enrollment
const factor = await auth.enrollMfa("totp", "My Phone");The publishable key (scalix_pk_) is what identifies your project to these routes from the browser. End-user sessions are JWTs scoped to your application; they cannot touch the platform API.