Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save silentius-satoshi/fd4a4e479f8b9540436b9058b7b158bd to your computer and use it in GitHub Desktop.

Select an option

Save silentius-satoshi/fd4a4e479f8b9540436b9058b7b158bd to your computer and use it in GitHub Desktop.
A split-service TEE architecture for privacy-preserving Silent Payment wallet services on Nostr

Nostr Silent Payments – Split-Service TEE Design Note

The Problem

The three-NIP Nostr Silent Payments stack (derivation standard PR #2355, receipts PR #2362, Silent Wallet Connect PR #2369) solves the on-chain privacy problem cleanly. A chain observer cannot link payments to the recipient’s identity. Each payment lands as a fresh unlinkable Taproot output.

But the wallet service – the always-on component that constructs and broadcasts Silent Payment transactions on behalf of Nostr clients – sees everything:

  • Who is paying (sender’s Nostr identity)
  • Who is being paid (recipient’s npub)
  • How much
  • When

Over time, a third-party wallet service operator builds a complete payment graph that correlates Nostr social identities with Bitcoin spending patterns. The on-chain privacy guarantee applies to chain observers. It does not extend to the wallet service operator.

This design note proposes a way to close that gap.


The Alice and Bob Problem

Alice wants to pay Bob privately. She opens her Nostr client, which sends an encrypted payment request to her connected wallet service. The wallet service derives Bob’s Silent Payment address from his npub, selects UTXOs, constructs the BIP-352 transaction, signs, and broadcasts.

The wallet service operator – even an honest one – now knows:

  • Alice paid Bob
  • The amount
  • The time

If Alice pays 100 people over a year, the operator has a complete map of her Bitcoin social graph. If the operator is subpoenaed, breached, or simply untrustworthy, that graph is exposed.

The on-chain transaction is private. The wallet service is not.


The Insight: Split the Knowledge

No single wallet service component needs to know everything. The payment flow has two distinct operations that can be separated:

Operation 1 – Finding the output key

To construct the BIP-352 output, the wallet needs to compute:

shared     = input_hash * a * Bscan
tk         = H_tagged("BIP0352/SharedSecret", serP(shared) || ser32(k))
Pk        = Bspend + tk * G

This requires knowledge of the recipient’s public scan key Bscan and the sender’s input private keys. The result is an output key Pk – a public value.

Operation 2 – Signing the transaction

To authorize the spend, the wallet needs the sender’s private keys to produce a valid Schnorr signature over the transaction. This requires no knowledge of the recipient at all – just the unsigned PSBT.

These two operations can be assigned to two separate components, each knowing only what it needs.


The Split-Service Architecture

Alice's Nostr Client
        |
        |  encrypted kind 23352 request
        |  carrying: Bscan, Bspend, amount
        |  (NOT recipient_npub)
        v
+-------------------------+
|   Derivation Service    |
|                         |
|  - receives Bscan,      |
|    Bspend, amount       |
|  - selects UTXOs        |
|  - computes output key  |
|    Pk = Bspend + tk*G   |
|  - builds unsigned PSBT |
|  - never learns npub    |
|  - never holds privkeys |
+-------------------------+
           |  unsigned PSBT only
           v
+-------------------------+
|    Signing Service      |
|                         |
|  - receives PSBT only   |
|  - signs with Alice's   |
|    private keys         |
|  - broadcasts tx        |
|  - never learns Bscan   |
|  - never learns npub    |
|  - never learns Bob     |
+-------------------------+
           |
           v
    Bitcoin network

What each component learns:

Component Knows sender? Knows recipient? Knows amount?
Derivation Service No Partial (Bscan/Bspend only) Yes
Signing Service Yes (holds privkeys) No Yes (from PSBT)
Either alone Insufficient to reconstruct payment graph

No single operator holds the full picture. A subpoena of one component yields an incomplete record. A breach of one component does not expose the full payment graph.


The TEE Layer

The split-service architecture reduces what each operator knows. Trusted Execution Environments (TEEs) go further – they prevent operators from seeing what their own service is processing.

A TEE is a hardware-enforced isolated execution environment. Code running inside a TEE cannot be observed or tampered with by the host operating system, the cloud provider, or the service operator. The TEE produces a cryptographic attestation proving to external parties that specific, unmodified code is running in the enclave.

Applied to the split-service architecture:

Derivation Service in a TEE:

  • The operator deploys the derivation code into an enclave
  • The enclave produces an attestation clients can verify before sending requests
  • The operator cannot log, inspect, or extract Bscan, Bspend, or amounts
  • Even if the operator is subpoenaed, they have nothing to hand over

Signing Service in a TEE:

  • Alice’s private keys never leave the enclave in plaintext
  • The operator cannot observe which UTXOs are being spent or who the output belongs to
  • The enclave signs and broadcasts without the operator seeing the transaction contents

Combined:

Alice's Client
      |
      |  verifies TEE attestation before sending
      |
      v
 Derivation TEE ---> unsigned PSBT ---> Signing TEE ---> Bitcoin
      |                                      |
   operator                              operator
  sees nothing                          sees nothing

Two independent privacy guarantees. Neither dependent on the other. A compromise of one TEE does not expose the other component’s knowledge.


What This Does and Does Not Protect

Protected:

  • Wallet service operator cannot build a payment graph
  • Neither component alone can link Alice to Bob
  • Operator compliance with subpoenas yields incomplete records
  • On-chain privacy (already guaranteed by BIP-352) is preserved

Not protected:

  • Global passive adversary correlating blockchain timing with Nostr events
  • Amount privacy on-chain – transaction amounts are public
  • Alice’s identity from Bob – the kind 8352 receipt hint reveals a payment was made, though the ephemeral gift wrap key hides the sender’s Nostr identity
  • TEE side-channel attacks – TEEs have known vulnerabilities (SGX has had multiple disclosures). The guarantee is probabilistic, not mathematical.

Relationship to the Current NIP Stack

This design note proposes an optional advanced implementation tier on top of the Silent Wallet Connect NIP (PR #2369). The core protocol is unchanged:

  • Kind 23352 request format is unchanged
  • Kind 23353 response format is unchanged
  • Kind 38352 wallet connection info is unchanged

The only change is that the client sends Bscan and Bspend directly instead of recipient_npub, and the wallet service is split into two TEE-attested components instead of one. The BIP-352 math and the Nostr event structure are identical.

This separation keeps the base NIP simple and implementable today with existing tools. The split-service TEE architecture is an upgrade path for operators who require stronger privacy guarantees – not a requirement for basic compliance.


Why Not Redesign Around the Root-Equivalence Problem?

The NSP derivation model (bscan = d_npub + t_scan mod n) means the scan private key is root-equivalent to the Nostr private key. This is an algebraic consequence of additive derivation on secp256k1, not a design choice. There is no construction that simultaneously provides:

  • Public derivability of the Silent Payment address from npub
  • Independence of bscan from nsec
  • BIP-352 compatibility

The split-service TEE architecture accepts this constraint and addresses the wallet service surveillance problem at the infrastructure layer – which is where it belongs.


Open Questions

TEE platform selection. Intel SGX, AMD SEV, and Apple Secure Enclave have different security properties, attack surfaces, and availability constraints. A production implementation would need to specify which TEE platforms are acceptable and how clients verify attestations across platforms.

Attestation verification by clients. How does Alice’s Nostr client verify the TEE attestation before sending a request? This requires either a trusted attestation service or direct remote attestation – both have tradeoffs.

PSBT handoff between components. How does the unsigned PSBT move from the Derivation TEE to the Signing TEE without leaking information to either operator or an intermediary? End-to-end encryption between enclaves is possible but adds protocol complexity.

Minimum viable implementation. A split-service architecture without TEE attestation still reduces the surveillance surface compared to a single monolithic wallet service. Defining a minimum viable split-service profile that does not require TEE hardware may be a useful intermediate step.


Authors

silentius-satoshi npub13vftmhzzxxyuvcq4d643agzwr4zvce3pc4gvxymgvuzlwpxa4z2sq4sjd9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment