03 / Develop · Shell · API

Shell API ​

Every route is served under /api by an Ignis controller in api/src/controllers/ — the definitive contract. Auth is two layers: a strategy verifies the caller, then an @authorize decorator gates the route by permission.

StrategyVerifiesUsed by
jwtA Shell RS256 tokenSigned-in users and satellites
patA veni_… personal access token (X-Veni-Token)The CLI and scripts

A handful of routes are public (no strategy): sign-in, the OAuth callback, JWKS, and PAT introspection — the things a caller needs before it has a token.

What satellites & the CLI depend on ​

The external contract — the endpoints other systems actually call:

EndpointPurposeAuth
GET /api/.well-known/jwks.jsonPublic keys a satellite verifies Shell tokens with — needs the RSA keypublic
POST /api/auth/exchangeExchange a Shell JWT for a service JWTJWT
POST /api/auth/local-login · GET /api/auth/login · GET /api/auth/callbackPassword login · OAuth start · OAuth callbackpublic
POST /api/auth/resolve-providerEmail → the org's SSO/login URL (per-org routing)public
POST /api/personal-tokens/introspectValidate a PAT → user (used by the Builder to gate the CLI download)public
POST /api/auth/cli/device · POST /api/auth/cli/device/tokenThe CLI device flow — start, then poll for the minted tokenpublic (the device_code is the credential)
GET /api/auth/cli/device/:userCode · …/approve · …/denyThe browser half — what the approval page at <shell UI>/cli reads and postsJWT

The REST surface, by concern ​

Each base path is one controller, grouped by the same clusters as the database. The permission family it enforces is in parentheses.

AreaBase pathWhat it manages
Identity/api/authSign-in, token exchange/refresh, me profile & orgs, switch-org, invites, password reset
Identity/api/personal-tokensPersonal access tokens — create (raw shown once), list, revoke, introspect
Organizations/api/organizationsOrg CRUD, SSO config, SMTP, portal-domain, subscription (ORGANIZATIONS_*)
RBAC/api/usersUser CRUD, per-user role assignment, invitations (USERS_*)
RBAC/api/roles · /api/permissionsRoles, role↔permission grants, the permission list (ROLES_*, PERMISSIONS_*)
App registry/api/admin/apps · /api/admin/orgsRegister / list / update apps, and the per-org catalog (requireAdmin, APPS_CREATE)
Billing/api/subscriptions · /api/plans · /api/invoicesBilling, the plan catalog, invoices
Platform/api/scim/v2 · /api/scim-tokensSCIM v2 provisioning (its own bearer auth) + token management
Platform/api/dashboard · /api/mcp · /api/tenant · /api/configDashboard widgets & AI briefing, the MCP endpoint, host→org resolution, public client config

gRPC — service-to-service ​

Three Connect-over-HTTP services, mounted at the root (not under /api). The rbac and quota services require a service JWT whose aud matches — so only trusted services call them; AuthGateway brokers on the caller's Shell JWT:

ServicePurposeCaller presents
/auth.v1.AuthGateway/*Brokered token exchange between systemsthe caller's Shell JWT
/rbac.v1.RbacService/*Live permission checks against the Shell's RBACservice JWT · aud: shell-rbac
/quota.v1.QuotaService/*Plan quota / usage limitsservice JWT · aud: shell-quota

Health & extending ​

GET /api/health is the liveness endpoint (Ignis's health-check component — what the Docker healthcheck hits). To add an endpoint, add a controller in api/src/controllers/ (or a route to an existing one), gate it with @authorize, and put the logic in a service — the layered architecture has the shape.

See also ​

  • Overview — what the Shell is and how it's built.
  • Database — the data these endpoints operate on.