Skip to content

Authentication & RBAC

agent-skills provides a pluggable authentication and role-based access control (RBAC) system for the HTTP API.

Role hierarchy

Four roles are defined, ordered by ascending privilege:

Role Can access
reader Health, describe, list, governance, discover
executor Reader + execute (sync, stream, async), attach, capabilities
operator Executor + webhooks, runs management
admin Operator + all endpoints (including unknown future routes)

Higher roles inherit all permissions from lower roles.

Enabling RBAC

Set the environment variable AGENT_SKILLS_RBAC=1 before starting the server:

export AGENT_SKILLS_RBAC=1
agent-skills serve --api-key my-key --port 8080

When RBAC is enabled, the configured --api-key is registered as an admin key automatically. Without RBAC enabled, the legacy flat API-key check is used (backward-compatible).

Authentication methods

API key (X-API-Key header)

POST /v1/skills/my-skill/execute
X-API-Key: my-admin-key

Keys are stored as SHA-256 hashes — the raw key is never kept in memory.

Bearer token (JWT HS256)

Set AGENT_SKILLS_JWT_SECRET to enable JWT authentication:

export AGENT_SKILLS_JWT_SECRET=my-256-bit-secret
export AGENT_SKILLS_RBAC=1
agent-skills serve

Tokens must include a sub claim. The role claim maps to the RBAC hierarchy. If no role claim is present, executor is used as default.

POST /v1/skills/my-skill/execute
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Token claims:

Claim Required Description
sub Yes Subject identifier
role No One of: reader, executor, operator, admin
exp No Expiry timestamp (UNIX epoch). Rejected if expired.

Anonymous access

When no API key or JWT secret is configured and RBAC is enabled, anonymous requests are assigned the reader role. Disable anonymous access by setting at least one API key.

Route → role mapping

Method Path pattern Required role
GET /v1/health reader
GET /openapi.json reader
GET /v1/skills/list reader
GET /v1/skills/governance reader
GET /v1/skills/diagnostics reader
GET /v1/skills/{id}/describe reader
POST /v1/skills/discover reader
POST /v1/skills/{id}/execute executor
POST /v1/skills/{id}/execute/stream executor
POST /v1/skills/{id}/execute/async executor
POST /v1/skills/{id}/attach executor
POST /v1/capabilities/{id}/execute executor
GET /v1/runs operator
POST/GET /v1/webhooks operator
DELETE /v1/webhooks/{id} operator
* Unknown routes admin

Programmatic usage

from runtime.auth import AuthMiddleware, ApiKeyStore, JWTVerifier

store = ApiKeyStore()
store.register("key-abc", subject="alice", role="admin")
store.register("key-xyz", subject="bob", role="executor")

jwt = JWTVerifier("my-secret", default_role="executor")

middleware = AuthMiddleware(
    api_key_store=store,
    token_verifier=jwt,
    allow_anonymous=False,
)

# In your request handler:
identity = middleware.authenticate({"x-api-key": "key-abc"})
if middleware.authorize(identity, "POST", "/v1/skills/s1/execute"):
    # proceed
    ...

Plugin extension

Third-party auth backends can be registered via entry points:

[project.entry-points."agent_skills.auth"]
my_oauth = "my_package.auth:OAuthBackend"