Authentication
How callers authenticate to the Lattix Platform API — bearer token, SDK client credentials, tenant binding, and scopes.
The Platform API supports two caller authentication patterns. Pick the one that matches how your environment wants to manage credentials.
Auth modes
The active auth mode is a deployment decision, surfaced to callers in the /v1/sdk/capabilities and /v1/sdk/bootstrap responses as one of:
auth_mode | Meaning |
|---|---|
bearer_token | Every request carries an Authorization: Bearer ... token. |
trusted_headers | The ingress layer already authenticated the caller and forwards identity in trusted headers. |
bearer_token_or_trusted_headers | Either pattern is accepted; a bearer token takes precedence when both are present. |
Callers can always rely on capabilities or bootstrap to report which mode they are operating under.
Bearer token
The canonical public pattern. The caller presents a JWT in the standard header:
Authorization: Bearer <token>Tokens are bound to a tenant via a claim the platform resolves server-side; the caller does not need to — and should not — re-assert the tenant from an inbound request header in this mode. The platform returns the resolved identity on every response as caller.tenant_id, caller.principal_id, caller.subject, and caller.scopes.
When a tenant header is forwarded alongside a bearer token, it must match the tenant claim in the token. Mismatches are rejected.
SDK usage:
- Rust —
Client::builder(base_url).with_bearer_token("...") - Go —
sdk.Options{ BearerToken: "...", ... } - Python —
LattixClient(bearer_token="...", ...)
SDK client credentials
For callers that cannot carry a pre-issued token — sidecars, pipelines, batch jobs — the API exposes a session-exchange endpoint:
POST /v1/sdk/session
Content-Type: application/json
{
"tenant_id": "<tenant>",
"client_id": "<client>",
"client_secret": "<secret>",
"requested_scopes": ["..."]
}Successful exchanges return a short-lived token:
{
"access_token": "...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "...",
"tenant_id": "...",
"client_id": "...",
"subject": "..."
}The caller attaches access_token to subsequent /v1/sdk/* calls as a bearer token. Each SDK performs this exchange and caches the result automatically. The session-exchange route is the only route subject to a published rate limit and returns a Retry-After header on 429.
SDK usage:
- Rust —
.with_client_id("...").with_client_secret("...").with_tenant_id("...") - Go —
sdk.Options{ ClientID, ClientSecret, TenantID, RequestedScopes } - Python —
LattixClient(client_id=..., client_secret=..., tenant_id=..., ...)
Credentials are never sent on any route other than /v1/sdk/session. The SDK implementations keep them out of subsequent requests.
Trusted-headers mode
In deployments that terminate authentication at the ingress layer, the API accepts caller identity through a dedicated header set by the trusted ingress:
x-lattix-tenant-id— tenant identifier for this caller.x-lattix-user-id— principal / user identifier (sometimes required, depending on configuration).
This pattern is intended only for environments where the ingress layer strips any inbound user-supplied versions of these headers before setting trusted values. Callers deployed behind such an ingress do not set these headers themselves — they are added for you. If you are building a client that needs to run in both bearer-token and trusted-headers deployments, defer to the SDK: each SDK supports both patterns transparently.
Scopes
The API uses scope-based authorization. Each /v1/sdk/* route publishes its required scopes through the capabilities response:
{
"auth_mode": "bearer_token",
"default_required_scopes": ["lattix.sdk.read"],
"routes": [
{
"route": "/v1/sdk/protection-plan",
"domain": "protection",
"configured": true,
"required_scopes": ["lattix.sdk.protection.plan"]
}
]
}The exact set of scopes is deployment-dependent. Callers should read capabilities at startup, cache the result, and present the scopes their workload needs.
403 responses indicate the caller is authenticated but missing one or more required scopes for the specific route.
Caller identity on every response
Every /v1/sdk/* response includes the resolved caller under caller:
{
"service": "sdk-control-plane",
"status": "ok",
"caller": {
"tenant_id": "...",
"principal_id": "...",
"subject": "...",
"auth_source": "bearer_token",
"scopes": ["..."]
}
}auth_source is one of bearer_token, sdk_client_credentials, or trusted_headers, and reflects how this specific request was authenticated.
Credential hygiene
- Treat bearer tokens as short-lived and never log them.
- Treat SDK
client_secretvalues as long-lived secrets; rotate per your organization's policy. - Never hard-code credentials in source. Load them from environment variables or a secrets manager.
- The SDKs cache session tokens in memory only. There is no on-disk credential store.
Relationship to other pages
Platform API
The Lattix Platform API — a metadata-only HTTP control plane for embedded enforcement. Eight /v1/sdk/* routes, JSON request/response, bearer-token auth, signed tenant and principal identity.
Endpoints
Reference for the eight /v1/sdk/* routes — discovery, session exchange, planning, registration, and evidence ingestion. Request and response shapes for each.