Reverse-specification of python-barbicanclient — branch stable/2026.2
Branch
stable/2026.2@8162006e8677(7.6.0-2-g8162006, 2 maintenance commits after tag7.6.0) — repository https://opendev.org/openstack/python-barbicanclient — analysed on 2026-11-01
- Scope: the whole repository: the
openstackplugin, the Python API under it, and the legacybarbicanshell. The change history (step 7) was not requested, so there is no 06 file. - Dependencies read at fixed versions: python-openstackclient 10.3.1, osc-lib 4.7.0, cliff 4.16.0, keystoneauth1 5.17.0. Cited with the prefixes
openstackclient:,osc-lib:,cliff:,keystoneauth1:. - Other repositories read, not run:
barbican:@de32745a7a4f,keystonemiddleware:@3a8a835,castellan:@bd6bd34, all onstable/2026.2. - Markers: references are
file:linefrom the repository root. [observed] means seen by running the real CLI against the local stub on 127.0.0.1. [inferred] means not followed to the end. Unmarked statements were read in the code. - Observed behaviour against a stub says nothing about a real service. The stub answers what it was told to. It shows what the client sends, and how it reads a given answer. No real Barbican, Keystone, lab or cloud was contacted.
Files of this specification
| File | Content |
|---|---|
| 01-cli-commands_stable-2026.2.md | shared mechanics, functional description per family, the 23 commands with class, arguments and observed requests, the Python API behind them, the legacy shell |
| 02-errors_stable-2026.2.md | exit codes, HTTP translation, exception classes, client-side messages with raise sites, and the defects with their reproduction |
| 03-interactions_stable-2026.2.md | Keystone, Barbican discovery and API, headers, sample frames, local files |
| 04-scenarios_stable-2026.2.md | 6 scenarios with sequence diagrams and diagnostics |
| 05-async-processing_stable-2026.2.md | none; what the client leaves to the user |
| 07-rewrite-pack_stable-2026.2.md | client contract: what the client requires from a key manager, vectors, proof and limits |
| _verification_stable-2026.2.md | output of the reference checker |
Overview
python-barbicanclient provides three things:
- a Python library,
barbicanclient.client.Client(barbicanclient/client.py:150), which castellan and others use; - a plugin of the
openstackcommand with 23 commands on secrets, consumers, orders, containers, CAs and ACLs (setup.cfg:35); - a deprecated stand-alone shell,
barbican, running the same 23 command classes (setup.cfg:30).
Every command is a thin layer on the library. A process authenticates, negotiates a microversion through two discovery requests, sends one to about a dozen REST calls, prints a table and exits. There is no database, bus, cache, server or background thread.
Architecture
flowchart LR user[User or script] -->|argv, env| osc[openstack process] user -->|argv, env| legacy[barbican process, deprecated] osc -->|entry points| cmds[barbicanclient.barbican_cli.v1] legacy -->|entry points| cmds cmds --> lib[barbicanclient.v1 managers] other[castellan and other Python callers] --> lib lib --> http[_HTTPClient, keystoneauth Adapter] http -->|HTTPS, X-Auth-Token, OpenStack-API-Version| kms[Barbican] http -->|POST /v3/auth/tokens| ks[Keystone] cmds <-->|--file| fs[local files]
Processes
| Process | Entry point | Role | Listens to / consumes |
|---|---|---|---|
openstack | plugin key_manager, setup.cfg:33, barbicanclient/osc_plugin.py:26 | runs one of the 23 commands | argv, environment; listens on nothing |
barbican | setup.cfg:30, barbicanclient/barbican.py:380 | deprecated shell, same commands, with its own authentication options and a no-auth mode | argv, environment; listens on nothing |
External dependencies
| System | Protocol | Library | Configuration | Usage |
|---|---|---|---|---|
| Keystone | HTTPS REST | keystoneauth1 | --os-auth-url, clouds.yaml for OSC; --os-* for barbican | token and catalog |
| Barbican | HTTPS REST | keystoneauth1 Adapter (barbicanclient/client.py:34) | catalog, service type key-manager, interface public (barbicanclient/client.py:28); --endpoint for barbican only | everything else |
Key figures
| Item | Count |
|---|---|
| Commands | 23 in one group openstack.key_manager.v1, 78 arguments, 0 import failures |
| Probe runs on the stub | 54 per mode (23 base runs, 31 option variants), 3 modes |
| Commands that sent a request | 23 of 23 in each mode |
| Operations used | 32: 25 observed on the wire, 7 only reachable from the Python API |
| Operations with no route in the service | 3: GET /cas, GET /cas/{id}, POST /secrets/{id}/metadata/{key} (read from barbican:, not run) |
| Service operations never called | 26, listed in the surface file |
| Exception classes | 9 |
| Unit tests | 254 pass, 2 identical runs; 213 hold at least one request, 384 requests. 0 of the 23 command classes run in them. |
| Defect reproductions | 86 cases, 1 process each, in defect-repro |
| Background processing | none |
Points of attention
Details and reproductions are in 02 § 4.
- **An error body without a top-level
titlehides the error [observed]**: the client prints'title'and loses the status (barbicanclient/client.py:141). The body keystonemiddleware sends on a 401 has this form (keystonemiddleware:keystonemiddleware/auth_token/__init__.py:682), so a rejected token should read this way on a real deployment [inferred, the body was read, not captured]. - **Most discovery failures read
Invalid client version v1; must be one of: v1[observed]**, because the factory catches everyKeyErrorandValueError(barbicanclient/client.py:215). That includes a version document withoutmax_versionand a server range the client does not cover. - A new field in a response breaks the client [observed]: secrets, consumers and orders are built with
**response(barbicanclient/v1/secrets.py:679,barbicanclient/v1/orders.py:359). A key-manager rewrite cannot add fields. - A secret without a name costs about 10 GET per display [observed]: the lazy test is "no name yet", so every property re-fetches (
barbicanclient/v1/secrets.py:468).secret getsent 10 requests, andsecret listwith 2 unnamed secrets sent 21. - **
secret store --nameshowsStatus NoneandCreated None[observed]**, for the same reason in reverse: a named secret is never reloaded. - **
secret store --payload xstoresapplication/octet-streamin base64, not text [observed]**, and the help says--payload-content-typeis required with--payload(barbicanclient/barbican_cli/v1/secrets.py:165,barbicanclient/barbican_cli/v1/secrets.py:196). - **
--debugwrites secret payloads to stderr [observed]**, onsecret storeandsecret get --payload, despite the masking in the library's own debug line (barbicanclient/v1/secrets.py:389,keystoneauth1:keystoneauth1/session.py:602). - **
secret container create --secret foosilently drops the secret [observed]** and creates an empty container (barbicanclient/barbican_cli/v1/containers.py:138). - **
-f jsonfails on RSA and certificate containers [observed]**, after a create has already succeeded when it issecret container create(barbicanclient/v1/containers.py:294,barbicanclient/v1/containers.py:415). - **
ca getandca listhave no route in Barbicanstable/2026.2** (barbican:barbican/api/controllers/versions.py:141). They should answer 404 on a real service [inferred]. - **
add_secret_metadataposts to a route the service refuses** with 405 (barbicanclient/v1/secrets.py:808,barbican:barbican/api/controllers/secrets.py:104). Its 3 unit tests pass against a mock [inferred for the live result, both sides were read]. - The tests that call the shell pass without running the command [observed]: they ignore the exit code (
barbicanclient/tests/keystone_client_fixtures.py:252). So nothing in the unit tests exercisesbarbican_cli, which is why items 5, 6, 8 and 9 survive.
No command returns a value from command.Command, so the defect of two OSC commands does not exist here: the 7 such classes (barbicanclient/barbican_cli/v1/secrets.py:38, barbicanclient/barbican_cli/v1/secrets.py:106, barbicanclient/barbican_cli/v1/secrets.py:225, barbicanclient/barbican_cli/v1/secrets.py:249, barbicanclient/barbican_cli/v1/orders.py:69, barbicanclient/barbican_cli/v1/containers.py:33, barbicanclient/barbican_cli/v1/acls.py:92) have no return, and all exit 0 in shape mode [observed].
Blind spots / [inferred]
- No real service was contacted, by instruction. Everything about real answers comes from reading the
barbican:andkeystonemiddleware:source, and is marked. Theshapestub bodies were written from that source. - **
functionaltests/was not run**: it needs a live Barbican and Keystone (etc/functional_tests.conf:5). - Unit tests prove little about the CLI: see point 12. The vectors record what the Python API sends against mocked answers.
- Argument coverage: each command ran with its required arguments, and 31 variants added the options that change a request. Not every option combination ran.
--os-*session options (--insecure,--os-cacert,--timeout) were not exercised, since the stub has no TLS. - Identity v2 in the legacy shell was read, not run: the stub has only v3.
- Output formatting by cliff was not specified per command. Only the
-f jsonfailure was followed. - Python API callers other than castellan were not surveyed.
- Skill scripts:
inventory.pyandcheck_refs.pywere run from the skill and are not copied; the adapted tools are in_rewrite/.
Source: reverse-spec-python-barbicanclient/00-summary_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.