diff --git a/features/policies/examples/access-control.mdx b/features/policies/examples/access-control.mdx index 77bbd0a9..dec26672 100644 --- a/features/policies/examples/access-control.mdx +++ b/features/policies/examples/access-control.mdx @@ -179,3 +179,27 @@ This policy can be used to say, only passkeys are allowed to sign transactions a "condition": "activity.action == 'EXPORT' && wallet_account.address == ''" } ``` + +#### Allow exporting only wallet accounts under a hardened derivation subtree + +This policy allows exporting only wallet accounts whose derivation path is a fully-hardened, +4-segment path of the form `m/8797555'/x'/3'/y'` (a Spark-purpose subtree). Because single-quoted +policy literals cannot contain `'` (apostrophe), the path is matched structurally via +`wallet_account.path_indexes` and `wallet_account.path_hardened` rather than as a string. Note that +policies match the literal stored path; on ed25519, soft and hardened spellings derive the same key. + +```json JSON +{ + "policyName": "Allow exporting only accounts under m/8797555'/x'/3'/y'", + "effect": "EFFECT_ALLOW", + "consensus": "approvers.any(user, user.id == '')", + "condition": "activity.kind == 'EXPORT_WALLET_ACCOUNT' && wallet_account.path_indexes.count() == 4 && wallet_account.path_indexes[0] == 8797555 && wallet_account.path_indexes[2] == 3 && wallet_account.path_hardened.all(h, h == true)" +} +``` + +The derivation-path fields are populated only for `EXPORT_WALLET_ACCOUNT` activities: on any other +activity, a condition referencing them produces an evaluation error (the policy's outcome is an +error, not a match against empty values). Prefer narrow allow policies like this one over combining +a broad allow with a path-based `EFFECT_DENY`: during rollout windows where some Turnkey components +do not yet know these fields, an erroring deny is skipped while a broad allow still matches (failing +open), whereas a narrow allow that errors fails closed. diff --git a/features/policies/language.mdx b/features/policies/language.mdx index 8d7c1617..aaa4dc0a 100644 --- a/features/policies/language.mdx +++ b/features/policies/language.mdx @@ -116,6 +116,12 @@ The language is strongly typed which makes policies easy to author and maintain. | | exported | bool | Boolean indicating whether or not this wallet has been exported | | | label | string | The label of this wallet | | **Wallet Account** | address | string | The wallet account address | +| | path | string | The account's BIP32 derivation path as stored, e.g. `m/44'/60'/0'/0/0` | +| | path_indexes | list\ | The derivation path child indexes with the hardened bit stripped, e.g. `[44, 60, 0, 0, 0]` | +| | path_hardened | list\ | Positionally paired with `path_indexes`; true where the corresponding path segment is hardened | +| | wallet_id | string | The identifier of the wallet this account belongs to | +| | curve | string | The curve enum name, e.g. `CURVE_SECP256K1` | +| | address_format | string | The address format enum name, e.g. `ADDRESS_FORMAT_COMPRESSED` | | **PrivateKey** | id | string | The identifier of the private key | | | tags | list\ | The collection of tags for the private key | | | imported | bool | Boolean indicating whether or not this private key has been imported | @@ -179,6 +185,30 @@ The language is strongly typed which makes policies easy to author and maintain. Primitives section.{" "} + + {" "} + Single-quoted policy string literals cannot contain `'` (apostrophe), so derivation paths like + `m/44'/60'/0'/0/0` cannot be matched as strings. This is why the Wallet Account derivation path + is exposed structurally via `path_indexes` and `path_hardened` in addition to `path`. The + `path`, `path_indexes`, `path_hardened`, `wallet_id`, `curve`, and `address_format` fields are + currently populated only for `EXPORT_WALLET_ACCOUNT` activities; other activities will follow. + On any other activity, a condition referencing these fields produces a `MissingField` evaluation + error — the policy's outcome is an error rather than a match against empty values (see + [Policy evaluation](#policy-evaluation) in the appendix), so an `EFFECT_ALLOW` policy that + references them does not grant access outside of exports.{" "} + + + + {" "} + During a rollout window in which some Turnkey components have not yet been updated with these + fields (version skew), a condition referencing them can produce an evaluation error even on + `EXPORT_WALLET_ACCOUNT` activities. An erroring `EFFECT_ALLOW` policy fails closed (the export + is denied), but an erroring `EFFECT_DENY` policy is skipped — so pairing a broad allow (e.g. + "allow all exports") with a path-based deny can fail open during the skew window. Prefer narrow + allow policies that match only the derivation paths you intend to permit over broad-allow plus + path-deny combinations.{" "} + + #### Nested structs | Struct | Field | Type | Description |