Contract first
The checked-in OpenAPI document currently describes 143 paths and 190 operations. The Rust API generates it, and the Svelte client generates TypeScript types from the same file.
Tests fail when either checked-in layer drifts. That makes the contract executable: a new handler is not complete until clients can discover and type it.
mise run openapi
cd web
pnpm run gen:apiAuthentication and scope
Authenticated API calls send a bearer key. A key resolves an identity, tenant and access predicate; handlers do not accept a tenant selector from request data.
Keys are shown once, stored hashed and represented in the interface only by an audit-safe prefix.
curl 'http://127.0.0.1:8080/assets?limit=20' \
-H 'Authorization: Bearer $DAMRS_API_KEY'Five MCP tools
MCP is another way into the same library, not a parallel authorization system.
| Tool | Purpose |
|---|---|
| search_assets | Run the same scoped query language as the interface |
| get_asset | Read metadata and evidence for one visible asset |
| get_brand_guidelines | Retrieve the tenant guidance needed for use |
| check_rights | Evaluate a proposed channel and territory |
| get_download_url | Request a purpose-bound delivery URL |
- Tool discovery reveals shapes, not hidden assets.
- Every result is limited by the caller’s compiled access predicate.
-
get_download_urlstill enters the ordinary delivery chokepoint. - Cross-tenant asset identifiers return no useful oracle.
Errors should teach the contract
Query parsing refuses an unknown field and reports its character offset instead of silently treating a typo as free text. Readiness names the dependency that failed. Configuration rejects unknown keys at startup.
That directness is part of the API design: a client should be able to correct a request without reading Rust source or inferring state from an empty body.