Authentication
API keys explained properly: the five key types and their prefixes, project-scoped keys, scopes, expiry and rotation, 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.
- A key can never mint a key more powerful than itself. Creating keys is a boundary-checked operation: the new key's scopes must be a subset of what the caller holds, and data-plane keys cannot create keys at all. Key management is for humans (or an owner-authenticated session), not for the keys themselves.
Scopes
There are 64 named scopes. They follow a service:action pattern; the ones you will meet first:
| Scope | Grants |
|---|---|
database:query | Run SQL against your databases |
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. If a data-plane call complains about project context, the key is org-wide and the route wants to know which project you mean; a project-scoped key answers that implicitly.
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.