> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cowboy.inc/llms.txt
> Use this file to discover all available pages before exploring further.

# cowboy secrets

> Register, encrypt, and manage CIP-24 CBSS secret versions and policies

## Synopsis

```bash theme={null}
cowboy secrets [--rpc-url <url>] [--private-key <path>] <subcommand>

cowboy secrets key-hash --account <address> --key <name>
cowboy secrets set [KEY] [VALUE] [--value-file <path>] [--actor <address>]... [--tee-required] [flags] [--json]
cowboy secrets finalize [KEY] [VALUE] --version <n> [--value-file <path>] [flags] [--json]
cowboy secrets request-account-dkg [--force-rekey] [--json]
cowboy secrets expire-account-dkg --account <address> [--json]
cowboy secrets policy --key-hash <hex> --version <n> [--actor <address>]... [--tee-required] [--json]
cowboy secrets grant-actor --key-hash <hex> --version <n> --actor <address> [--prepare --max-fee-wei <wei> | --json]
cowboy secrets delete-version --key-hash <hex> --version <n> [--yes] [--json]
cowboy secrets delete --key-hash <hex> [--yes] [--json]
cowboy secrets reshare [--json]
cowboy secrets list [--account <address>] [--json]
cowboy secrets account-key [--account <address>] [--json]
cowboy secrets encrypt --account <address> --key <name> (--value <str> | --value-file <path>) [--ingest-endpoint <url>]
cowboy secrets build-tx --account <address> --kind <kind> [flags]
cowboy secrets submit-signed --unsigned-tx-hex <hex> --signature <slot-json> [--prepare]
```

`cowboy secrets` drives the CIP-24 Cowboy Blockchain Secrets Service (CBSS): it registers encrypted secret versions on chain, manages which actors may read them, and runs the account-scoped DKG that produces the release key the secrets are wrapped to. The secret value itself is never stored on chain -- only a key hash, a pointer to the encrypted CBFS payload, and the wrapped data-encryption key.

Write commands sign with the key found by [key auto-discovery](https://docs.cowboy.inc/cli-specs/key-auto-discovery.md). `key-hash`, `encrypt`, `build-tx`, and `submit-signed` never touch a private key; `list` and `account-key` read one only to default `--account` to its address.

With `--json`, a write reports one versioned JSON result and waits for its transaction to commit first.

**Parent flags:**

| Flag | Default | Description |
| - | - | - |
| `--rpc-url` | Auto-discovered | RPC URL (`--rpc-url`, then `COWBOY_RPC_URL`, then `.cowboy/config.json`, then `http://localhost:4000`) |
| `--private-key` | Auto-discovered | Private key file path, or set `COWBOY_PRIVATE_KEY_FILE` / `COWBOY_PRIVATE_KEY` |

## Subcommands

### `cowboy secrets key-hash`

Compute a CBSS key hash locally from an owner account and a key name, without a signing key or network request. Prints the hash as `0x` hex.

**Flags:**

| Flag | Default | Description |
| - | - | - |
| `--account` | Required | Secret owner address (20-byte hex) |
| `--key` | Required | Plaintext secret key name (not its value); must not be empty |

***

### `cowboy secrets set`

Encrypt and submit a new secret version, or submit pre-encrypted fields.

**Behavior:**

1. Resolve the plaintext value. `--value-file <path>` reads a file (`-` reads stdin); a trailing newline is stripped. Passing `VALUE` inline is refused when stdin is a terminal, because it would land in shell history.
2. If any of `--committee-n`, `--committee-t`, or `--preferred-proxy` is given, the command takes the per-secret committee-override path: both `--committee-n` and `--committee-t` are required, `t` must be in `1..=n`, and if `--preferred-proxy` is supplied it must be repeated exactly `n` times.
3. **Plaintext mode** (`KEY` or `VALUE` given): derive the owner account from the signing key and compute the key hash from the account and `KEY`. Fetch the account's release key from the RPC; if none exists yet, submit `RequestAccountDkg` and wait up to 300 seconds (following finalized `cbss.committee.rotated` events, with backoff) for the committee to commit one. Resolve the next version number, IBE-encrypt the value to the release key, place the ciphertext at the CBFS location (`--cbfs-output` / `--cbfs-uri`; see `--help`), then submit `SetSecret` with the policy and wait for it to commit.
4. **Pre-encrypted mode** (no `KEY`/`VALUE`): requires `--key-hash`, `--recipient-account`, `--cbfs-uri`, `--cbfs-envelope`, `--wrap-epoch`, `--wrapped-dek-ciphertext`, `--wrapped-dek-nonce`, `--wrapped-dek-ephemeral-u`, and `--wrapped-dek-aad`, and builds `SetSecret` from them directly.
5. Sign and `POST /submit`; print the transaction hash and the node response.

**Flags:**

| Flag | Default | Description |
| - | - | - |
| `KEY` | None | Secret key name (plaintext mode) |
| `VALUE` | None | Plaintext value (refused on a tty; prefer `--value-file`) |
| `--value-file` | None | File holding the plaintext value, or `-` for stdin |
| `--cbfs-output` | None | Local mounted-CBFS output path for the ciphertext JSON |
| `--cbfs-uri` | None | CBFS URI of the encrypted payload |
| `--key-hash` | None | 32-byte key hash (pre-encrypted mode) |
| `--recipient-account` | None | Account release-key recipient (pre-encrypted mode) |
| `--cbfs-envelope` | None | CBFS envelope bytes, hex (pre-encrypted mode) |
| `--wrap-epoch` | None | Wrap epoch used in the IBE AAD (pre-encrypted mode) |
| `--wrapped-dek-ciphertext` | None | IBE-wrapped DEK ciphertext, hex |
| `--wrapped-dek-nonce` | None | 12-byte AES-GCM nonce, hex |
| `--wrapped-dek-ephemeral-u` | None | 96-byte compressed G2 ephemeral point, hex |
| `--wrapped-dek-aad` | None | Wrapped-DEK AAD bytes, hex |
| `--actor` | None | Actor allowed to read the secret; repeatable. Empty means no allowlist |
| `--tee-required` | `false` | Require a TEE-capable runner for release |
| `--committee-n` | None | Committee size override for this version |
| `--committee-t` | None | Threshold override for this version |
| `--preferred-proxy` | None | Preferred CBSS proxy id; repeatable |
| `--json` | `false` | Emit one versioned JSON result |

**Example:**

```bash theme={null}
$ cowboy secrets set OPENAI_API_KEY --value-file ./openai.key --actor 0x7a3B...F92E
  Transaction hash: 0xabc123...
CBSS encrypted secret submitted: <node response>
Encrypted secret 'OPENAI_API_KEY' version 1 to /mnt/cbfs/secrets/....json
```

***

### `cowboy secrets finalize`

Finalize a pending secret-specific CBSS version with encrypted fields.

**Behavior:**

1. Resolve the plaintext value as in `set`.
2. If `KEY` or `VALUE` is given, encrypt the value for the pending version and submit `FinalizeSecretVersion`.
3. Otherwise require `--key-hash`, `--cbfs-uri`, `--cbfs-envelope`, `--wrap-epoch`, and the four `--wrapped-dek-*` flags, and build `FinalizeSecretVersion` from them.

**Flags:**

| Flag | Default | Description |
| - | - | - |
| `KEY` | None | Secret key name (plaintext mode) |
| `VALUE` | None | Plaintext value (refused on a tty; prefer `--value-file`) |
| `--value-file` | None | File holding the plaintext value, or `-` for stdin |
| `--version` | Required | Pending secret version to finalize |
| `--cbfs-output` | None | Local mounted-CBFS output path for the ciphertext JSON |
| `--key-hash` | None | 32-byte key hash |
| `--cbfs-uri` | None | CBFS URI of the encrypted payload |
| `--cbfs-envelope` | None | CBFS envelope bytes, hex |
| `--wrap-epoch` | None | Wrap epoch used in the IBE AAD |
| `--wrapped-dek-ciphertext` | None | IBE-wrapped DEK ciphertext, hex |
| `--wrapped-dek-nonce` | None | 12-byte AES-GCM nonce, hex |
| `--wrapped-dek-ephemeral-u` | None | 96-byte compressed G2 ephemeral point, hex |
| `--wrapped-dek-aad` | None | Wrapped-DEK AAD bytes, hex |
| `--json` | `false` | Emit one versioned JSON result |

***

### `cowboy secrets request-account-dkg`

Request an account-scoped CBSS DKG ceremony for the signing account.

**Flags:**

| Flag | Default | Description |
| - | - | - |
| `--force-rekey` | `false` | Start a fresh next-epoch DKG even when the account already has a release key |
| `--json` | `false` | Emit one versioned JSON result; waits for the transaction to commit |

**Example:**

```bash theme={null}
$ cowboy secrets request-account-dkg
  Transaction hash: 0xdef789...
CBSS secrets command submitted: <node response>
```

***

### `cowboy secrets expire-account-dkg`

Expire a timed-out account-scoped DKG and settle the requester refund.

**Flags:**

| Flag | Default | Description |
| - | - | - |
| `--account` | Required | Account whose pending DKG should be expired |
| `--json` | `false` | Emit one versioned JSON result; waits for the transaction to commit |

***

### `cowboy secrets policy`

Replace the policy of one secret version.

**Flags:**

| Flag | Default | Description |
| - | - | - |
| `--key-hash` | Required | 32-byte key hash (hex, `0x` optional) |
| `--version` | Required | Secret version |
| `--actor` | None | Actor allowed to read; repeatable. Empty means no allowlist |
| `--tee-required` | `false` | Require a TEE-capable runner for release |
| `--json` | `false` | Emit one versioned JSON result; waits for the transaction to commit |

***

### `cowboy secrets grant-actor`

Atomically add one reader, preserving the current policy and TEE requirement.

**Flags:**

| Flag | Default | Description |
| - | - | - |
| `--key-hash` | Required | 32-byte key hash |
| `--version` | Required | Secret version |
| `--actor` | Required | Actor address to add |
| `--prepare` | `false` | Sign and print the exact transaction without submitting it. Requires `--max-fee-wei` |
| `--max-fee-wei` | None | Maximum permitted transaction fee in wei; the prepared transaction is refused if its maximum fee exceeds it. Requires `--prepare` |
| `--json` | `false` | Emit one versioned JSON result; waits for the transaction to commit. Conflicts with `--prepare` |

***

### `cowboy secrets delete-version`

Delete one secret version.

**Behavior:**

1. Unless `--yes` is passed, print a summary and require the operator to type `yes`. If stdin is not a terminal and `--yes` is absent, the command refuses.
2. Submit `DeleteSecretVersion`.

**Flags:**

| Flag | Default | Description |
| - | - | - |
| `--key-hash` | Required | 32-byte key hash |
| `--version` | Required | Secret version |
| `--yes`, `-y` | `false` | Skip the confirmation prompt |
| `--json` | `false` | Emit one versioned JSON result; waits for the transaction to commit |

***

### `cowboy secrets delete`

Delete all active versions of a secret. Uses the same confirmation gate as `delete-version`.

**Flags:**

| Flag | Default | Description |
| - | - | - |
| `--key-hash` | Required | 32-byte key hash |
| `--yes`, `-y` | `false` | Skip the confirmation prompt |
| `--json` | `false` | Emit one versioned JSON result; waits for the transaction to commit |

***

### `cowboy secrets reshare`

Request a proactive secret-sharing reshare for the caller's account-scoped release key. Submits `RequestReshare` with scope `Account(<signer address>)`; the committee completes the reshare and submits `RotateCommittee` as a separate chain event. Its only flag is `--json` (emit one versioned JSON result; waits for the transaction to commit).

***

### `cowboy secrets list`

List the metadata of every secret registered to an account. Read-only; queries `GET /cbss/secrets/{account}` and signs nothing.

**Flags:**

| Flag | Default | Description |
| - | - | - |
| `--account` | Signing key's address | Account whose secrets to list (`0x` hex, 40 chars) |
| `--json` | `false` | Emit one versioned JSON result |

**Example:**

```bash theme={null}
$ cowboy secrets list --account 0x7a3B...F92E
CBSS secrets for 0x7a3B...F92E (1):

  key_hash=0x9f1c...  latest_version=2  active_versions=[1,2]
```

***

### `cowboy secrets account-key`

Show whether an account has a CBSS release key, the key `secrets set` encrypts to. It appears once the DKG from `request-account-dkg` commits. Prints the committee epoch and threshold when ready. Signs nothing.

**Flags:**

| Flag | Default | Description |
| - | - | - |
| `--account` | Signing key's address | Account to check (`0x` hex, 40 chars) |
| `--json` | `false` | Emit one versioned JSON result (`ready`, `committee_epoch`, `threshold`, `committee_size`) |

***

### `cowboy secrets encrypt`

Keyless owner-side encryption for an external (in-browser) signer. IBE-encrypts the value to the account's release key and prints machine JSON on stdout; no private key is touched.

**Behavior:**

1. Fetch the account's release key. If none exists, print `{"needs_dkg":true}` and exit successfully -- run the DKG flow first.
2. Resolve the next version, fetch the chain id, and encrypt.
3. Print a JSON object with `needs_dkg: false`, `chain_id`, `account`, `key_hash`, `version`, `wrap_epoch`, `wrapped_dek`, and `envelope_b64`. With `--ingest-endpoint`, an `ingest` object (`url`, `headers`, `signing_digest`) is included for the caller to sign and POST.

**Flags:**

| Flag | Default | Description |
| - | - | - |
| `--account` | Required | Owner account (`0x` hex, 40 chars) |
| `--key` | Required | Secret key name |
| `--value` | None | Plaintext value (prefer `--value-file`) |
| `--value-file` | None | File holding the plaintext value, or `-` for stdin |
| `--rpc-url` | Parent `--rpc-url` | Override the RPC URL for this call |
| `--ingest-endpoint` | None | cbssd ingest endpoint to build an upload request for |

***

### `cowboy secrets build-tx`

Build an unsigned CBSS transaction for an external signer.

**Behavior:**

1. Fetch the sender nonce and base fee, and decode the closed `/chain-info` response to pin the real chain ID and instance for this operation.
2. Build the instruction for `--kind`: `set-secret` (requires `--key-hash`, `--wrap-epoch`, the four `--wrapped-dek-*` flags, and `--cbfs-uri`), `grant-secret-actor` (requires `--key-hash`, `--version`, and exactly one `--actor`, and accepts nothing else), `request-account-dkg`, or `delete-secret` (requires `--key-hash`).
3. Build version-3 bytes through the canonical codec with a native secp256k1 placeholder slot. Print a JSON object with `unsigned_tx_hex`, the identity-bound `signing_digest`, and `nonce`.

**Flags:**

| Flag | Default | Description |
| - | - | - |
| `--account` | Required | Transaction sender (`0x` hex, 40 chars) |
| `--kind` | Required | `set-secret`, `grant-secret-actor`, `request-account-dkg`, or `delete-secret` |
| `--rpc-url` | Parent `--rpc-url` | Override the RPC URL for this call |
| `--key-hash` | None | 32-byte key hash |
| `--version` | None | Existing secret version |
| `--wrap-epoch` | None | Wrap epoch used in the IBE AAD |
| `--wrapped-dek-ciphertext` | None | IBE-wrapped DEK ciphertext, hex |
| `--wrapped-dek-nonce` | None | 12-byte AES-GCM nonce, hex |
| `--wrapped-dek-ephemeral-u` | None | 96-byte compressed G2 ephemeral point, hex |
| `--wrapped-dek-aad` | None | Wrapped-DEK AAD bytes, hex |
| `--cbfs-uri` | None | CBFS URI of the encrypted payload |
| `--actor` | None | Actor allowed to read; repeatable |
| `--tee-required` | `false` | Require a TEE-capable runner for release |
| `--force-rekey` | `false` | Start a fresh DKG even when a release key exists (`request-account-dkg` only) |
| `--cycles-limit` | `5000000` | Cycles limit for the unsigned transaction |
| `--cells-limit` | `5000000` | Cells limit for the unsigned transaction |
| `--access-limit` | Protocol default | Access limit for the unsigned transaction |

***

### `cowboy secrets submit-signed`

Submit a transaction signed externally.

For the signing handoff, use [`cowboy transaction sign-hash --expect-tx-hex`](cowboy-transaction.mdx#cowboy-transaction-sign-hash) with the unsigned transaction bytes and signing digest. Its output identifies the signed slot and mode; build the canonical SignatureSlot JSON from that output and pass it as `--signature` below.

**Behavior:**

1. Decode `--unsigned-tx-hex` strictly as v3. Parse `--signature` as the canonical SignatureSlot JSON object (`algorithm`, `mode`, `public_key`, `signature`) and require its descriptor to match the frozen primary slot before attaching its signature. At genesis this is algorithm 1, mode 0 or 1, `public_key: null`, and 65-byte low-S `r||s||v` with recovery ID 0 or 1. Untagged signature hex is refused.
2. Fetch the trusted chain identity from `/chain-info`, run the complete structural and signature preflight, and require the recovered signer to equal `tx.from` before submitting. Wrong-version bytes, reserved fields and duplicate signers reject.
3. With `--prepare`, print the complete signed transaction together with `chain_identity` and `slot_index`, then stop. Otherwise `POST /submit` and print `{"tx_hash": ..., "result": ...}` on stdout (progress lines go to stderr).

**Flags:**

| Flag | Default | Description |
| - | - | - |
| `--unsigned-tx-hex` | Required | Unsigned transaction hex from `build-tx` |
| `--signature` | Required | Canonical SignatureSlot JSON; hex byte strings, numeric algorithm/mode, null public key |
| `--prepare` | `false` | Emit the complete signed transaction without submitting it |
| `--rpc-url` | Parent `--rpc-url` | Override the RPC URL for this call |

## Edge Cases

* An inline `VALUE` (or `--value` on `encrypt`) is refused when stdin is a terminal; use `--value-file` or pipe the value on stdin with `--value-file -`.
* Supplying both an inline value and `--value-file` is an error.
* A committee override needs both `--committee-n` and `--committee-t`; `--preferred-proxy` must then be repeated exactly `committee-n` times.
* `set` in plaintext mode auto-requests an account DKG when the account has no release key and waits up to 300 seconds for it; `encrypt` instead prints `{"needs_dkg":true}` and exits.
* `delete` and `delete-version` refuse to run without `--yes` when stdin is not a terminal.
* A `--cycles-limit`, `--cells-limit`, or `--access-limit` of zero is refused by `build-tx` before any RPC call.
* Write commands sign with resource limits from the node's estimate. Only `build-tx` sets explicit limits (5,000,000 cycles and cells by default).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.