Metadata-Version: 2.4
Name: draco-toolkit
Version: 0.1.1
Summary: Authentication and authorization for AI agents: SPIFFE identity, per-tool OAuth tokens, per-call authorization, and behavioral telemetry
Project-URL: Homepage, https://draco-tech.ai/
Project-URL: Repository, https://github.com/Draco-Tech-ai/draco-toolkit
Project-URL: Issues, https://github.com/Draco-Tech-ai/draco-toolkit/issues
Project-URL: Documentation, https://github.com/Draco-Tech-ai/draco-toolkit/tree/main/docs
Author: Draco Tech
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: ai-agents,authentication,authorization,oauth,openfga,spiffe,token-exchange,workload-identity
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: cryptography>=43.0
Requires-Dist: httpx>=0.27
Requires-Dist: hvac>=2.3
Requires-Dist: pyjwt>=2.9
Requires-Dist: python-ulid>=3.0
Requires-Dist: spiffe>=0.2
Provides-Extra: agent
Requires-Dist: langchain-core>=0.3; extra == 'agent'
Requires-Dist: langchain-openai>=0.3; extra == 'agent'
Requires-Dist: langchain>=0.3; extra == 'agent'
Requires-Dist: langgraph>=0.4; extra == 'agent'
Requires-Dist: pydantic-settings>=2.0.0; extra == 'agent'
Provides-Extra: dev
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.22; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: metrics
Requires-Dist: opentelemetry-api>=1.20; extra == 'metrics'
Requires-Dist: opentelemetry-sdk>=1.20; extra == 'metrics'
Description-Content-Type: text/markdown

# draco (Python)

Python library for the Draco AI agent authentication and authorization system.

## Install

```bash
uv pip install -e ".[dev]"
```

Optional extras:

```bash
uv pip install -e ".[metrics]"   # OpenTelemetry instrumentation
```

## Test

```bash
pytest
```

## Lint

```bash
ruff check .
ruff format --check .
mypy src/
```

## Configuration

The SDK reads a JSON config file via `bootstrap(Mode.DIRECT, path)`. All IdP-specific behavior is controlled by config fields; the code has no IdP-specific branches.

### Token flow

The SDK uses a two-phase token model:

1. Base token: SVID assertion at the IdP's token endpoint. The grant type and parameter names vary by IdP.
2. Per-target tokens: RFC 8693 token exchange of the base token, scoped by audience.

DCR (RFC 7591) is optional. When configured, the agent self-registers with the IdP at startup and deregisters on shutdown. When not configured, the agent uses a pre-registered client identified by the SVID's subject claim.

### Keycloak

Keycloak uses `client_credentials` grant with the SVID as a `client_assertion` (federated-jwt client authentication). DCR is not supported directly with SVIDs; agents use pre-registered clients.

```json
{
  "agent_spiffe_id": "spiffe://example.draco.local/agents/hello-agent/default",
  "spiffe_endpoint_socket": "unix:///spiffe-workload-api/spire-agent.sock",
  "idp_token_endpoint": "https://keycloak.example.draco.local/realms/draco/protocol/openid-connect/token",
  "idp_grant_type": "client_credentials",
  "ca_bundle_path": "/etc/ssl/step-ca/root_ca.crt",

  "authz_backend": "openfga",
  "openfga_url": "https://openfga.example.draco.local",
  "openfga_store_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
  "openfga_audience": "openfga",

  "posture_source": "/etc/draco/bundles",
  "trust_store_backend": "file",
  "trust_store_path": "/var/draco/trust-state.json",

  "tools": [...]
}
```

Keycloak prerequisites:

- A Keycloak client (e.g. `draco-hello-agent`) with `clientAuthenticatorType=federated-jwt`, `serviceAccountsEnabled=true`, and `standard.token.exchange.enabled=true`.
- `jwt.credential.sub` set to the agent's SPIFFE ID.
- `jwt.credential.issuer` set to the SPIFFE IdP alias (e.g. `spire`).
- A SPIFFE identity provider at the realm level with the SPIRE trust domain and bundle endpoint.
- For each target service (e.g. OpenFGA), a Keycloak client registered with that service name as the `clientId`, and the requesting client's service account assigned a role on that target client (Keycloak resolves available exchange audiences via role mappings).
- A self-audience mapper on the agent's client so the base token's `aud` includes the client itself (required by Keycloak's standard token exchange).

Other RFC 8693-capable IdPs are supported through the same configuration surface (`idp_grant_type`, `idp_client_audience`, and the optional `dcr` block); Keycloak is the documented reference.

### Config field reference

| Field | Required | Default | Description |
|---|---|---|---|
| `agent_spiffe_id` | yes | | The agent's SPIFFE ID. |
| `spiffe_endpoint_socket` | yes | | Path to the SPIRE Workload API socket. |
| `idp_token_endpoint` | yes | | OAuth token endpoint URL. |
| `idp_grant_type` | no | `client_credentials` | Grant type for the base token. `client_credentials` (Keycloak federated-jwt), or `urn:ietf:params:oauth:grant-type:jwt-bearer` for IdPs that take the SVID as the authorization grant. |
| `idp_assertion_type` | no | `urn:ietf:params:oauth:client-assertion-type:jwt-spiffe` | Client assertion type. |
| `idp_client_audience` | no | derived from token endpoint | Audience for the client_assertion SVID. Omit for Keycloak (derived from realm URL); set explicitly when the IdP expects a fixed audience string. |
| `ca_bundle_path` | no | | CA bundle for TLS verification to the IdP. |
| `authz_backend` | yes | | `openfga` for live authz, `bundle` for local-only. |
| `openfga_url` | when openfga | | OpenFGA API URL. |
| `openfga_store_id` | when openfga | | OpenFGA store ID. |
| `openfga_audience` | no | `openfga_url` | Audience for RFC 8693 exchange when targeting OpenFGA. Set to the Keycloak client_id for OpenFGA (e.g. `openfga`). |
| `posture_source` | yes | | Path to the directory containing per-trust-level bundle JSON files. |
| `trust_store_backend` | no | `file` | `file` or `openbao`. |
| `trust_store_path` | when file | | Path to the trust state JSON file. |
| `dcr` | no | | DCR configuration. Omit to skip DCR (pre-registered client). |
| `dcr.auth_method` | no | `private_key_jwt` | `token_endpoint_auth_method` declared to the IdP during DCR. |
| `dcr.grant_types` | no | `[client_credentials, token-exchange]` | Grant types requested during DCR. |
| `dcr.jwks_uri` | no | | SPIRE OIDC JWKS URL, included in the DCR body so the IdP can verify the client's assertions. |
| `dcr.scopes_source` | no | `policy` | Where to derive scopes for DCR. |
| `tools` | no | | Array of tool definitions (`name`, `description`, `parameters`, `endpoint`, `transport`). |
