Skip to content

HSM Policy JSON File#

The HSM policy is established by a simple JSON file, which configures various security settings, and the rules for which transactions should be signed automatically by the Coldcard. This JSON file is uploaded to the Coldcard, which parses it, and creates a text version of the policy, for you to approve on-screen.

The file has two parts: global settings and a variable number of "rules". The global (top-level) values are as follows:

  • notes: free-form text, shown at top of confirmation message, up to 80 chars long
  • period: (integer) velocity period, in minutes, shared across all the rules. Range: 1..4320 (3 days max).
  • must_log: (bool) fail anything we can't log to SD card
  • never_log: (bool) disable all log generation (even if SD card inserted)
  • warnings_ok: (bool) normally will fail a PSBT that has any warnings, this allows it

  • msg_paths: enable message signing, but only using provided derivation paths (a list) use any to allow any path

  • share_xpubs: same for xpubs sharing, but m is always shared regardless
  • share_addrs: same for addrs, but keyword p2sh in list also allows p2sh address calcs

  • set_sl: (text string) load the storage locker with a value. Length: 16..414 chars.

  • allow_sl: (integer) number of times the storage locker can be read per boot-up (1..100)

  • boot_to_hsm: if defined, six-digit numeric code used to escape boot-to-HSM feature

  • priv_over_ux: (bool) reduce chattiness of status responses in HSM mode, making UX harder

  • rules: list of objects, see below. Each rule is checked in order given in this list.

Each rule consists of these values:

  • whitelist: a list of specific addresses which are allowed as destinations
  • whitelist_opts: dict of extra whitelist options (only valid if whitelist is set). See Whitelist Options below.
  • per_period: total satoshis that can move thru this rule in the period
  • max_amount: max satoshis per transaction, that this rule can apply to (independent of period)
  • users: a list of usernames that are able to approve (N)
  • min_users: number of users (M) needed to approve (from list of users for rule, not system), if missing, assume all users required (M=N)
  • local_conf: bool flag, local user must (also) approve (via 6-digits entered on keypad)
  • wallet: either name of a multisig wallet, or "1" indicating rule only applies to non-multisig
  • min_pct_self_transfer: (float, 0..100) minimum percentage of own input value that must end up in own outputs. See Minimum Self-Transfer below.
  • patterns: list of transaction-shape constraints the PSBT must satisfy. See Transaction Patterns below.

When an element of the rule is missing or has value null, then the restriction does not apply. For example, if whitelist is missing, then the Coldcard will not consider the destination address when considering the rule.

If no rules are defined, then no PSBT will be signed. This can be useful for text message signing applications. On the other hand, an empty rule, meaning just {}, allows any transaction to be signed, so be careful!

Spending Period (Velocity)#

To implement spending limits based on time, the Coldcard requires you to define a period. This period, expressed in minutes, applies to all rules. Any rule with a defined per_period value, will be affected.

The period starts when it is first used. There is no absolute concept of time on the Coldcard (it doesn't have a real time clock). There is only one period, so it will begin as soon as any rule using a per_period limit is applied successfully.

At the end of the period, the totals are reset to zero.

Spending Rules#

Multiple spending rules can be defined. The system scans the rules starting from the first one, and will test each rule. The first rule that is satisfied is applied and following rules are not considered.

We recommend putting the most narrow rules first. Catch-all rules, which might move more money should be later in the list.

Max Transaction Amount#

max_amount per transaction is less useful because a number of transactions could be put together to "work around" this rule. However, if there is natural rate-limiting in your system, for example, by using a local operator to enter a code each time, then this is still helpful. Use local_conf=True to enable the local code when the rule is applied.

Per-Period Limit#

per_period is the total amount, in Satoshis, that can be spent using this rule in the period.

Authorizing Users#

You can list username in the users field. If defined the min_users controls how many of those are required. By default (if min_users isn't defined), all users listed must confirm the operation. You can achieve 2-of-5 and similar setups using min_users. All users listed must already be defined on the Coldcard before the policy is activated.

The local_conf boolean enables the local PIN code, and requires it for transactions that take advantage of the rule. It can be combined with the user list, or used by itself.

Limit to Named Wallet#

The wallet field can be omitted, or set to the name of a multisig wallet. If set to the string "1", it indicates this rule only applies to the non-multisig wallet.

Whitelist Address#

You may specify a list of addresses in the whitelist field. The Coldcard will only apply the rule if all destination addresses of the PSBT transaction are included in the whitelist. This is a powerful feature when your target wallets that you control in the whitelist, such as emergency cold wallets.

Whitelist Options#

The optional whitelist_opts dict (only valid when a whitelist is also set) tunes how the whitelist is applied:

  • mode: "BASIC" (default) or "ATTEST".
    • BASIC — every non-change destination address must literally appear in whitelist.
    • ATTEST — every non-change output must carry a Coinkite-proprietary attestation signature (PSBT proprietary key with prefix COINKITE and subtype 0) over the consensus-encoded txout (8-byte little-endian value followed by the scriptPubKey). The recovered public key must hash to one of the addresses listed in whitelist. This lets a third-party signer (whose pubkey/address is whitelisted) co-sign destination policy out of band, without you having to hardcode every possible destination address.
  • allow_zeroval_outs: bool. When true, outputs with a zero nValue bypass whitelist enforcement (useful for protocols that include OP_RETURN-style marker outputs alongside spends).

Example:

{
  "whitelist": ["bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"],
  "whitelist_opts": {"mode": "ATTEST", "allow_zeroval_outs": true}
}

Minimum Self-Transfer#

min_pct_self_transfer (float, 0..100) requires that a given percentage of the total own input value ends up in own outputs (i.e. addresses derived from this Coldcard's keys). Useful for consolidation rules, or to defend against policies being misused to drain funds to non-self destinations.

The check is (sum_of_own_output_values / sum_of_own_input_values) * 100 >= min_pct_self_transfer.

Transaction Patterns#

patterns is a list of transaction-shape constraints. All listed patterns must be satisfied for the rule to apply. Supported values:

  • EQ_NUM_INS_OUTS — the PSBT must have an equal number of inputs and outputs.
  • EQ_NUM_OWN_INS_OUTS — the PSBT must have an equal number of own inputs and own outputs (i.e. inputs and outputs that derive from this Coldcard's keys).
  • EQ_OUT_AMOUNTS — every output must have the same nValue.

Patterns are useful for enforcing CoinJoin-like or batch-consolidation shapes.

Global Policy Values#

Logging to MicroSD Card#

Two setting affect logging: must_log and never_log. By default, the Coldcard will log if a card is inserted. It does not fail if the card is missing. If that is an issue for you, then set must_log and transactions will be refused if the card isn't installed and working. never_log is useful when you don't want to keep records at the Coldcard's location.

Warnings Okay?#

This boolean allows the Coldcard to sign PSBT files that have warnings. Typically this is overly-large fees or weird path derivations. Since we don't expect warnings, any transactions with a warning is normally refused.

Message Signing#

To enable text message signing, list one or more BIP32 derivation paths in msg_paths. You can use the special value "any" to allow all signing. You may also use a star in the last position of a path, like these examples:

  • m/84'/0'/0/*
  • m/84'/0'/0'/*'
  • m/9984/*

The star allows any number in the final position (only). It does not allow deeper paths.

Sharing Xpubs#

The Coldcard can calculate XPUB values for derived paths, if share_xpubs is defined. You can limit this feature by giving a list of permitted paths, or the keyword "any" to allow any subpath. The master xpub (m) is always available over USB protocol and cannot be disabled.

Sharing Addresses#

Similarly, the Coldcard can calculate wallet addresses, if share_addrs contains a list of whitelisted derivation paths. Star patterns, and the keyword "any" can be used, as well as the keyword "p2sh" which allows addresses in multisig wallets to be shared.

In the case of multisig wallets, we do not check the script provided, beyond the normal checks for inclusion into a known multisig wallet.

Storage Locker#

The storage locker is small number of bytes held in the secure element of the Coldcard. The HSM policy file can be used to write a text value to this area, using set_sl (16..414 chars). (That part of the policy file is forgotten after it's committed into the secure element.) The storage locker is protected by the master PIN, and has the same level of protection as the master seed.

The storage locker is readable over the USB connection in HSM mode. However, the number of times it can be read is limited by the allow_sl value. You may set this to one, so that when your companion software is started, it has one opportunity to read the locker. If it is restarted, or other (unauthorized) software on your USB-connected machine reads the locker, it will fail. Further access will require reboot of the Coldcard, and knowledge of the master PIN.

CKBunker manages the Storage Locker for it's own purposes: it stores a 32-byte secret to unlock a NaCL secret box holding all CKBunker settings.

Boot-to-HSM#

This feature forces the Coldcard to start in HSM mode immediately after boot up (and entry of the master PIN). It is enabled if boot_to_hsm is defined.

If you specify a 6-digit numeric code for boot_to_hsm, and if that code is provided in the first 60 seconds after startup, the Coldcard will leave HSM mode.

But if you set the boot_to_hsm value to a non-numeric value which cannot be entered by the keypad, the Coldcard will never be able to leave HSM mode.

Bricking Hazard

No changes to firmware, HSM policy, Coldcard settings will be possible—ever again.
Not even the master PIN holder can change HSM policy nor escape HSM mode! Firmware upgrades are not possible.

Privacy over UX#

During development of the HSM feature, we found there were numerous status and informational values being shared over USB that, to some degree, assist attackers. However, those values are needed to provide a usable interface and a nice user experience (UX).

If you set priv_over_ux to true, the following values will not be shared over USB in the HSM status response:

  • text summary of the spending policy
  • count of approvals / refusals
  • the number of time the storage locker has been read
  • the period length
  • when the period will end
  • how much each rule has spent in current period
  • system uptime
  • list of usernames
  • number of users which have provided auth credentials for current PSBT

The CKBunker can operate in either mode, but you will find it harder to use, as it's not possible to know where you stand in terms of velocity spending and user authorization.

Example JSON Policy File#

Here is a sample policy file, ready to be uploaded into a Coldcard.

It has three rules:

  • local user can authorize up to 1BTC per txn (by themselves)
  • either Alice or Bob can authorize up to 1BTC per 4 hour period
  • allow any txn that sends to address bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq
{
  "never_log": true,
  "must_log": false,
  "priv_over_ux": false,
  "boot_to_hsm": null,
  "period": 240,
  "set_sl": "my secret here",
  "allow_sl": 13,
  "rules": [
    {
      "whitelist": [],
      "per_period": null,
      "max_amount": 100000000,
      "users": [],
      "local_conf": true,
      "wallet": null
    },
    {
      "whitelist": [],
      "per_period": 100000000,
      "max_amount": null,
      "users": [
        "alice",
        "bob"
      ],
      "min_users": 1,
      "local_conf": false,
      "wallet": null
    },
    {
      "whitelist": [
        "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"
      ],
      "per_period": null,
      "max_amount": null,
      "users": [],
      "local_conf": false,
      "wallet": null
    }
  ],
  "msg_paths": [
    "any"
  ],
  "share_xpubs": [
    "m/84'/0'/0'/*"
  ],
  "share_addrs": [
    "m/84'/0'/0'/*"
  ],
  "notes": "Semper Fi"
}

The Coldcard will show this text to summarize the policy:

=-=
Semper Fi
=-=

Transactions:
- Rule #1: Up to 1 XTN per txn will be approved if local user confirms
- Rule #2: Up to 1 XTN per period may be authorized by any one user: alice OR bob
- Rule #3: Any amount will be approved provided it goes to: bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq

Velocity Period:
 240 minutes
 = 4 hrs

Message signing:
- Allowed if path matches: (any path)

Other policy:
- No logging.
- Storage Locker will be updated, and can be read 13 times.
- XPUB values will be shared, if path matches: m OR m/84'/0'/0'/*.
- Address values values will be shared, if path matches: m/84'/0'/0'/*.

Last update: June 2, 2026