{
  "schema": "aaes/capability-pack/1",
  "provider": "vault",
  "version": "1.1.0",
  "description": "HashiCorp Vault: list secret names, read secret values, write new versions, soft-delete or permanently destroy versions, delete all metadata and history, and revoke leases.",
  "notes": "The endpoint host 'vault-cluster.invalid' is a reserved non-production placeholder (an RFC 2606 reserved TLD): substitute the customer's own Vault cluster address before enabling, in every endpoint, in the resource locator and in every egress allowlist entry. The allowlist matches exactly, so endpoints and allowlist must be substituted together, and substitution is a required deployment step — the reserved host being allowlisted does not make an unsubstituted pack safe to enable.\n\nMOUNT and PATH in the endpoint paths are placeholders: MOUNT binds to the KV version 2 secrets engine mount path (for example 'secret') and PATH to the secret path; bind both before enabling. This pack registers only KV v2 engine paths — KV v1 mounts use different paths (no data/metadata segments) and are not registered.\n\nApproval classification: the KV v2 secret-value read sits at the minimum R0 tier for read like every read in the library; assess confidentiality through data classification and policy; but its data class is an AAES classification judgment, not a provider fact: the registered paths are unrestricted over stored content, and a secrets engine can hold personal credentials and personal data, so the value-touching capabilities (secret read, write, delete, version destroy and the metadata delete, which permanently removes all version data) are classified pii, the strictest applicable handling class; the key-name list read discloses no values, remains internal, and also sits at the minimum R0 tier.\n\nVersioned KV v2 writes are registered as create and update at the minimum R2 tier: a POST creates a new version, and prior versions are retained only up to the mount's configured max_versions — retention-driven permanent removal of the oldest versions is possible, so the pack makes no blanket reversibility claim for writes. Soft deletes, version destroys, metadata deletes and lease revocations are registered at the minimum R3 tier: delete is one of the designated actions requiring human approval and Vault's own documentation describes destroy as permanently removing version data and the metadata delete as permanently deleting the key metadata and all version data.\n\nSoft delete of the latest version (DELETE on the data path) can be restored through the separate undelete path. That provider restoration feature does not remove the R3 approval requirement. Lease revocation is registered as delete at the minimum R3 tier: according to the reviewed leases reference the default behavior QUEUES the revocation (immediate completion only with the optional sync=true body parameter), but once revocation completes the dynamic credential or secret the lease tracks is revoked at the backing system and requires a new credential to restore access — treating it as delete follows the pack standard that revoke equivalents are irreversible.\n\nDocumented exclusions: this pack does not register auth/token/create and auth method login endpoints (token issuance is an external custody process, not a governed capability — see the credential note), /sys/leases/revoke-prefix and /sys/leases/revoke-force (sudo-gated mass revocation the Vault documentation itself describes as potentially very dangerous), /sys/policy and /sys/mounts and /sys/auth administration (cluster configuration, not secret operations), seal/unseal and key-rotation operator endpoints, the transit secrets engine (encrypt/decrypt/sign operations are execute-effect capabilities needing their own review), and cubbyhole access.\n\nVault considers PUT and POST synonyms on write paths; this pack registers the documented verb per operation (POST for writes, PUT for destroy) and does not register the alternates. Enterprise namespaces (the X-Vault-Namespace header) are not registered: bind the token's policy and the substituted paths to a single namespace at install.",
  "credential": {
    "secret_ref_id": "secret-ref-vault-service-token",
    "custody": "customer_vault",
    "inject": "header",
    "inject_name": "X-Vault-Token",
    "note": "A Vault service token for the customer's own cluster, held in the client's own secret manager. Token issuance is external: an out-of-band deployment process authenticates to a Vault auth method (for example an AppRole login) and stores the resulting service token (an 'hvs.'-prefixed string) under this reference; the pack creates no token and the injector performs no login — it injects the stored value verbatim in the X-Vault-Token header on every call.\n\nVault also accepts the same token as 'Authorization: Bearer <token>'; this pack registers X-Vault-Token only, and a deployment must not store both forms under one reference. Every non-root token has a TTL: prefer renewable service tokens with a short TTL, with renewal owned by the external custody job — an expired token fails closed with a 403. Prefer service tokens over batch tokens, which Vault documents as not revocable.\n\nNever store a root token under this reference. Revocation behavior: DISABLING an auth method mount (DELETE /sys/auth/:path, the API form of 'vault auth disable') immediately revokes ALL access tokens generated via that mount — Vault's own command documentation states they are immediately revoked and the call blocks until revocation completes. Deleting a ROLE within a mount is a different operation and does not revoke tokens already issued under it: those remain valid until their TTL expires, so TTLs must be short, and the per-token cut-offs are explicit revocation (auth/token/revoke) or revoking a parent token, which revokes its children and their leases.\n\nLeast privilege: the token's ACL policy should grant exactly the registered operations on the bound mount and paths — read on the concrete MOUNT/data/PATH paths, list and read on MOUNT/metadata/PATH, create AND update on MOUNT/data/PATH (according to the reviewed KV v2 reference a write requires the create capability when the key does not yet exist and the update capability when it does) and update on MOUNT/destroy/PATH, delete on MOUNT/data/PATH and MOUNT/metadata/PATH as the enabled capabilities require (the registered soft-delete and metadata-delete operations are HTTP DELETEs and need the delete ACL capability), and update on sys/leases/revoke only if the lease capability is enabled.\n\nSudo permissions: the single-lease revoke endpoint this pack registers carries NO sudo requirement — the sudo-gated lease endpoints are the LIST on sys/leases/lookup/:prefix, sys/leases/revoke-prefix and sys/leases/revoke-force, none of which this pack registers — so the token needs no sudo capability at all, and granting none keeps it below the mass-revocation paths. Grant no wildcard path access: bind every grant to the concrete bound paths rather than MOUNT/data/*-style wildcards.\n\nNo metadata-update permission is needed: this pack registers no metadata write, only metadata list/read and metadata delete.\n\nExecutor hygiene: the token must live only in executor memory for the lifetime of a call, must never be written to logs, traces or diagnostics, and must not appear in crash dumps; a deployment that cannot meet that bar must not enable the pack."
  },
  "capabilities": [
    {
      "capability_id": "vault.kv.v2.secrets.list",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "vault-cluster.invalid"
      ],
      "endpoint": "https://vault-cluster.invalid/v1/MOUNT/metadata/PATH",
      "method": "LIST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-vault-service-token",
      "inject": "header",
      "inject_name": "X-Vault-Token",
      "actions": [
        "read"
      ],
      "resource_id": "vault-cluster",
      "description": "List key names under a path in the KV v2 engine. This is a collection read of key names only — secret values are not accessible through this endpoint. Vault's own documentation warns that no policy-based filtering is performed on keys and advises not to encode sensitive information in key names; a deployment whose key names are themselves sensitive must raise the tier or class by policy.\n\nThe Vault HTTP API also accepts GET with ?list=true for this operation; this pack registers the LIST method only.\n\nMOUNT and PATH are placeholders to bind before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "vault.kv.v2.secret.read",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "vault-cluster.invalid"
      ],
      "endpoint": "https://vault-cluster.invalid/v1/MOUNT/data/PATH",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-vault-service-token",
      "inject": "header",
      "inject_name": "X-Vault-Token",
      "actions": [
        "read"
      ],
      "resource_id": "vault-cluster",
      "description": "Read a secret's current version or an older version chosen with ?version=. The response contains plaintext secrets such as passwords, private keys, or API tokens. A single allowed call can disclose a credential.\n\nRegistered at the R0 minimum tier for read. That tier does not measure confidentiality risk; restrict policy to named paths and actors. Bind MOUNT and PATH before enabling. The caller controls the optional version parameter.",
      "requires_review": true
    },
    {
      "capability_id": "vault.kv.v2.secret.write",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R2",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "vault-cluster.invalid"
      ],
      "endpoint": "https://vault-cluster.invalid/v1/MOUNT/data/PATH",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-vault-service-token",
      "inject": "header",
      "inject_name": "X-Vault-Token",
      "actions": [
        "create",
        "update"
      ],
      "resource_id": "vault-cluster",
      "description": "Write a new version of a secret. Prior versions survive only within the mount's max_versions setting. Older versions beyond that limit are permanently removed; max_versions=1 retains no prior version. Registered at R2 for create and update, without an unconditional retention or restoration promise.\n\nThe body accepts an unrestricted data map. The registration restricts the path, rather than what secret content is written. It also accepts optional cas; when the mount enables cas_required, a write without the current version number fails.\n\nBind MOUNT and PATH before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "vault.kv.v2.secret.delete",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "vault-cluster.invalid"
      ],
      "endpoint": "https://vault-cluster.invalid/v1/MOUNT/data/PATH",
      "method": "DELETE",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-vault-service-token",
      "inject": "header",
      "inject_name": "X-Vault-Token",
      "actions": [
        "delete"
      ],
      "resource_id": "vault-cluster",
      "description": "Soft-delete a secret's latest version, so reads no longer return it. Vault retains the underlying data and supports restoration through a separate privileged undelete call.\n\nDelete is registered at R3 because it immediately affects consumers of the secret. The AAES approval category does not mean the provider lacks a restoration path. On supported AAES-mediated execution paths, delete requires approval by an authorized person; this does not prevent calls through bypass or observation-only paths.\n\nBind MOUNT and PATH before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "vault.kv.v2.versions.destroy",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "vault-cluster.invalid"
      ],
      "endpoint": "https://vault-cluster.invalid/v1/MOUNT/destroy/PATH",
      "method": "PUT",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-vault-service-token",
      "inject": "header",
      "inject_name": "X-Vault-Token",
      "actions": [
        "delete"
      ],
      "resource_id": "vault-cluster",
      "description": "Permanently remove the specified versions' data for a secret. Vault's own documentation describes this as permanently removing version data from the key-value store — there is no undelete for a destroyed version. Registered as delete at the minimum R3 tier; the request body names the versions array, and this registration does not restrict which versions a caller may name.\n\nMOUNT and PATH are placeholders to bind before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "vault.kv.v2.metadata.delete",
      "version": "1.1.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "vault-cluster.invalid"
      ],
      "endpoint": "https://vault-cluster.invalid/v1/MOUNT/metadata/PATH",
      "method": "DELETE",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-vault-service-token",
      "inject": "header",
      "inject_name": "X-Vault-Token",
      "actions": [
        "delete"
      ],
      "resource_id": "vault-cluster",
      "description": "Delete a secret's metadata and ALL of its version data: Vault documents this as permanently deleting the key metadata and all version data — the whole key's history is removed. This is the most destructive KV operation this pack registers, at the minimum R3 tier for delete. Because it destroys every stored version of the secret values, it carries the same pii class as the other value-touching capabilities (secret read, write, delete and version destroy); pack version 1.0.0 registered it as internal.\n\nMOUNT and PATH are placeholders to bind before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "vault.leases.revoke",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "vault-cluster.invalid"
      ],
      "endpoint": "https://vault-cluster.invalid/v1/sys/leases/revoke",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-vault-service-token",
      "inject": "header",
      "inject_name": "X-Vault-Token",
      "actions": [
        "delete"
      ],
      "resource_id": "vault-cluster",
      "description": "Revoke one lease and its associated dynamic credential or secret. By default, Vault queues revocation; the call does not prove immediate completion in the backing system. Setting sync=true waits for revocation to complete. The registration permits either choice.\n\nOnce complete, the credential stops working wherever it is held. Restoring access requires a new credential, so the capability is registered as a delete equivalent at R3.\n\nThe caller supplies lease_id in the body; the registration does not restrict that selection. Mass revocation through /sys/leases/revoke-prefix and /sys/leases/revoke-force is excluded. Both require sudo; Vault describes revoke-force as potentially very dangerous.",
      "requires_review": true
    }
  ],
  "resources": [
    {
      "resource_id": "vault-cluster",
      "kind": "api",
      "locator": "https://vault-cluster.invalid/v1",
      "data_class": "pii",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    }
  ],
  "review": {
    "state": "library",
    "date": "2026-10-22",
    "sources": [
      "https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2 (retrieved 2026-09-21)",
      "https://developer.hashicorp.com/vault/api-docs/system/leases (retrieved 2026-09-21)",
      "https://developer.hashicorp.com/vault/api-docs (retrieved 2026-09-21)",
      "https://developer.hashicorp.com/vault/docs/concepts/tokens (retrieved 2026-09-21)",
      "https://developer.hashicorp.com/vault/api-docs/system/auth (retrieved 2026-09-21)",
      "https://developer.hashicorp.com/vault/docs/commands/auth/disable (retrieved 2026-09-21)",
      "https://developer.hashicorp.com/vault/docs/concepts/lease (retrieved 2026-09-21)"
    ],
    "reviewer_id": "owner-delegated"
  }
}
