Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2490ff2
remote signer nut
lescuer97 Mar 30, 2025
d9b2d8f
fix: improved compromise of remote signer
lescuer97 Apr 10, 2025
195e120
fix: change of derivation
lescuer97 Apr 10, 2025
8e9fd16
standard consolidation
lescuer97 Apr 14, 2025
40bb2ba
change integer type
lescuer97 Apr 22, 2025
5e65d02
change name to service
lescuer97 Apr 23, 2025
b8d31c7
run pretty
lescuer97 Apr 23, 2025
c049e08
auth section
lescuer97 Apr 23, 2025
58a0363
Update nut-xx.md
lescuer97 Apr 23, 2025
0674129
Update nut-xx.md
lescuer97 Apr 23, 2025
4b33469
fix spec
lescuer97 Apr 30, 2025
d0006f9
Merge branch 'remote-signer-nut' of github.com:lescuer97/nuts into re…
lescuer97 Apr 30, 2025
30b4f11
prettier fix
lescuer97 Apr 30, 2025
0c051de
test vector
lescuer97 May 2, 2025
ae8429c
fix: remove oneOf
lescuer97 May 8, 2025
734332d
add metadata fields
lescuer97 May 13, 2025
36b043d
fix: protobuf styling
lescuer97 May 13, 2025
465bf8e
fix: remove proof witness
lescuer97 May 16, 2025
59c3404
remove unnedded text
lescuer97 May 31, 2025
a8401f7
fix: bytes for id
lescuer97 May 31, 2025
f6e41b9
remove amounts
lescuer97 May 31, 2025
2f0e728
fix: ids as bytes
lescuer97 May 31, 2025
2add6e5
chore: add version of keyset
lescuer97 Jul 21, 2025
5abce61
feat: final_expiry
lescuer97 Sep 8, 2025
99e9f8e
add optional to final_expiry
lescuer97 Oct 6, 2025
6cdccbf
cleanup file
pokemongogouy Nov 2, 2025
f45fddd
fix range of integer
pokemongogouy Nov 6, 2025
4e54ead
prettier
pokemongogouy Nov 6, 2025
2dc81e4
change to use different notation
pokemongogouy Nov 6, 2025
fc48eb0
auth test
pokemongogouy Nov 7, 2025
43f2543
check version type
pokemongogouy Nov 8, 2025
01fa60a
add seedphrase constraint and unit string serialization
lescuer97 Dec 1, 2025
f4558d4
add comparison table
lescuer97 Dec 1, 2025
192e235
change to use uppercase
lescuer97 Dec 2, 2025
a91269f
change unit reference warning
lescuer97 Dec 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions nut-xx.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# NUT-XX: Remote signer communications

`optional`

---

This NUT define a standard way for Mints to communicate with a remote signer. The mint can protect its private key by isolating it to a service.

Mints will connect to the signer and ask for validation of proofs as well signing blinded messages. This NUT does not specify the transport method needed to communicate with the mint.

## Protocol

The signer MUST speak the same language using the GRPC protobuf defined in `remote-signer.proto` inside the repository.

### Authentication

The signer and the mint can communicate with each other over different transports.
The signer MUST NOT accept any request if authentication has not occurred.

#### Over the Network

The Mint and the Signer MUST authenticate with each other using mTLS.

## Seedphrase usage

[BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) mnemonic seedphrase must be used for generation
of the keysets.

Example: `legal winner thank year wave sausage worth useful legal winner thank yellow`

### Canonical unit reference parsing

Implementations **MUST** accept currency unit labels case-insensitively and ignore leading or trailing ASCII whitespace. When serializing a unit into JSON (e.g., as part of a keyset description in NUT-01 responses), implementations **SHOULD** emit the uppercase representation of the unit so that mints and wallets display consistent labels.

Before deriving an index, the input label **MUST** be transformed as follows:

1. Remove leading and trailing ASCII whitespace characters (space, tab, carriage return, line feed).
2. Apply Unicode Normalization Form C (NFC).
3. Convert the normalized string to uppercase using Unicode-aware semantics.

| Input unit | Canonical form | Index |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you say 'index' here, do you mean 'unit reference'? This is essentially the first mention of 'index' in this doc, and I don't know what it is.

(I see many others have commented already. Apologies in advance if I now ask a few questions that have already been answered 😀)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi! it just talks about the actual index number that is going to be used later on the derivation path

Copy link
Contributor Author

@lescuer97 lescuer97 Jan 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi! it just talks about the actual index number that is going to be used later on the derivation path.

Should probably use better naming

| ------------ | -------------- | ------------ |
| `sat` | `SAT` | `1967237907` |
| `SAT` | `SAT` | `1967237907` |
| `nuts` | `NUTS` | `1502388627` |
| `USD` | `USD` | `577560378` |
| `usD` | `USD` | `577560378` |
| `café` | `CAFÉ` | `642348965` |
| `cafe\u0301` | `CAFÉ` | `642348965` |
| `eurc` | `EURC` | `1321886550` |

NOTE: Mints **MUST** make sure that the unit_reference integer has not been repeated before for a different normalized
unit string.

## Generating keys for the signer.

For compatibility reasons all signers SHOULD implement the following BIP32 derivation path.

- m = master key
- 129372' (UTF-8 for 🥜)
- unit_reference = Big endian encoded integer of the first 4 bytes of the sha256 hash of the canonical unit string reference modulo by 2^31. We modulo because we want to stay inside the `2^31 - 1` range.
ex: sha256sum('auth')[:4] = bdf49c3c = 3186924604
3186924604 % 2^31 = 1039440956.
- version: uint32
- index_of_amount = index of and the amounts of the keyset as if the where laid in an array. ex: [1, 2, 4, 8, 16, ...]

`m / 129372' / unit_reference' / version' / index_of_amount'`

## Configuration

The Signer can also be configured to have some rate limiting features. Limiting the amount of minting that can happen by
time or single action.
145 changes: 145 additions & 0 deletions remote-signer.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
syntax = "proto3";

package signatory;

service Signatory {
rpc BlindSign(BlindedMessages) returns (BlindSignResponse);
rpc VerifyProofs(Proofs) returns (BooleanResponse);
// returns all the keysets for the mint
rpc Keysets(EmptyRequest) returns (KeysResponse);
// rotates the keysets
rpc RotateKeyset(RotationRequest) returns (KeyRotationResponse);
}

enum Operation {
OPERATION_UNSPECIFIED = 0;
OPERATION_MINT = 1;
OPERATION_MELT = 2;
OPERATION_SWAP = 3;
}

message BlindSignResponse {
Error error = 1;
BlindSignatures sigs = 2;
}

message BlindedMessages {
repeated BlindedMessage blinded_messages = 1;
Operation operation = 2;
string correlation_id = 3;
}

// Represents a blinded message
message BlindedMessage {
uint64 amount = 1;
bytes keyset_id = 2;
bytes blinded_secret = 3;
}

message BooleanResponse {
Error error = 1;
bool success = 2;
}

message KeyRotationResponse {
Error error = 1;
KeySet keyset = 2;
}

message KeysResponse {
Error error = 1;
SignatoryKeysets keysets = 2;
}

message SignatoryKeysets {
bytes pubkey = 1;
repeated KeySet keysets = 2;
}

message KeySet {
bytes id = 1;
CurrencyUnit unit = 2;
bool active = 3;
uint64 input_fee_ppk = 4;
Keys keys = 5;
uint32 version = 6;
optional uint64 final_expiry = 7;
}

message Keys {
map<uint64, bytes> keys = 1;
}

message RotationRequest {
CurrencyUnit unit = 1;
uint64 input_fee_ppk = 2;
repeated uint64 amounts = 3;
// unix timestamp for expiration
optional uint64 final_expiry = 4;
}

enum CurrencyUnitType {
CURRENCY_UNIT_TYPE_UNSPECIFIED = 0;
CURRENCY_UNIT_TYPE_SAT = 1;
CURRENCY_UNIT_TYPE_MSAT = 2;
CURRENCY_UNIT_TYPE_USD = 3;
CURRENCY_UNIT_TYPE_EUR = 4;
CURRENCY_UNIT_TYPE_AUTH = 5;
}

message CurrencyUnit {
oneof currency_unit {
CurrencyUnitType unit = 1;
string custom_unit = 2;
}
}

message Proofs {
repeated Proof proof = 1;
Operation operation = 3;
string correlation_id = 4;
}

message Proof {
uint64 amount = 1;
bytes keyset_id = 2;
bytes secret = 3;
bytes c = 4;
}

message BlindSignatures {
repeated BlindSignature blind_signatures = 1;
}

message BlindSignature {
uint64 amount = 1;
bytes keyset_id = 2;
bytes blinded_secret = 3;
optional BlindSignatureDLEQ dleq = 4;
}

message BlindSignatureDLEQ {
bytes e = 1;
bytes s = 2;
}

enum ErrorCode {
ERROR_CODE_UNSPECIFIED = 0;
ERROR_CODE_AMOUNT_OUTSIDE_LIMIT = 1;
ERROR_CODE_DUPLICATE_INPUTS_PROVIDED = 2;
ERROR_CODE_DUPLICATE_OUTPUTS_PROVIDED = 3;
ERROR_CODE_KEYSET_NOT_KNOWN = 4;
ERROR_CODE_KEYSET_INACTIVE = 5;
ERROR_CODE_MINTING_DISABLED = 6;
ERROR_CODE_COULD_NOT_ROTATE_KEYSET = 7;
ERROR_CODE_INVALID_PROOF = 8;
ERROR_CODE_INVALID_BLIND_MESSAGE = 9;
ERROR_CODE_UNIT_NOT_SUPPORTED = 10;
}

message Error {
ErrorCode code = 1;
string detail = 2;
}

message EmptyRequest {}
100 changes: 100 additions & 0 deletions tests/xx-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# NUT-XX Test Vectors

The following are keysets corresponding to the id: `00b5a0580f75cc2f`.

- `version` = 1
- `unit` = sat

```json
{
"1": "0233501d047ff4058007722d5d24e10a8ff5c723a677be411fff46a3cee9a92cc0",
"2": "03a09803ce40118b8917fafa08409dbe6e8bb36d76c55f4c58400cd720abaf54cb",
"4": "02dac058df2e8611098286ef87ee9698f555548784ab4b1a860c79338073ad8c49",
"8": "025b66b937d65544981817aa9a053a762a7d72a7543c66a54370ea68aa53170a10",
"16": "027cf2ad5fa02b99ea37b305048562828453d89dfa7defcda1c10f6746f25f7541",
"32": "0336033cbbc044737bced1fd40b7f0cb0ce08a83aedaa882ed1ced875a1f517879",
"64": "035be95ecaadbfe67b14f07205d13bbcab5da58bb595c57dfb9b61c5e3e7e4de0e",
"128": "0232c757957a8f5a14e93a9bbe8852c273b985ad238ce9b4d5a16885d8a761462b",
"256": "02cbd889df7d38e95dca2ee0e09bc22e3ae57e95975043854a5560a464f970ac1f",
"512": "02c99a0b72ba8f01c5da765c534e75ae3e5f51e4931bfced18a91df4b9233b168f",
"1024": "0320527abb6ae3dd6db9da5041ca941be679e953b446614843af7a4393e9ac96bc",
"2048": "033f9276b0c5f73fbeb0130eab5705a8e878f4191fe251a18cbd918cda3c9e2d5e",
"4096": "03cf69ed2939be4ac35308560d4423e1a0d96cacf9fe33267c7e6a047bf438e53e",
"8192": "027c8bfff71352766c3870e9f5f577830bbb44eadfb757fdff9a8cd209c4b22d76",
"16384": "02ea21bd310828b9e46746eba2ae985626b3a2efc2468db66ae480715dc6deec8a",
"32768": "027ae7179192282d5b44ac55bff82c13e1ea916ae1edefa33ea64100be7408e015",
"65536": "028f333c1beada3445cb62108e35d72199925a055c1e7c102c742e1761770f6c62",
"131072": "03de95cae3614499a3df2d412e91aa09ddef8b8d49e8d652e3798419da86958139",
"262144": "03c7817c19b4b107eb2ccf2f32b60f9c22a59a1d4a93e492ad01f1505097a654b7",
"524288": "028aad03886b6ec6b9f628090e9c151a73f025aa949a9686dac1f0b32995a4e8df",
"1048576": "034bf50a5916d9f112b8fbfe82a5ac914b5bec792b107cf25922c9866f002473e8",
"2097152": "03d2894e1b1b7ab7497ff69e16d280b630f60ba34fe00edd7c748ae5ee73bc0d1a",
"4194304": "0285ba0ee2960927de958610b13d63fc29019407eb32c477d9a2d016fda3062a37",
"8388608": "03d7a4b4b1b8d6b9f2b5966e380a62f8efd53f79d1965e076a716d2fb75e9774a1",
"16777216": "037a033e2f1df992523df83bcb9aa02cefdadd59882d7949f4500f5493d89fa2fd",
"33554432": "03014de7af4809599cabc6d6b30e5121b4a88153eb38a7b66dd8e50e3166215ab0",
"67108864": "0240162a1d2eb1841450de53a6244a625922b14006153d5219dad0fcf0c369c497",
"134217728": "03f8c6f7b0ee71f66940a33c746c3bf8b1cba793a498dd2fdeb6857552415a4d5d",
"268435456": "02dc9de15fa1332f5a2c8f85045ea127cbc3407fb8a844b453f38e1c9cdce9ef87",
"536870912": "0291bdcb1719b5bf447b2885efc84061d1de30b9d1f583d25034059457a2fd739e",
"1073741824": "02f8a96485e3fa791f57d7f4ef279dd3617b873efbdf673815c49dbf9ce7422b0d",
"2147483648": "02ff8cf3e3de985bb2f286c98e335a175b2b53a0e0d7fa1f53d642c95a372329a2",
"4294967296": "02d96196cc54e7506bfe9fdb4a0d691eed2948ecb9b8e81d28d27225287ad5debc",
"8589934592": "03e64e5664f7ab843f41aaf4c0534d698b3318d140c23cbd2fcc33eece53400dac",
"17179869184": "034c9a4bf7b4cb8fac6ace994624e5250ddac5ac84541b6c8bd12b71d22719bb2d",
"34359738368": "0313027c2b106c7dcdee0d806c3343026260276c6793d4d1dfdf79aae30875be31",
"68719476736": "03081adca96d42cb2ac4ac94e0ea2aac4d9412265ae55ed377e3c0357aa1157253",
"137438953472": "02fdc4118761739425220ba87dee5ea9fdc1d581abfcb506fb5afabf76e172b798",
"274877906944": "031dd7cd25f761c8f80828b487bab1cef730f68e8d6f2026b443cc7223862f6c73",
"549755813888": "02da505eab15744a6fd3fa6b3257bced520d4d294ea94444528fd30d7f90948629",
"1099511627776": "02bfc54369099958275376ab030f2a085532c8a00ae4d1bbfa5031c64b42d58a47",
"2199023255552": "032241a5d4d1e988b8ae85f68a381df0e40065ae8c81b1c4f7ea31c87eab2c0d81",
"4398046511104": "03a681e41990d350cdedd30840f26ad970b4015dd6e6b5c03f7cc99b384bee8762",
"8796093022208": "033d5293a33cda29d65058d6d3a4b821472574e92414fa052c79f8bdc1cd72faba",
"17592186044416": "033ddfec40622aaf62d672f43fd05ddb396afd7ad9f00daede45102c890d3a012b",
"35184372088832": "02564bbdcbed18a8e2d79b2fdad6e5e8a9fe92e853ab23170934d84015cc4b96b0",
"70368744177664": "02170950642b94d0ed232370d5dd3630b5eb7e73791447fb961b12d8139de975de",
"140737488355328": "02b2add5a6eb5dc06f706e9dba190ba412c2c7ba240284b336b66ef38a39e51f1c",
"281474976710656": "03e3e584a4bc1d0a6399f5b6b9355bd67a10ad9f46c8a4283de96854e47eb4357c",
"562949953421312": "033821262e6a78f29dad81d3133845883a7632a47f51ab1d99a0eae4a5354eef45",
"1125899906842624": "038db672a61c70dc66b504152ea39b607527f2f59e8ebfdf8d955c38e914661534",
"2251799813685248": "03dafb9683eac036a422266ddc85b675bf13aeafe0658cad2ec1555c28f4049b28",
"4503599627370496": "0351733345d4bb491e27bdb221e382d00f2248f2ee7f04dc6f3faab2692fbd296c",
"9007199254740992": "03f930c1e6c154ca169370adbec7691fd9c11245867a37ae086f7547f5c9e8386f",
"18014398509481984": "02d700dc30d3cd6be292bddbd5f74c09df784862c785cd763ad6c829be59c21bed",
"36028797018963968": "03444b9c312900fffbd478e390aa6fdf9d3ffe230239141ecadf0bcee25e379512",
"72057594037927936": "03af7acedfcfcaf83cfdb7d171ef64723286bd6e0ab90f3629e627e77955917776",
"144115188075855872": "02e35aef647a881e8c318879fb81b6261df73e385dfbc5ff3fc0ab40f13f5ed560",
"288230376151711744": "024558ed8e986901e05839c34d17c261c8d93b8cabb5dee83ab805bb5028e5e463",
"576460752303423488": "024f60a89ba055e009d84a90a13a7860a909fb486a8ffb4315c2f59aff6fbfd929",
"1152921504606846976": "0311b2a5b91dfaebab4fb125338fd38dab72ec5671e6db5f468cb1477970ea3876",
"2305843009213693952": "02aeaa116d930767b5143cac922511c0e093beee5a2850f67490f5a5bb44a8af76",
"4611686018427387904": "02bf7003847bc8e7ad35ea5c8975e3fdde8d1c43ef540d250cf2dc75792c733647",
"9223372036854775808": "0376b06a13092fbb679f6e7a90ce877c37d5a20714a65567177a91a0479b3e86a9"
}
```

Keyset id: `00e1cf6079abb988`.

- `version` = 1
- `unit` = auth

```json
{
"1": "025b6c1ca8bb741a6f2321c953266df7bf3f3f2c3be8c54c0a6e41bb00976046a4"
}
```

This are the expected integer for unit strings:

```json
{
"sat" = 866057899
"msat" = 1980671987
"auth" = 1039440956
"eur" = 975082952
"usd" = 1443872135
"SAT" = 866057899
"cafe\u0301" = 84901316
}
```