Skip to content

feat(audit): accept a named role when creating or re-scoping an API key - #113

Merged
ngjunsiang merged 2 commits into
mainfrom
feat/audit-apikey-role
Oct 20, 2026
Merged

ngjunsiang merged 2 commits into
mainfrom
feat/audit-apikey-role

Conversation

@ngjunsiang

Copy link
Copy Markdown
Contributor

Closes #112

Summary

The audit service has a named role catalogue for API keys (campus#918, campus PR #935): a role name is accepted wherever a scope list is accepted, and resolves to exactly one scope list.

Role Grants
traces-viewer traces:read
traces-searcher traces:read, traces:search
traces-producer traces:read, traces:search, traces:write
key-operator apikeys:read, apikeys:write

The SDK could not express that: APIKeys.new() required a scope list and there was nowhere to name a role, so every caller hand-assembled the scope list the catalogue exists to avoid.

APIKeys.new() and APIKey.update() now take a role, and scopes became optional so a key can be created by role alone.

The SDK does not own the catalogue

  • An unknown role is the server's to refuse, and the server names the roles that exist. The SDK does not keep its own copy of the role names — a client-side copy would drift from the service's.
  • A role and a scope list together are refused server-side (they leave it ambiguous which was meant). The SDK sends both rather than silently preferring one, so the server's answer is what the caller sees.

That is deliberate: the catalogue is a service-side vocabulary, and the SDK is a client of it.

Testing

  • test_new_posts_a_role_instead_of_scopes — the payload carries the role and no scope list.
  • test_new_sends_both_role_and_scopes_when_given — both reach the server, unmodified.
  • test_update_patches_a_role — the re-scoping path.

poetry run pytest: 295 passed. Ruff and pyright report the same counts as main (43 and 37) — all pre-existing in this checkout, none from this change.

Related

  • campus#918 (the role catalogue — the server side), campus#927 (the key-management page), campus-cli#67 (the --role surface that will use this)

The audit service has a named role catalogue for API keys (campus#918,
campus PR #935): a role name is accepted wherever a scope list is
accepted, and resolves to exactly one scope list.

The SDK could not express that — APIKeys.new() required a scope list
and there was nowhere to name a role — so every caller hand-assembled
the scope list the catalogue exists to avoid.

APIKeys.new() and APIKey.update() now take a role, and scopes becomes
optional so a key can be created by role alone.

The SDK does not validate the role, and does not duplicate the
catalogue: the service owns it. An unknown role is refused by the
server, which names the roles that exist. A role and a scope list
together are refused server-side too (they leave it ambiguous which
was meant), so this change sends both rather than silently preferring
one — the server's answer is the right one.
The role parameter came with docstrings, but the README still
described a package layout from before the audit client existed — the
`audit/` package (apikeys, traces, the ingest circuit breaker) was
missing from the tree, and the audit client was missing from the
feature list.

The apikeys module now also quotes the service's role catalogue, for
orientation: it is explicitly not a contract, since the service is the
authority and refuses an unknown role with the names of the roles it
does have.
@ngjunsiang
ngjunsiang merged commit 2667846 into main Oct 20, 2026
5 checks passed
@ngjunsiang
ngjunsiang deleted the feat/audit-apikey-role branch October 20, 2026 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(audit): accept a named role when creating or re-scoping an API key

1 participant