Skip to content

feat(management): Flux API key bearer tokens - #20

Merged
loookashow merged 2 commits into
mainfrom
feat/flux-bearer-token
Aug 10, 2026
Merged

feat(management): Flux API key bearer tokens#20
loookashow merged 2 commits into
mainfrom
feat/flux-bearer-token

Conversation

@loookashow

Copy link
Copy Markdown
Contributor

Adds the Management API calls for a Flux API key's bearer token — an opaque fxk_ credential for hosted MCP connectors.

Why

A hosted MCP connector accepts a single token value and sends it as Authorization: Bearer <token>. The scheme is not selectable. Flux accepts only Simple and Secure, so every authentication-required Flux API was unreachable from one — and since writes always require an authenticated key, that ruled out agent memory entirely: a connector could read public APIs and nothing else.

The token is a second credential on an existing key, not a new kind of key. It identifies that key and nothing more — role, grants and per-collection permissions are the key's own.

What

Both clients, sync and async:

  • issue_flux_api_key_bearer_token(key) — issues or replaces the token, returning FluxAPIKeyBearerToken.
  • revoke_flux_api_key_bearer_token(key) — revokes it.

FluxAPIKeySummary gains bearer_token_prefix and bearer_token_issued_at.

Two things worth reviewing closely

The calls address the token sub-resource, never the key. A request to the key's own URL would delete the key and take its public_key/secret_key down with it, silently breaking every integration already using them. Both tests assert the URL ends in /bearer-token/ and explicitly assert it does not end in /api-keys/{key}/ — the negative half is the one that catches this, and it is the mistake here that fails quietly.

The new fields are optional. The SDK ships ahead of the server deployment, so the model has to keep validating a response from a server that predates the feature. There is a test for exactly that: FluxAPIKeySummary validates the old payload shape with both fields None, and validates the new one with them populated.

The show-once contract

The plaintext is returned only by the issue call, and only once — the service stores a hash, exactly as it does for secret_key. A lost token is re-issued, not recovered. Every later read exposes only bearer_token_prefix, the first 12 characters: enough to recognise a token in a config file, never enough to use one. The test asserts a validated FluxAPIKeySummary carries no bearer_token attribute at all.

Issuing does not disturb the key

public_key, secret_key, role and grants survive an issue, a re-issue and a revoke. That is the point of a separate sub-resource rather than a field on the key: re-issuing cuts off a connector while everything using the key pair keeps working — revocation without recreating the key and reconfiguring its consumers.

Requires

A server with bearer-token support. Against an older one both methods return 404.

Tests

Full suite green; ruff check . and ruff format --check . clean over the files this branch touches.

Hosted MCP connectors accept a single token value and send it as
`Authorization: Bearer <token>`; the scheme is not selectable. Flux accepts only
`Simple` and `Secure`, so every authentication-required Flux API was unreachable
from them — and since writes always require an authenticated key, that ruled out
agent memory entirely.

Adds the two Management API calls that manage the credential, on both the sync
and the async client:

- `issue_flux_api_key_bearer_token(key)` issues or replaces the token and
  returns the plaintext. It is returned only here and only once: the service
  stores a hash, exactly as for `secret_key`, so a lost token is re-issued
  rather than recovered.
- `revoke_flux_api_key_bearer_token(key)` revokes it.

Both address the token sub-resource, never the key. Issuing, re-issuing and
revoking all leave `public_key`, `secret_key`, `role` and grants untouched, so
`Simple` and `Secure` integrations keep working — that is what makes a re-issue
a way to cut off a connector without recreating a key and reconfiguring
everything that uses it.

`FluxAPIKeySummary` gains `bearer_token_prefix` and `bearer_token_issued_at`,
both OPTIONAL so the model still validates a response from a server that
predates the feature — the SDK ships ahead of the deployment. The prefix is the
first 12 characters: enough to recognise a token in a config file, never enough
to use one.

Tests cover both clients and pin that each call reaches the sub-resource and not
the key's own URL. A request to the key URL would delete the key and take its
Simple/Secure credentials down with it, which is the one mistake here that fails
silently.

Requires a server with bearer-token support; against an older one both methods
return 404.

Version bumped in src/foxnose_sdk/_version.py, the single source the build
backend reads, and in the test that pins it.

The changelog entry goes in docs/changelog.md, which is the maintained one — it
is published through mkdocs and carries 0.7.0 and 0.7.1. The root CHANGELOG.md
stopped at 0.6.0 some releases ago and is left untouched here rather than half
revived.
@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@loookashow loookashow changed the title Feat/flux bearer token feat(management): Flux API key bearer tokens Aug 10, 2026
@loookashow
loookashow merged commit fb5c98a into main Aug 10, 2026
6 checks passed
@loookashow
loookashow deleted the feat/flux-bearer-token branch August 10, 2026 09:11
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.

1 participant