The authorization service answers may this user operate on this cloud resource? — a question about a subscription, a project, or an account, asked before Kumoss runs anything against a live environment.
Contract: contracts/openapi/authz.v1.yaml. Default endpoint:
http://authz:8083. Bearer token: the value of the variable named by
services.authz.token_env, KUMOSS_AUTHZ_TOKEN by default. The
service is optional and ships disabled (services.authz.enabled:
false).
|
Kumoss’s own roles do not live here. The operation roles ( |
What the core actually calls
The contract describes seven paths. The core calls one of them.
POST /api/v1/auth/authorize — and nothing else in the core — forwards
to POST /v1/check. The other five /v1 paths exist for callers of
this service directly, or for an organization building on the bundled
implementation, to administer that service’s own user and role store.
The generated client contains methods for them; nothing in the core
invokes them.
When services.authz.enabled is false, the shipped default, the
core does not call out at all and answers authorized. When it is
enabled, the core never fails open:
| Situation | What the core answers |
|---|---|
Sidecar unreachable, or the call times out |
|
Sidecar returns a |
|
Sidecar returns |
The decision it computed, passed through |
A 422 from the sidecar is therefore visible at the browser as a
502, which matters for one specific shape of request — see
The bundled implementation.
Endpoints
| Endpoint | What it does |
|---|---|
|
Decides whether a user may operate on a named cloud resource. The only endpoint Kumoss uses. |
|
Returns the calling user’s record and roles, creating an empty record if none exists. |
|
Lists the role names the service knows. |
|
Lists all known users. Caller must hold the |
|
Assigns a role. |
|
Revokes a role. |
|
Liveness probe, answers |
Asserted identity
The administrative endpoints take identity from headers, not from a token this service validates. The two are declared unevenly:
-
X-User-Id— caller-asserted user identifier. Optional. Declared on four operations:GET /v1/users/me,GET /v1/users,POST /v1/users/{user_id}/roles, andDELETE /v1/users/{user_id}/roles/{role}. The contract advertises no maximum length, deliberately, so that a boundary caller does not get a hard4xx; an implementation may clamp it instead. -
X-User-Email— caller-asserted email. Optional. Declared onGET /v1/users/mealone.
GET /v1/roles declares no parameters at all and therefore takes
neither header.
OIDC token validation lives entirely in the core, so this service makes no assumption about the upstream identity provider and does not verify the assertion. That is only safe because the service is reachable from the core alone. The core sends neither header on any live path — they matter only if you call these endpoints yourself.
POST /v1/check
{
"user_id": "someone@example.com",
"cloud": "azure",
"project": "my-project",
"environment": "dev"
}
cloud and project are required; user_id and environment are
nullable. additionalProperties is false.
| Field | Limits | Meaning |
|---|---|---|
|
1-1024 chars, nullable |
The user to check. Null for an anonymous flow. The core sends the caller’s email, falling back to the token subject when there is no email. |
|
1-32 chars, required |
|
|
1-256 chars, required |
The resource being checked. |
|
1-32 chars, nullable |
Deployment dimension — |
The response:
{
"authorized": true,
"portal_url": null,
"reason": "Permissive OSS reference impl: all checks return true. Replace this implementation with real authorization logic (e.g., cloud SPN access check) for production."
}
That is the bundled implementation’s actual answer. A policy-backed
implementation puts its own explanation in reason and a console deep
link in portal_url; the bundled one always leaves portal_url null.
| Field | Meaning |
|---|---|
|
Required boolean. The decision. |
|
Nullable, ≤2048 chars. A link to the cloud console for the resource.
Null when the implementation has no portal-URL convention. The core
passes it through as |
|
Nullable, ≤1024 chars. Human-readable explanation, useful in the UI and in debugging. |
A denial is still 200: the decision was computed, and it was "no".
The error statuses are for cases where no decision could be reached.
| Status | Meaning |
|---|---|
|
A decision was computed, granted or denied. |
|
The body could not be parsed at the transport layer. |
|
Missing or invalid bearer token. Carries a |
|
Well-formed JSON that failed schema validation. |
|
The downstream identity or cloud backend returned an error. |
The bundled implementation
services/authz/ is permissive by default: POST /v1/check returns
authorized: true unconditionally. It exists so a fresh deployment
works end to end, and it is the reason the service ships disabled — an
enabled permissive authorizer is worse than none, because it looks
like a control.
|
The contract’s description of this implementation is stale. Both the
document summary and the |
KUMOSS_AUTHZ_PERMISSIVE is the only knob Kumoss’s own flow
exercises. Setting it to false flips the default to authorized:
false, which makes a misconfiguration visible instead of silently
allowing everything. A production deployment replaces the body of
/v1/check with real logic — directory groups, a CMDB-driven policy,
a cloud IAM query — rather than tuning this flag.
The other endpoints are backed by a JSON file at
KUMOSS_AUTHZ_ROLE_STORE, default /data/roles.json, a
container-local path, so role assignments are lost when the container
is recreated. Point it at a mounted path if they need to survive. When
KUMOSS_AUTHZ_ROOT_ADMIN_EMAIL is set, the record with that email is
granted the admin role at startup, and that email is also the
record’s key and id. Again: this store governs only this service’s
own administrative endpoints.
|
A long IaC path produces a |
Configuration
| Variable | Required | Meaning |
|---|---|---|
|
no |
Bearer token clients must present. Blank accepts every request. |
|
no |
Default |
|
no |
Path to the roles JSON file, default |
|
no |
Email granted the |
Conformance
The implementation-agnostic Schemathesis suite at
contracts/conformance/authz/ runs against any implementation of this
contract:
cd contracts/conformance/authz
uv sync
uv run pytest \
--service-url=https://authz.your.example \
--service-token=$YOUR_TOKEN
--service-token is needed only if the implementation enforces
authentication.
The suite checks schema conformance of the responses and that invalid
bodies do not produce undocumented 5xx answers. It cannot check that
the decisions are correct — a permissive implementation that grants
everything passes it. Correctness of the policy is your own test
suite’s job.
Next steps
-
Roles and permissions for the roles that actually gate Kumoss.
-
Core API for the one core route that consults this service.
-
the
servicessection of the configuration reference for enabling the sidecar.