REST API — barbican — branch stable/2026.2
Branch
stable/2026.2@de32745a7a4f— repository https://opendev.org/openstack/barbican — analysed on 2026-10-29
How routing works (Pecan)
Barbican does not declare routes as data. The paste composite main maps / to the version application, /healthcheck to oslo.middleware and /v1 to the API pipeline (etc/barbican/barbican-api-paste.ini:1). The API application is a pecan.Pecan whose root is V1Controller (barbican/api/app.py:105, barbican/api/app.py:47). Pecan dispatches on objects:
- an attribute is a URL segment:
self.secrets,self.orders… andproject-quotas/secret-stores, set withsetattrbecause they are not identifiers (barbican/api/controllers/versions.py:139); _lookup(self, id, *remainder)consumes a dynamic segment, loads the entity and returns the next controller (barbican/api/controllers/secrets.py:358);@pecan.expose(generic=True) def indexplus@index.when(method='GET')selects the handler by HTTP method; the genericindexitself answers405for any other method (barbican/api/controllers/secrets.py:374).
The full table, with the real handler of each method (decorator closures unwrapped), policy rule, accepted Content-Type, body schema and the source line of each tree edge, is extracted by _rewrite/tools/barbican_routes.py into _rewrite/routes_stable-2026.2.json; _rewrite/openapi_stable-2026.2.json is built from it.
Every handler is wrapped, outermost first, by handle_exceptions (exception → status and message, barbican/api/controllers/__init__.py:100), enforce_rbac (policy check, then the caller's project id becomes the first argument, barbican/api/controllers/__init__.py:74) and, for bodies, enforce_content_types (415, barbican/api/controllers/__init__.py:132). Request bodies are read with a size cap and validated by load_body (barbican/api/__init__.py:40).
Common rules
- Project: the caller's project comes from the token (
X-Project-Idset by keystonemiddleware,barbican/api/middleware/context.py:80). The first call of a project creates itsprojectsrow (barbican/common/resources.py:34). - References: entities are returned as absolute URLs (
secret_ref,container_ref,order_ref…), built from[DEFAULT] host_hrefor, when unset, from the request (Host,X-Forwarded-Proto, path prefix without/v1) (barbican/common/utils.py:67,barbican/common/hrefs.py:65). - Paging:
offset(default 0, negative → 0) andlimit(defaultdefault_limit_paging=10, clamped to 1…max_limit_paging=100; non-numeric → default) (barbican/model/repositories.py:244). Lists returntotalandnext/previousURLs when applicable; an empty page returns{"<name>": [], "total": n}without links (barbican/common/hrefs.py:132). - Identifiers: secret, container, consumer and transport-key ids must be UUID version 4, otherwise
404"Not Found. Provided … id is invalid." (barbican/common/utils.py:210). Order ids are not checked: an unknown id gives404 Order not found.(barbican/api/controllers/orders.py:116). - Body: JSON at most
max_allowed_request_size_in_bytes(25,000) bytes, read truncated at that size (barbican/api/__init__.py:51,barbican/common/config.py:36); string values are stripped recursively (barbican/api/__init__.py:140). - Microversions: header
OpenStack-API-Version: key-manager 1.x, parsed by microversion-parse (barbican/api/middleware/microversion.py:30); absent means1.0.1.1addsconsumersto secret representations;1.2refuses to delete a secret that has consumers unless?force=true(doc/source/api/microversion_history.rst:22).
Operations
Statuses in the last column were observed in the functional capture (_rewrite/exchanges-functional_stable-2026.2.json) or the API unit tests (_rewrite/exchanges-api-tests_stable-2026.2.json); 401 applies to every /v1 call without a valid token (keystonemiddleware) and is not repeated.
| Method | Path | Handler | Policy | Success | Errors (observed or by code) |
|---|---|---|---|---|---|
| GET | / | barbican/api/controllers/versions.py:182 | — | 300 | 406 |
| GET | /v1 | barbican/api/controllers/versions.py:156 | — | 200 | 406 |
| GET | /healthcheck | oslo.middleware (etc/barbican/barbican-api-paste.ini:59) | — | 200 | 503 |
| GET | /v1/secrets | barbican/api/controllers/secrets.py:380 | secrets:get | 200 | 400, 403 |
| POST | /v1/secrets | barbican/api/controllers/secrets.py:473 | secrets:post | 201 | 400, 403, 404, 413, 415 |
| GET | /v1/secrets/{secret_id} | barbican/api/controllers/secrets.py:117 | secret:get (secret:decrypt if Accept ≠ JSON) | 200 | 403, 404, 406 |
| PUT | /v1/secrets/{secret_id} | barbican/api/controllers/secrets.py:221 | secret:put | 204 | 400, 403, 404, 409, 413, 415 |
| DELETE | /v1/secrets/{secret_id} | barbican/api/controllers/secrets.py:258 | secret:delete | 204 | 400, 403, 404 |
| GET | /v1/secrets/{secret_id}/payload | barbican/api/controllers/secrets.py:203 | secret:decrypt | 200 | 400, 403, 404, 405, 406 |
| GET | /v1/secrets/{secret_id}/acl | barbican/api/controllers/acls.py:62 | secret_acls:get | 200 | 403, 404 |
| PATCH | /v1/secrets/{secret_id}/acl | barbican/api/controllers/acls.py:72 | secret_acls:put_patch | 200 | 400, 403, 404, 415 |
| PUT | /v1/secrets/{secret_id}/acl | barbican/api/controllers/acls.py:124 | secret_acls:put_patch | 200 | 400, 403, 404, 415 |
| DELETE | /v1/secrets/{secret_id}/acl | barbican/api/controllers/acls.py:186 | secret_acls:delete | 200 | 403, 404 |
| GET | /v1/secrets/{secret_id}/metadata | barbican/api/controllers/secretmeta.py:52 | secret_meta:get | 200 | 403, 404 |
| PUT | /v1/secrets/{secret_id}/metadata | barbican/api/controllers/secretmeta.py:67 | secret_meta:put | 201 | 400, 403, 404, 415 |
| POST | /v1/secrets/{secret_id}/metadata | barbican/api/controllers/secretmeta.py:85 | secret_meta:post | 201 | 400, 403, 404, 409, 415 |
| GET | /v1/secrets/{secret_id}/metadata/{key} | barbican/api/controllers/secretmeta.py:126 | secret_meta:get | 200 | 403, 404 |
| PUT | /v1/secrets/{secret_id}/metadata/{key} | barbican/api/controllers/secretmeta.py:145 | secret_meta:put | 200 | 400, 403, 404, 409, 415 |
| DELETE | /v1/secrets/{secret_id}/metadata/{key} | barbican/api/controllers/secretmeta.py:172 | secret_meta:delete | 204 | 403, 404 |
| GET | /v1/secrets/{secret_id}/consumers | barbican/api/controllers/consumers.py:279 | secret_consumers:get | 200 | 403, 404 |
| POST | /v1/secrets/{secret_id}/consumers | barbican/api/controllers/consumers.py:318 | secret_consumers:post | 200 | 400, 403, 404, 415 |
| DELETE | /v1/secrets/{secret_id}/consumers | barbican/api/controllers/consumers.py:347 | secret_consumers:delete | 200 | 400, 403, 404, 415 |
| GET | /v1/secrets/{secret_id}/consumers/{consumer_id} | barbican/api/controllers/consumers.py:235 | consumer:get | 200 | 403, 404 |
| GET | /v1/containers | barbican/api/controllers/containers.py:139 | containers:get | 200 | 403 |
| POST | /v1/containers | barbican/api/controllers/containers.py:181 | containers:post | 201 | 400, 403, 404, 415 |
| GET | /v1/containers/{container_id} | barbican/api/controllers/containers.py:65 | container:get | 200 | 403, 404 |
| DELETE | /v1/containers/{container_id} | barbican/api/controllers/containers.py:81 | container:delete | 204 | 403, 404 |
| POST | /v1/containers/{container_id}/secrets | barbican/api/controllers/containers.py:243 | container_secret:post | 201 | 400, 403, 404, 409, 415 |
| DELETE | /v1/containers/{container_id}/secrets | barbican/api/controllers/containers.py:294 | container_secret:delete | 204 | 400, 403, 404 |
| GET/PATCH/PUT/DELETE | /v1/containers/{container_id}/acl | barbican/api/controllers/acls.py:223 … barbican/api/controllers/acls.py:351 | container_acls:get / :put_patch / :delete | 200 | 400, 403, 404, 415 |
| GET | /v1/containers/{container_id}/consumers | barbican/api/controllers/consumers.py:109 | container_consumers:get | 200 | 403, 404 |
| POST | /v1/containers/{container_id}/consumers | barbican/api/controllers/consumers.py:148 | container_consumers:post | 200 | 400, 403, 404, 415 |
| DELETE | /v1/containers/{container_id}/consumers | barbican/api/controllers/consumers.py:173 | container_consumers:delete | 200 | 400, 403, 404, 415 |
| GET | /v1/containers/{container_id}/consumers/{consumer_id} | barbican/api/controllers/consumers.py:64 | consumer:get | 200 | 403, 404 |
| GET | /v1/orders | barbican/api/controllers/orders.py:128 | orders:get | 200 | 403 |
| POST | /v1/orders | barbican/api/controllers/orders.py:163 | orders:post | 202 | 400, 403, 415 |
| PUT | /v1/orders | barbican/api/controllers/orders.py:156 | orders:put | — | 405 always |
| GET | /v1/orders/{order_id} | barbican/api/controllers/orders.py:80 | order:get | 200 | 403, 404 |
| DELETE | /v1/orders/{order_id} | barbican/api/controllers/orders.py:87 | order:delete | 204 | 403, 404 |
| GET | /v1/transport_keys | barbican/api/controllers/transportkeys.py:104 | transport_keys:get | 200 | 403 |
| POST | /v1/transport_keys | barbican/api/controllers/transportkeys.py:143 | transport_keys:post | 201 | 400, 403, 415 |
| GET | /v1/transport_keys/{transport_key_id} | barbican/api/controllers/transportkeys.py:57 | transport_key:get | 200 | 403, 404 |
| DELETE | /v1/transport_keys/{transport_key_id} | barbican/api/controllers/transportkeys.py:69 | transport_key:delete | 204 | 403, 404 |
| GET | /v1/quotas | barbican/api/controllers/quotas.py:50 | quotas:get | 200 | 403 |
| GET | /v1/project-quotas | barbican/api/controllers/quotas.py:133 | project_quotas:get | 200 | 403 |
| GET | /v1/project-quotas/{project_id} | barbican/api/controllers/quotas.py:75 | project_quotas:get | 200 | 403, 404 |
| PUT | /v1/project-quotas/{project_id} | barbican/api/controllers/quotas.py:87 | project_quotas:put | 204 | 400, 403, 415 |
| DELETE | /v1/project-quotas/{project_id} | barbican/api/controllers/quotas.py:102 | project_quotas:delete | 204 | 403, 404 |
| GET | /v1/secret-stores | barbican/api/controllers/secretstores.py:165 | secretstores:get | 200 | 403, 404 |
| any | /v1/secret-stores/global-default | barbican/api/controllers/secretstores.py:187 | secretstores:get_global_default | 200 | 403, 404 |
| any | /v1/secret-stores/preferred | barbican/api/controllers/secretstores.py:202 | secretstores:get_preferred | 200 | 403, 404 |
| GET | /v1/secret-stores/{secret_store_id} | barbican/api/controllers/secretstores.py:124 | secretstore:get | 200 | 403, 404, 405 |
| POST | /v1/secret-stores/{secret_store_id}/preferred | barbican/api/controllers/secretstores.py:90 | secretstore_preferred:post | 204 | 403, 404 |
| DELETE | /v1/secret-stores/{secret_store_id}/preferred | barbican/api/controllers/secretstores.py:71 | secretstore_preferred:delete | 204 | 403, 404 |
Undocumented in doc/source/api/reference (_rewrite/api-ref-gaps_stable-2026.2.json): /, /v1, /healthcheck, the four transport-key operations, GET …/consumers/{consumer_id} (secret and container), and PUT /v1/orders.
Versions — GET / and GET /v1
Purpose: version discovery for keystoneauth and the clients. Actor: anyone; the / pipeline has no authentication (etc/barbican/barbican-api-paste.ini:8).
GET /always answers 300. With microversion1.0(no header) the body is the legacy{"versions": {"values": [{"id": "v1", "status": "stable", "links": […], "media-types": […]}]}}; with1.1+ it is{"versions": [{"id": "v1", "status": "CURRENT", "min_version": "1.0", "max_version": "1.2", "links": […]}]}(barbican/api/controllers/versions.py:189,barbican/api/controllers/versions.py:94). python-barbicanclient negotiates by sendingkey-manager 1.1here (python-barbicanclient:barbicanclient/v1/client.py:131).GET /?buildreturns{"build": "<version>"}(barbican/api/controllers/versions.py:186).Accept: application/json-homeredirects (302) to/v1/(barbican/api/controllers/versions.py:209). OtherAcceptvalues than JSON / JSON-home give406(Pecan content negotiation,barbican/api/controllers/versions.py:181).- The
updatedfield of the legacy representation is never set:version['updated']: cls.last_updatedis an annotation, not an assignment (barbican/api/controllers/versions.py:116).
Secrets
POST /v1/secrets — store a secret (one or two steps)
Purpose: create a secret, with or without its payload. Actor: project member (creator/admin in the legacy roles). Preconditions: quota secrets not reached (barbican/api/controllers/secrets.py:480); Content-Type: application/json (415 otherwise, barbican/api/controllers/secrets.py:472).
Business rules (barbican/common/validators.py:139):
name≤ 255,algorithm,mode,bit_length(integer ≥ 0 [see schema]),secret_type∈symmetric, public, private, passphrase, certificate, opaque(defaultopaque),expirationISO 8601 in the future (barbican/common/validators.py:254).payloadgiven ⇒payload_content_typemandatory and one oftext/plain,text/plain;charset=utf-8,application/octet-stream,application/pkcs8(barbican/plugin/util/mime_types.py:25). Binary types requirepayload_content_encoding: "base64"and a decodable payload;text/plainforbids an encoding (barbican/plugin/util/mime_types.py:58,barbican/plugin/util/translations.py:62).- payload larger than
max_allowed_secret_in_bytes(20,000) →413(barbican/common/validators.py:44). payload_content_typewithoutpayload→400(only allowed inside an order,barbican/common/validators.py:213).creator_idis forced to the token user (barbican/api/controllers/secrets.py:486).transport_key_needed: "true"with no payload asks for a transport key reference in the answer (two-step upload with client-side wrapping,barbican/plugin/resources.py:83).
Flow (synchronous): validate → create/find project → quota → pick a secret-store plugin that store_secret_supports(key_spec) (barbican/plugin/interface/secret_store.py:573) → normalise the payload to base64 (barbican/plugin/util/translations.py:25) → store (for store_crypto: find or create the project KEK, encrypt, insert encrypted_data; 07 § 3) → insert secrets and secret_store_metadata (plugin_name, content_type) (barbican/plugin/resources.py:338).
Outcome: 201, Location and {"secret_ref": "<url>"} (+ transport_key_ref) (barbican/api/controllers/secrets.py:503). Status ACTIVE immediately (barbican/model/models.py:377).
GET /v1/secrets — list
Filters (barbican/api/controllers/secrets.py:393, barbican/model/repositories.py:590): name, alg, mode (SQL LIKE, % wildcards pass through), bits (non-numeric ignored), secret_type, created / updated / expiration ([gt|gte|lt|lte:]ISO-date, comma-separated, contradictory combinations → 400, barbican/api/controllers/secrets.py:303), sort (key[:asc|desc] among algorithm, bit_length, created, expiration, mode, name, secret_type, status, updated, barbican/api/controllers/secrets.py:335), acl_only=true (secrets whose ACL lists the user, any project, barbican/model/repositories.py:640). Expired and deleted secrets are never listed (barbican/model/repositories.py:611). Items carry content_types ({"default": "<type>"}), and from 1.1 consumers.
GET /v1/secrets/{id} — metadata (or deprecated payload)
With Accept: application/json (or none, or */*) returns the secret's fields: secret_ref, name, secret_type, status, algorithm, bit_length, mode, expiration, creator_id, created, updated, content_types and, from 1.1, consumers (barbican/api/controllers/secrets.py:134, barbican/model/models.py:403). ?transport_key_needed=true adds transport_key_id when the store supports transport keys (barbican/api/controllers/secrets.py:149). Any other Accept returns the decrypted payload, as /payload does, but logs a deprecation warning (barbican/api/controllers/secrets.py:128).
GET /v1/secrets/{id}/payload — decrypt
Purpose: return the secret in clear. Actor: secret:decrypt (the legacy audit role cannot). Rules: Accept absent → application/octet-stream; */* → the stored content type; an unsupported Accept → 406 Wrong payload content-type (barbican/api/controllers/secrets.py:165, barbican/plugin/resources.py:123). text/plain returns UTF-8 text (406 if the bytes are not UTF-8), binary types return the raw bytes (barbican/plugin/util/translations.py:89). No payload yet → 404 Not Found. Sorry but your secret has no payload. (barbican/api/controllers/secrets.py:173). Optional transport-key wrapping: trans_wrapped_session_key + transport_key_id query parameters (barbican/api/controllers/secrets.py:176). Any method other than GET → 405 (barbican/api/controllers/secrets.py:204).
PUT /v1/secrets/{id} — second step: upload the payload
Content-Type must be text/plain or application/octet-stream (415 otherwise, including JSON); body = raw payload (Content-Encoding: base64 for base64 text); empty → 400 No data supplied to process.; too big → 413; the secret already has data → 409 Secret already has data, cannot modify it. (barbican/api/controllers/secrets.py:221). Answer 204.
DELETE /v1/secrets/{id}
From 1.2, refuses with 400 Secret cannot be deleted as it has consumers. unless force is a true boolean string (true, 1, yes…; anything else → 400) (barbican/api/controllers/secrets.py:264). Deletes the external-store object if any (barbican/plugin/resources.py:251), soft-deletes the secret and its children (store metadata, user metadata, encrypted data, container links; ACLs are hard-deleted) (barbican/model/models.py:379), then deletes the consumer rows (barbican/api/controllers/secrets.py:285). The project used for the deletion is the secret's project, not the caller's (barbican/api/controllers/secrets.py:281).
stateDiagram-v2
[*] --> ACTIVE_no_payload: POST /v1/secrets without payload
[*] --> ACTIVE: POST /v1/secrets with payload / order completed
ACTIVE_no_payload --> ACTIVE: PUT /v1/secrets/{id}
ACTIVE --> expired: expiration reached (hidden from GET/list)
ACTIVE --> deleted: DELETE (soft delete; 1.2 needs no consumers or force)
expired --> deleted: barbican-manage db clean --soft-delete-expired-secrets
deleted --> [*]: barbican-manage db clean (hard delete after min-days)Secret metadata (user key/value pairs)
GET …/metadata→{"metadata": {k: v}};PUT …/metadatareplaces all pairs ({"metadata": {…}}, values strings) →201 {"metadata_ref": …}(barbican/api/controllers/secretmeta.py:67).POST …/metadata{"key", "value"}adds one pair; existing key →409; key must match^[A-Za-z0-9_-]*$(400 Invalid Key. Key must be URL safe.) and is lower-cased (barbican/common/validators.py:400). Answer201,Location: …/metadata/<key>.PUT …/metadata/{key}requires the body key to equal the URL key (409,barbican/api/controllers/secretmeta.py:155); unknown key →404 Secret metadata not found.;DELETE …/metadata/{key}→204, also for a key that does not exist (bulkDELETE … WHERE key=…without a check,barbican/model/repositories.py:898).
Secret consumers (from 1.1 in representations)
POST …/consumers {"service", "resource_type", "resource_id"} registers a use; it is idempotent (an existing, even soft-deleted, triple is revived, barbican/model/repositories.py:2517) and counts against the consumers quota. It answers 200 with the secret representation (not 201) (barbican/api/controllers/consumers.py:318). DELETE …/consumers with the same body removes it; only the consumer's project or the secret's project may do it (403, barbican/api/controllers/consumers.py:366). Unknown triple → 404 Consumer not found.
ACLs (secrets and containers)
Format: {"read": {"users": [...], "project-access": true|false}}; only read is accepted by the schema (barbican/common/validators.py:569). GET returns the ACLs with created/updated, or the implicit default {"read": {"project-access": true}} (barbican/api/controllers/acls.py:42). PUT replaces (missing project-access → true, operations not given are deleted), PATCH merges (only given fields change); both answer 200 {"acl_ref": "<secret_ref>/acl"} (barbican/api/controllers/acls.py:72, barbican/api/controllers/acls.py:124). DELETE removes all ACLs → 200 (barbican/api/controllers/acls.py:186). project-access: false makes the entity private: only its creator and the listed users can read it (policy secret_non_private_read, barbican/common/policies/base.py:106). The ACL data feeding the policy is built per request (barbican/api/controllers/__init__.py:198).
Containers
POST /v1/containers {"name", "type": generic|rsa|certificate, "secret_refs": [{"name", "secret_ref"}]} (barbican/common/validators.py:648): reference names unique, secret ids unique, every secret_ref must start with this service's base URL (400 … does not match the configured hostname, barbican/common/validators.py:713); rsa allows public_key, private_key (required) and private_key_passphrase; certificate requires certificate and allows private_key, private_key_passphrase, intermediates (barbican/common/validators.py:732). Each secret must exist in the caller's project (404 Secret provided for '<name>' doesn't exist., barbican/api/controllers/containers.py:198). Quota containers. Answer 201 {"container_ref"}.
GETreturnscontainer_ref, name, type, status, secret_refs[{name, secret_ref}], consumers[{name, URL}], creator_id, created, updated(barbican/model/models.py:878).DELETEsoft-deletes the container and deletes its consumer rows (barbican/api/controllers/containers.py:81).PUT→405.POST/DELETE …/secretsadd or remove a{name, secret_ref}link, only forgenericcontainers (400,barbican/api/controllers/containers.py:246); duplicate →409.- Container consumers:
{"name", "URL"}, same idempotency and ownership rules as secret consumers; the answer is the container representation with200and a wrongLocationheader (…/v1/consumers/<container_id>/consumers,barbican/common/hrefs.py:46).
Orders — asynchronous key generation
Purpose: ask Barbican to generate a symmetric key (type: key) or an RSA/DSA key pair (type: asymmetric). Body {"type", "meta": {"name", "algorithm", "bit_length", "mode", "payload_content_type", "expiration", "passphrase"}} (barbican/common/validators.py:420): algorithm and bit_length required; bit_length multiple of 8 (EC: 256/384/521); payload forbidden; certificate orders → 400 Feature not implemented … (barbican/common/validators.py:560).
Flow: quota orders → insert order PENDING → commit → cast process_type_order(order_id, project_id, request_id) (barbican/api/controllers/orders.py:197) → 202 {"order_ref"}. The worker (or the API itself when [queue] enable=False) generates the key through the secret-store plugin (barbican/tasks/resources.py:245): a key order creates one secret; an asymmetric order creates private, public and optional passphrase secrets plus an rsa/dsa container (barbican/plugin/resources.py:191). Then the order becomes ACTIVE with secret_ref or container_ref, or ERROR with error_status_code and error_reason (barbican/tasks/resources.py:189).
stateDiagram-v2
[*] --> PENDING: POST /v1/orders (202)
PENDING --> ACTIVE: worker generated the secret(s)
PENDING --> ERROR: generation failed (error_status_code, error_reason)
PENDING --> PENDING: retry scheduled (order_retry_tasks; unused by key/asymmetric)
ACTIVE --> deleted: DELETE /v1/orders/{id} (generated secrets stay)
ERROR --> deleted: DELETE /v1/orders/{id}GET /v1/orders?meta=… filters with a substring match on the serialized JSON meta column (LIKE '%…%', barbican/model/repositories.py:1073). Order lookup uses the caller's project: another project's order is 404 even for an admin (barbican/api/controllers/orders.py:113).
Transport keys
Used with plugins that support client-side wrapping (Dogtag). POST /v1/transport_keys {"plugin_name", "transport_key"} (admin, 201); GET lists references (?plugin_name=), GET/{id} returns the stored key as a raw string with Content-Type: application/json (barbican/api/controllers/transportkeys.py:63); DELETE/{id} → 204.
Quotas
GET /v1/quotas→ effective quotas of the caller's project{"quotas": {"secrets", "orders", "containers", "consumers", "cas"}}: project values, falling back to[quotas] quota_*(default-1= unlimited) (barbican/common/quota.py:76)./v1/project-quotas[/{project_id}](service admin): list{"project_quotas": [{"project_id", "project_quotas": {…}}], "total"}, get one (404 Project quotas not found.when none configured),PUT {"project_quotas": {"secrets": n, …}}→204(body read by Pecan intokwargs,barbican/api/controllers/quotas.py:93),DELETE→204. Any project id string is accepted and auto-creates aprojectsrow (barbican/common/quota.py:107). Value semantics:<0unlimited,0disabled,ncap (barbican/common/quota.py:178).
Secret stores (multiple back ends)
Only with [secretstore] enable_multiple_secret_stores = True; otherwise every operation answers 404 Not Found. Multiple backends support is not enabled … (barbican/api/controllers/secretstores.py:168). Stores are configured in [secretstore:<suffix>] sections (secret_store_plugin, crypto_plugin, global_default) and synchronised to the secret_stores table at API start (barbican/plugin/util/multiple_backends.py:106). A project may set a preferred store (POST …/{id}/preferred, 204); new secrets of the project then go to that store (barbican/plugin/util/multiple_backends.py:262). Representation: {"secret_store_ref", "name", "secret_store_plugin", "crypto_plugin", "global_default", "status", "created", "updated"} (barbican/api/controllers/secretstores.py:45).
Source: reverse-spec-barbican/01-rest-api_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.