OpenKeel: the sovereign French Cloud solution of the inherent group, with a European reach Discover inherent

Reverse-spec · Libraries

keystoneauth — Rewrite pack

Client contract (rewrite pack) — keystoneauth → Keystone and services — branch stable/2026.2

Branch stable/2026.2 @ 1cd5400202c6 — repository https://opendev.org/openstack/keystoneauth — analysed on 2026-10-28 Service used for the proof: Keystone stable/2026.2 @ e6dc5ae079e6 (../reverse-spec-keystone/), run in-process

keystoneauth is the client of every OpenStack API. A rewrite of Keystone (OpenKeel Keystone), or of any other service, must answer what keystoneauth expects. Otherwise the CLIs, the SDKs, keystonemiddleware and the services themselves (Nova towards Placement, for example) stop working. This file lists these requirements and provides a suite of 9 scenarios recorded against the real Keystone.

1. Contents of _rewrite/

FileContent
[exchanges_stable-2026.2.json](_rewrite/exchanges_stable-2026.2.json)the 9 scenarios (name, expected result, result, detail) and the 26 HTTP exchanges between keystoneauth and Keystone (requests, headers, bodies, responses)
[response-schemas_stable-2026.2.json](_rewrite/response-schemas_stable-2026.2.json), [response-schemas_stable-2026.2.md](_rewrite/response-schemas_stable-2026.2.md)schemas of the Keystone responses observed through keystoneauth
[observed-errors_stable-2026.2.md](_rewrite/observed-errors_stable-2026.2.md)errors received (401 on authentication and on a revoked token)

How it was produced: conformance_capture.py scenarios with the keystoneauth adapter (skill generate-reverse-spec).

  1. A throw-away Keystone: SQLite, db_sync, fernet_setup, credential_setup, receipt_setup, bootstrap.
  2. Served in-process at http://keystone.test through wsgi-intercept.
  3. Each scenario uses keystoneauth's public API, as a service or a CLI would.

The test data (password secretpassword, throw-away tokens) are fictitious.

2. Scenarios (proof: 9/9 as expected)

#ScenarioWhat it provesExchanges
1generic-password-discovery-project-scopediscovery on / (300 accepted), v3 authentication, catalog lookup (identity, public)GET / 300, POST /v3/auth/tokens 201
2v3-password-domain-and-system-scopescope.domain and scope.system.all2 × POST /v3/auth/tokens 201
3v3-unscoped-then-token-rescope"scope": "unscoped", then the token method with X-Auth-Token2 × 201
4session-endpoint-filter-list-usersrelative URL resolved through the catalog; no extra discovery when the catalog URL already ends with the versionGET /v3/users 200
5v3-password-wrong-password401 → keystoneauth1.exceptions.UnauthorizedPOST /v3/auth/tokens 401
6session-reauth-after-revocation401 on a revoked token → invalidate, re-authenticate, replay onceGET 401, POST 201, GET 200
7v3-application-credentialapplication_credential method; token scoped to the credential's projectPOST …/application_credentials 201, POST /v3/auth/tokens 201
8v3-multifactor-receipt-then-totp**401 + Openstack-Auth-Receipt** → MissingAuthMethods (methods=['password']), then password + totp in one request401 receipt, 201
9discovery-version-documentparsing of the Keystone version document (v3.14)GET / 300

3. What keystoneauth requires from Keystone

RequirementSource
POST /v3/auth/tokens returns 201 (any 2xx works [inferred]), the **X-Subject-Token** header and a JSON body with **token**; otherwise InvalidResponsekeystoneauth1/identity/v3/base.py:240-262, keystoneauth1/access/access.py:45
token fields read by AccessInfoV3: expires_at, issued_at, user (id, name, domain), project/domain/system, roles, catalog (type, name, id, endpoints[].interface/region_id/region/url/id), audit_ids, OS-TRUST:trust, application_credential, is_domainkeystoneauth1/access/access.py
accepts "scope": "unscoped" (string) and {"system": {"all": true}}keystoneauth1/identity/v3/base.py:186-227
MFA: 401 **+ Openstack-Auth-Receipt header** + body receipt.methods, receipt.expires_at, required_auth_methodskeystoneauth1/exceptions/http.py:505-510, keystoneauth1/exceptions/auth.py:23-32
401 (not 403 or 404) for a revoked or expired X-Auth-Token on any route, so that re-authentication happenskeystoneauth1/session.py:1128-1136
version document on / and /v3: versions.values[] (or versions[], or version), each with id, status, links[rel=self]keystoneauth1/discover.py:66-160
errors: {"error": {"message", "details"}} (Keystone), x-openstack-request-id, Retry-Afterkeystoneauth1/exceptions/http.py:445-531

4. What keystoneauth requires from every service (OpenKeel Placement included)

  • Version document at the root or at the versioned URL, with min_version/max_version for microversions. Without one, the catalog URL is used as is (version hack) or DiscoveryFailure is raised.
  • Microversion header OpenStack-API-Version: <service_type> <x.y>, with the service type as keystoneauth names it (placement, volume for Cinder…).
  • Retriable statuses: 429 and 503, with Retry-After honoured by the callers that enable retries.
  • Error bodies {"errors": [{"title", "detail"}]} (API-SIG) or {"error": {"message"}}.
  • Redirects with Location; the method is kept.

5. Conformance strategy for OpenKeel

  1. Run the same 9 scenarios against the Go Keystone. Point the adapter at its URL instead of the in-process app (run_scenarios: replace initialize_application with a reset of the Go service and its URL). Expected: the same outcomes, the same sequences of (method, route, status), and the presence of X-Subject-Token, Openstack-Auth-Receipt and WWW-Authenticate on the same responses.
  2. Validate the response bodies against response-schemas_stable-2026.2.json, and against the schemas declared in Keystone (../reverse-spec-keystone/_rewrite/).
  3. Then the suites of the clients: osc-placement (928 commands, ../reverse-spec-osc-placement/), and Tempest identity.

6. Gaps

GapImpact
federation (OIDC, SAML, WebSSO), OAuth1/OAuth2, Kerberos, tokenless: not run (they need an external IdP or mTLS)requirements read from the code only; to cover with an OIDC IdP in the test environment
microversion discovery of a service other than Keystone not exercised (Keystone has no microversions)covered by osc-placement (negotiation) and Placement's gabbi
v2 plugins not exercisedobsolete, Keystone v2 removed

7. Checklist (OpenKeel Keystone seen from keystoneauth)

  • [ ] 300 + version document on /, 200 on /v3
  • [ ] 201 + X-Subject-Token + token on POST /v3/auth/tokens; unscoped and system.all scopes
  • [ ] 401 + receipt for incomplete MFA; 401 for a revoked token on every route
  • [ ] the 9 scenarios as expected

Source: reverse-spec-keystoneauth/07-rewrite-pack_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.