Skip to main content

Intent API Admin

Console-issued admin tokens and zero-boilerplate admin surfaces for any Intent API product.

intent-api-admin plugs into intent-api to add a dedicated admin-machine surface, Ed25519/JWKS-verified admin tokens, an automatic CRUD admin generator from your SQLAlchemy models, role-aware role gates, audit correlation, and a schema document the console can render directly.

Install

pip install intent-api-admin

Requires intent-api>=0.5.0 and Python 3.10+.

Three-step integration

from intent_api import IntentRouter
from intent_api_admin import (
    configure_admin_auth, auto_admin, admin_machine_auth,
)

configure_admin_auth(
    issuer="https://console.example.com",
    audience="myapp",
    base=Base,
)

router = IntentRouter()
router.register("User", UserService())
auto_admin(router, expose=["User", "Team"])

app.include_router(router.build(get_user=get_clerk_user, get_db=get_db))
app.include_router(router.build_admin_machine(get_user=admin_machine_auth(), get_db=get_db))

That is the entire integration. Console operators now get full read/write CRUD on every model in expose=[...], and your handlers are unchanged.

Impersonation (Clerk actor tokens)

Impersonation consumes Clerk's native actor tokens, minted per-product via the product's own Clerk SDK. The console never holds product Clerk credentials and never mints Clerk tokens itself.

The visual indicator is a UX hint, not a guarantee. Always confirm impersonation state via the console's session list before assuming the absence of an indicator means you're not impersonating. The security boundary is server-side.

Two-step integration

Step 1 — Mint. Add a User.create_clerk_actor_token custom action that calls clerk_sdk.actor_tokens.create(...). The full reference adapter is in docs/CLERK_ACTOR_TOKEN_ADAPTER.md.

Step 2 — Enforce. Call enforce_actor_writes at the top of any write handler, OR install actor_writes_middleware once in your runtime.

# Per-handler
from intent_api_admin import enforce_actor_writes

@custom_action(admin_only=True)
async def ban(self, *, db, user, context, id, payload, session_claims=None):
    enforce_actor_writes(session_claims or {}, action="custom",
                         is_read_only_custom_action=False)
    ...

# Or middleware (one-time install)
from intent_api_admin import actor_writes_middleware
runtime = build_runtime(middlewares=[actor_writes_middleware])

Audit context — one line in get_user

bind_impersonation_context populates acting_admin_id and impersonation_session_id as structlog contextvars so every log line in the handler inherits them. One line in your auth dependency:

from intent_api_admin import bind_impersonation_context

async def get_current_user(...):
    auth_state = clerk_sdk.authenticate_request(...)
    bind_impersonation_context(auth_state.payload)   # one-liner
    context.clerk_claims = auth_state.payload
    ...

CSP — frame-ancestors 'none'

The console always launches impersonation in a new tab via window.open(url, "_blank", "noopener,noreferrer"). Set CSP frame-ancestors 'none' on every product response so this architecture self-enforces — even if a future change tries to embed your product, browsers will refuse.

@app.middleware("http")
async def add_csp_headers(request, call_next):
    response = await call_next(request)
    response.headers["Content-Security-Policy"] = "frame-ancestors 'none'"
    return response

See docs/PRODUCT_ADOPTION.md for the full per-product checklist (~60 LOC) and SECURITY.md for the V1 threat model.

Roles

Three roles, ordered by rank:

Role Rank Default for
owner 3 delete on auto-admin services
admin 2 update, default for @custom_action(admin_only=True)
support 1 list, read

A higher-ranked role satisfies any lower-ranked requirement. So an owner token can perform any action that requires support, admin, or owner.

Heuristic defaults

auto_admin() reads each SQLAlchemy model and picks sensible defaults you almost never need to override.

  • list_fields — primary key + name / email / title / slug / status / created_at if present, then non-text columns up to 6 total.
  • search_fields — string columns with index=True or unique=True and length<=255.
  • default_sortcreated_at desc if present; otherwise <primary_key> desc.
  • Auto-redaction patterns — any column whose name starts with password, secret, api_key, token, or _, or ends with _hash / _digest. Redacted fields are stripped from responses and refused on update.

Override patterns

auto_admin(
    router,
    expose=["User", "Team", "Order"],
    redact_fields={"User": ["last_login_ip"]},          # add to auto-redaction
    deny_edit_fields={"User": ["email"]},                # read-only in admin
    deny_delete=["Order"],                                # disallow delete entirely
    overrides={                                           # surgical schema override
        "User": {"default_sort": "email asc",
                 "list_fields": ["id", "email", "name", "plan", "is_active"]},
    },
)

Custom admin actions

Use the augmented @custom_action from intent_api_admin for admin-only commands. They are dispatched on the admin-machine surface only — invisible to standard, MCP, machine, and the legacy admin route.

from intent_api_admin import custom_action

class UserService(IntentService):
    @custom_action(admin_only=True, min_role="admin")
    async def ban(self, *, db, user, context, id, payload):
        ...

    @custom_action(admin_only=True, min_role="support", read_only=True)
    async def view_audit(self, *, db, user, context, id, payload):
        ...

read_only=True is honored by enforce_actor_writes (see above) so the action remains callable under a read-only Clerk actor session.

Common errors and fixes

Code When Fix
ADMIN_AUTH_NOT_CONFIGURED auto_admin() called before configure_admin_auth() Call configure_admin_auth(...) first.
CONFIGURATION_ORDER_ERROR configure_admin_auth() called again after auto_admin() ran Reset only in tests; in prod call exactly once at startup.
MODEL_NOT_FOUND expose=["X"] but X isn't on Base.registry Make sure the model is imported before auto_admin().
WRONG_SURFACE Admin token on /api/intent Use /api/admin-machine-intent for admin tokens.
IMPERSONATION_WRITE_DENIED A write ran under a read-only Clerk actor session Mint the actor token with allow_writes=true, or mark the action read_only=True.
INSUFFICIENT_ROLE Token role rank is below the action's min_role Issue a token with the required role.
FIELD_REDACTED Update payload references a redacted field Drop the field from payload, or remove from redact_fields.
FIELD_READ_ONLY Update payload references a deny_edit_fields field Drop the field, or remove the deny entry.
DELETE_DENIED Delete on a deny_delete model Remove the model from deny_delete=[...].
UNSUPPORTED_FILTER_OP List filter uses an op the auto-admin filter parser doesn't support Use one of eq, ne, lt, lte, gt, gte, contains, in, is_null.

See TOKEN_CONTRACT.md for the full token spec.

Reference test suite

tests/wbs_reference/ is an exhaustive contract suite that mirrors the WBS success criteria S1–S52. Copy it into your repo to validate every adoption.

Release files for intent-api-admin 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for intent-api-admin 0.2.0
File Size Uploaded
intent_api_admin-0.2.0.tar.gz 52.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for intent-api-admin 0.2.0
File Interpreter ABI Platform
intent_api_admin-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 89.9 kB

Release files / intent_api_admin-0.2.0.tar.gz

Download URL intent_api_admin-0.2.0.tar.gz
Size 52.4 kB
Tags Source
SHA-256 checksum
How to use checksums
0a7127fd34d8fc77a8b75ec53c84705fe5cf3ef1e0ac23ed5e01fbb090faf257
BLAKE2b-256 checksum
How to use checksums
9631ccee81f19867479c07016400ad969dbe5a65d5f9315d6497fff466c82e35
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Release files / intent_api_admin-0.2.0-py3-none-any.whl

Download URL intent_api_admin-0.2.0-py3-none-any.whl
Size 37.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e20dd46a87681c1340a95de5c49b222c6af728070562be4325e0147aebd150b6
BLAKE2b-256 checksum
How to use checksums
a64655c0fb6178669167445728f48c476e8b93427f8f38f326794bd5378ba3bf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page