Skip to content

Commit c448ca9

Browse files
andrewkminclaude
andcommitted
feedback: caveat static-deposit key export
Address review on static deposit security caveats: - Flag the static-deposit key-export exception in the top-of-page caveat and link to the security section - Wrap the EXPORT_WALLET_ACCOUNT note in a callout box - Add reassurance that the exported key is irrelevant after claiming (links SPARK_CLAIM_TRANSFER + SDK example) - Outline the SSP + Spark Operator collusion risk window and link Spark's sovereignty docs Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d2ac181 commit c448ca9

1 file changed

Lines changed: 13 additions & 4 deletions

File tree

features/networks/spark.mdx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: "Spark support on Turnkey"
33
sidebarTitle: "Spark"
44
---
55

6-
[Spark](https://www.spark.money/) is a Bitcoin Layer 2 that uses FROST threshold signing across a collective of operators to enable fast, low-fee transfers and Lightning payments without giving up self-custody of the underlying BTC. Turnkey provides enclave-based key management for Spark: your identity key, leaf keys, deposit keys, and Lightning preimages are generated and used inside the Turnkey enclave, and never leave it.
6+
[Spark](https://www.spark.money/) is a Bitcoin Layer 2 that uses FROST threshold signing across a collective of operators to enable fast, low-fee transfers and Lightning payments without giving up self-custody of the underlying BTC. Turnkey provides enclave-based key management for Spark: your identity key, leaf keys, deposit keys, and Lightning preimages are generated and used inside the Turnkey enclave. With a single exception, none of this key material ever leaves it — the [static deposit flow](#static-deposits-export-a-key-from-the-enclave) exports one deposit key so a Spark Service Provider can process deposits while your wallet is offline, and that key stops mattering once you claim the deposit.
77

88
If you don't know the protocol, read [Spark core concepts](https://docs.spark.money/learn/core-concepts) and [Sovereignty](https://docs.spark.money/learn/sovereignty) first. This page covers what Turnkey adds to a Spark integration.
99

@@ -119,14 +119,23 @@ This is the property that makes Spark Operator unavailability a _liveness_ conce
119119

120120
### Static deposits export a key from the enclave
121121

122-
Static deposit addresses are reusable: one address can receive many deposits, each creating a separate Spark leaf. To make that work, the SSP needs to process deposits while your wallet is offline, which means it needs co-signing capability on the static deposit key. This is the **only** Spark flow that uses [`EXPORT_WALLET_ACCOUNT`](/api-reference/activities/export-wallet-account) to take a raw private key out of the Turnkey enclave.
122+
Static deposit addresses are reusable: one address can receive many deposits, each creating a separate Spark leaf. To make that work, the SSP needs to process deposits while your wallet is offline, which means it needs co-signing capability on the static deposit key.
123123

124-
From the moment the static deposit key is shared with the SSP until you claim any deposits made to that address, the SSP holds co-signing capability. This is the intentional custodial tradeoff of static deposits. To minimize exposure:
124+
<Warning>
125+
Static deposits are the **only** Spark flow that takes a raw private key out of the Turnkey enclave. The flow uses [`EXPORT_WALLET_ACCOUNT`](/api-reference/activities/export-wallet-account) to export the static deposit key so it can be shared with the SSP. Every other Spark flow keeps all key material inside the enclave.
126+
</Warning>
127+
128+
This is the intentional custodial tradeoff of static deposits — expected behavior, not a leak. The exported key controls only the **static deposit address**; it cannot move existing leaves or touch your identity key. To minimize exposure while the key is in transit:
125129

126130
- Use a fresh ephemeral P-256 keypair for each export and zero it immediately after decrypting.
127131
- Transmit the key to the SSP only over an encrypted channel.
128132
- Zero the key in your local memory immediately after transmission.
129-
- Only use SSPs you trust: a compromised SSP holding this key can co-sign spends from the static deposit address.
133+
134+
<Note>
135+
**The exported key stops mattering once you claim the deposit.** Claiming runs through [`SPARK_CLAIM_TRANSFER`](/api-reference/activities/claim-spark-transfer), which rotates the leaf to a fresh key derived inside your enclave; after that, the exported static deposit key has no authority over the funds. See the static deposit scripts in the [SDK example](#sdk-example) for the full claim flow.
136+
</Note>
137+
138+
**The risk window.** A static deposit address is an aggregate of your static deposit key and the Spark Operators' key, so moving funds out of it requires a signature from both. Between the moment funds arrive at the address and the moment you claim them, the SSP holds your half of that key. During this window — and only this window — an SSP that **colludes with the Spark Operators** could co-sign a spend of the unclaimed deposit. This is the same operator-trust boundary described in Spark's [sovereignty model](https://docs.spark.money/learn/sovereignty); use only SSPs you trust, and claim deposits promptly to keep the window short.
130139

131140
If you don't need a reusable receiving address, prefer single-use deposits. They keep key material inside the enclave throughout.
132141

0 commit comments

Comments
 (0)