nostr-bray 1.22.0 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +299 -54
  2. package/dist/cli.js +11 -10
  3. package/dist/config.d.ts +38 -2
  4. package/dist/config.js +122 -14
  5. package/dist/dispatch/capabilities.d.ts +49 -0
  6. package/dist/dispatch/capabilities.js +120 -0
  7. package/dist/dispatch/handlers.d.ts +143 -0
  8. package/dist/dispatch/handlers.js +279 -0
  9. package/dist/dispatch/identities.d.ts +19 -0
  10. package/dist/dispatch/identities.js +62 -0
  11. package/dist/dispatch/protocol.d.ts +176 -0
  12. package/dist/dispatch/protocol.js +224 -0
  13. package/dist/dispatch/tools.d.ts +5 -0
  14. package/dist/dispatch/tools.js +343 -0
  15. package/dist/format.d.ts +18 -0
  16. package/dist/format.js +288 -0
  17. package/dist/identity/handlers.d.ts +4 -1
  18. package/dist/identity/handlers.js +9 -1
  19. package/dist/identity/nip05.d.ts +22 -0
  20. package/dist/identity/nip05.js +59 -0
  21. package/dist/identity/tools.d.ts +6 -0
  22. package/dist/identity/tools.js +42 -0
  23. package/dist/index.d.ts +4 -0
  24. package/dist/index.js +48 -3
  25. package/dist/marketplace/handlers.d.ts +185 -0
  26. package/dist/marketplace/handlers.js +495 -0
  27. package/dist/marketplace/listings.d.ts +64 -0
  28. package/dist/marketplace/listings.js +129 -0
  29. package/dist/marketplace/tools.d.ts +3 -0
  30. package/dist/marketplace/tools.js +406 -0
  31. package/dist/moderation/handlers.d.ts +168 -0
  32. package/dist/moderation/handlers.js +424 -0
  33. package/dist/moderation/tools.d.ts +3 -0
  34. package/dist/moderation/tools.js +306 -0
  35. package/dist/privacy/handlers.d.ts +113 -0
  36. package/dist/privacy/handlers.js +132 -0
  37. package/dist/privacy/tools.d.ts +3 -0
  38. package/dist/privacy/tools.js +156 -0
  39. package/dist/relay/auth.d.ts +18 -0
  40. package/dist/relay/auth.js +102 -0
  41. package/dist/relay/count.d.ts +16 -0
  42. package/dist/relay/count.js +116 -0
  43. package/dist/relay/handlers.d.ts +8 -1
  44. package/dist/relay/handlers.js +21 -1
  45. package/dist/relay/intelligence-handlers.d.ts +97 -0
  46. package/dist/relay/intelligence-handlers.js +469 -0
  47. package/dist/relay/intelligence-tools.d.ts +3 -0
  48. package/dist/relay/intelligence-tools.js +79 -0
  49. package/dist/relay/tools.js +53 -3
  50. package/dist/resolve.d.ts +45 -0
  51. package/dist/resolve.js +125 -0
  52. package/dist/safety/canary-handlers.d.ts +149 -0
  53. package/dist/safety/canary-handlers.js +311 -0
  54. package/dist/safety/tools.js +214 -0
  55. package/dist/signet/assessor.d.ts +27 -0
  56. package/dist/signet/assessor.js +88 -0
  57. package/dist/signet/handlers.d.ts +87 -0
  58. package/dist/signet/handlers.js +157 -0
  59. package/dist/signet/tools.d.ts +3 -0
  60. package/dist/signet/tools.js +91 -0
  61. package/dist/social/articles.d.ts +47 -0
  62. package/dist/social/articles.js +97 -0
  63. package/dist/social/badges.d.ts +45 -0
  64. package/dist/social/badges.js +134 -0
  65. package/dist/social/blossom.d.ts +118 -0
  66. package/dist/social/blossom.js +324 -0
  67. package/dist/social/calendar.d.ts +53 -0
  68. package/dist/social/calendar.js +154 -0
  69. package/dist/social/communities.d.ts +57 -0
  70. package/dist/social/communities.js +152 -0
  71. package/dist/social/dm.d.ts +10 -0
  72. package/dist/social/dm.js +22 -3
  73. package/dist/social/handlers.d.ts +17 -3
  74. package/dist/social/handlers.js +40 -3
  75. package/dist/social/notifications.d.ts +11 -0
  76. package/dist/social/notifications.js +40 -9
  77. package/dist/social/scheduled.d.ts +44 -0
  78. package/dist/social/scheduled.js +136 -0
  79. package/dist/social/search.d.ts +34 -0
  80. package/dist/social/search.js +71 -0
  81. package/dist/social/tools.js +521 -35
  82. package/dist/social/wiki.d.ts +44 -0
  83. package/dist/social/wiki.js +92 -0
  84. package/dist/trust/attestation-deep-handlers.d.ts +64 -0
  85. package/dist/trust/attestation-deep-handlers.js +131 -0
  86. package/dist/trust/ring-deep-handlers.d.ts +28 -0
  87. package/dist/trust/ring-deep-handlers.js +75 -0
  88. package/dist/trust/spoken-deep-handlers.d.ts +25 -0
  89. package/dist/trust/spoken-deep-handlers.js +40 -0
  90. package/dist/trust/tools.js +208 -12
  91. package/dist/trust-context.d.ts +56 -0
  92. package/dist/trust-context.js +186 -0
  93. package/dist/types.d.ts +8 -0
  94. package/dist/util/handlers.d.ts +16 -0
  95. package/dist/util/handlers.js +27 -1
  96. package/dist/util/tools.js +19 -5
  97. package/dist/vault/handlers.d.ts +101 -0
  98. package/dist/vault/handlers.js +264 -0
  99. package/dist/vault/resolver.d.ts +26 -0
  100. package/dist/vault/resolver.js +96 -0
  101. package/dist/vault/tools.d.ts +3 -0
  102. package/dist/vault/tools.js +110 -0
  103. package/dist/veil/cache.d.ts +21 -0
  104. package/dist/veil/cache.js +49 -0
  105. package/dist/veil/filter.d.ts +7 -0
  106. package/dist/veil/filter.js +10 -0
  107. package/dist/veil/scoring.d.ts +18 -0
  108. package/dist/veil/scoring.js +77 -0
  109. package/dist/widgets/dm-thread-handler.d.ts +26 -0
  110. package/dist/widgets/dm-thread-handler.js +56 -0
  111. package/dist/widgets/dm-thread.html +280 -0
  112. package/dist/widgets/feed-widget-handler.d.ts +26 -0
  113. package/dist/widgets/feed-widget-handler.js +54 -0
  114. package/dist/widgets/identity-picker-handler.d.ts +18 -0
  115. package/dist/widgets/identity-picker-handler.js +35 -0
  116. package/dist/widgets/identity-picker.html +198 -0
  117. package/dist/widgets/index.d.ts +17 -0
  118. package/dist/widgets/index.js +101 -0
  119. package/dist/widgets/profile-images.d.ts +11 -0
  120. package/dist/widgets/profile-images.js +72 -0
  121. package/dist/widgets/social-feed.html +219 -0
  122. package/dist/workflow/handlers.d.ts +152 -0
  123. package/dist/workflow/handlers.js +593 -0
  124. package/dist/workflow/tools.d.ts +14 -0
  125. package/dist/workflow/tools.js +98 -0
  126. package/dist/zap/handlers.d.ts +11 -0
  127. package/dist/zap/handlers.js +33 -0
  128. package/dist/zap/tools.js +51 -6
  129. package/llms-full.txt +176 -42
  130. package/llms.txt +95 -9
  131. package/package.json +23 -29
package/README.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # nostr-bray
2
2
 
3
- > Sovereign Nostr identities for AI agents
3
+ **Trust-aware Nostr for AI and humans.**
4
+
5
+ Three dimensions of trust -- verification, proximity, and access -- woven into every interaction. 185 tools for identity, social, payments, moderation, privacy, and encrypted access control.
6
+
7
+ | Dimension | Source | Question |
8
+ |-----------|--------|----------|
9
+ | **Verification** | Signet | Are they real? |
10
+ | **Proximity** | Web of Trust | Do I know them? |
11
+ | **Access** | Dominion | What can they see? |
4
12
 
5
13
  [![CI](https://github.com/forgesworn/bray/actions/workflows/ci.yml/badge.svg)](https://github.com/forgesworn/bray/actions/workflows/ci.yml)
6
14
  [![npm](https://img.shields.io/npm/v/nostr-bray)](https://www.npmjs.com/package/nostr-bray)
@@ -8,7 +16,7 @@
8
16
  [![licence](https://img.shields.io/npm/l/nostr-bray)](./LICENSE)
9
17
  [![TypeScript](https://img.shields.io/badge/TypeScript-ESM-blue)](./tsconfig.json)
10
18
 
11
- An MCP server that gives AI agents a full Nostr identity — not just a key pair, but a hierarchical identity tree with personas, attestations, ring signatures, encrypted DMs, and duress detection. 77 tools across 10 groups.
19
+ An MCP server that gives AI agents a full Nostr identity: not just a key pair, but a hierarchical identity tree with personas, attestations, ring signatures, encrypted DMs, duress detection, identity verification via Signet, and epoch-based encrypted access control via Dominion. 185 tools across 17 groups.
12
20
 
13
21
  ## The Problem
14
22
 
@@ -18,7 +26,7 @@ nostr-bray solves this with nsec-tree hierarchical derivation. A single master s
18
26
 
19
27
  ## How It Works
20
28
 
21
- Every tool operates as the "active identity." Derive a persona, switch to it, and everything you do is signed by that persona's key — cryptographically unlinkable to the master unless you publish a proof.
29
+ Every tool operates as the "active identity." Derive a persona, switch to it, and everything you do is signed by that persona's key -- cryptographically unlinkable to the master unless you publish a proof.
22
30
 
23
31
  ```
24
32
  identity-derive-persona("work") → npub1abc...
@@ -28,34 +36,38 @@ identity-switch("master") → back to master
28
36
  social-post("Back to main") → signed by master npub
29
37
  ```
30
38
 
31
- This is not just key management — it is context isolation. Each persona has its own relay set, its own contact list, and its own attestation chain. Compromise one and the others remain intact.
32
-
33
- ## Comparison
34
-
35
- | Feature | nostr-bray | nak | nostr-mcp | nostr-tools |
36
- |---------|-----------|-----|-----------|-------------|
37
- | MCP server | 77 tools | [via `nak mcp`](https://github.com/fiatjaf/nak) | 5–23 tools | — |
38
- | CLI | 77 commands | ✓ (mature) | — | — |
39
- | Hierarchical identity (nsec-tree) | ✓ | — | — | — |
40
- | Persona switching | ✓ | — | — | — |
41
- | Ring signatures (anonymous proofs) | ✓ | — | — | — |
42
- | Shamir backup (BIP-39 words) | ✓ | — | — | — |
43
- | Duress detection | ✓ | — | — | — |
44
- | NIP-VA attestations (kind 31000) | ✓ | — | — | — |
45
- | Linkage proofs (blind/full) | ✓ | — | — | — |
46
- | NIP-46 bunker (server + client) | ✓ | ✓ (client) | partial | — |
47
- | NIP-17 encrypted DMs | ✓ | ✓ | ✓ | ✓ |
48
- | NWC Lightning payments | ✓ | — | — | — |
49
- | Blossom media | ✓ | ✓ | — | — |
50
- | NIP-29 groups | ✓ | — | — | — |
51
- | Tor routing (SOCKS5h) | ✓ | — | — | — |
52
- | 329 tests, 96% coverage | ✓ | — | — | — |
53
-
54
- ## Quick Start — CLI
39
+ This is not just key management: it is context isolation. Each persona loads its own NIP-65 relay list on switch, signs with its own key, and maintains its own attestation chain. Compromise one and the others remain intact.
40
+
41
+ ## Relationship to the Nostr Ecosystem
42
+
43
+ nostr-bray stands on the shoulders of the existing Nostr tooling:
44
+
45
+ - **[nostr-tools](https://github.com/nbd-wtf/nostr-tools)** (838 stars, 1.2M monthly npm downloads) is our primary dependency. It handles event creation, signing, NIP-44 encryption, relay connections, and most of the protocol-level heavy lifting. If you are building a Nostr client in JavaScript, nostr-tools is the standard.
46
+ - **[nak](https://github.com/fiatjaf/nak)** is the definitive Nostr CLI, written in Go by fiatjaf (the Nostr protocol creator). It covers far more ground than nostr-bray does: MuSig2 collaborative signing, a built-in relay server with negentropy sync, a FUSE filesystem, NIP-60 Cashu wallet, smart publishing with outbox routing, PoW mining, NIP-86 relay admin, paginated bulk downloads, and a full bunker (server + client) with persistence and QR codes. If you want a power-user Swiss Army knife for Nostr, nak is it. It also has [MCP support](https://github.com/fiatjaf/nak).
47
+
48
+ **What nostr-bray adds** is a narrow, opinionated layer on top: sovereign identity for AI agents. The features that are unique to nostr-bray all revolve around that theme:
49
+
50
+ | Capability | What it does |
51
+ |-----------|-------------|
52
+ | Hierarchical identity (nsec-tree) | Derive unlimited child keys from one master secret |
53
+ | Persona switching | Change the active signing identity mid-session |
54
+ | Ring signatures | Prove group membership without revealing which member you are |
55
+ | Shamir backup | Split the master secret into BIP-39 word shares for social recovery |
56
+ | Duress detection | An alternative identity that silently signals coercion |
57
+ | Verifiable attestations | NIP-VA kind 31000 creation, verification, and revocation |
58
+ | Linkage proofs | Prove (or selectively hide) links between personas |
59
+ | Spoken verification | HMAC-based spoken word tokens for in-person identity checks |
60
+ | WoT-scored feeds | Trust-filtered social feeds via nostr-veil integration |
61
+ | Relay health monitoring | Detect dead or unreliable relays before they lose your data |
62
+ | Contact list protection | Safety guard against accidental follow list destruction |
63
+
64
+ It also bundles NIP-46 bunker auth (your key never leaves the signer), NWC Lightning payments, NIP-29 groups, and a full social toolkit (post, reply, DM, follow, feed) into a single MCP server, so an AI agent can get a complete Nostr identity out of the box without stitching together multiple tools.
65
+
66
+ ## Quick Start -- CLI
55
67
 
56
68
  ```bash
57
69
  # Post a note to Nostr from your terminal
58
- export NOSTR_SECRET_KEY="nsec1..."
70
+ export NOSTR_SECRET_KEY="nsec1..." # env var (least safe -- bunker or file preferred)
59
71
  export NOSTR_RELAYS="wss://relay.damus.io,wss://nos.lol"
60
72
 
61
73
  npx nostr-bray whoami # show your npub
@@ -65,10 +77,12 @@ npx nostr-bray prove blind # create a linkage proof
65
77
  npx nostr-bray --help # see all commands
66
78
  ```
67
79
 
68
- ## Quick Start — MCP Server
80
+ ## Quick Start -- MCP Server
69
81
 
70
82
  Add to your Claude/Cursor/Windsurf MCP config:
71
83
 
84
+ **Recommended -- NIP-46 bunker (safest: key never leaves your device):**
85
+
72
86
  ```json
73
87
  {
74
88
  "mcpServers": {
@@ -76,7 +90,7 @@ Add to your Claude/Cursor/Windsurf MCP config:
76
90
  "command": "npx",
77
91
  "args": ["nostr-bray"],
78
92
  "env": {
79
- "NOSTR_SECRET_KEY": "nsec1...",
93
+ "BUNKER_URI": "bunker://...",
80
94
  "NOSTR_RELAYS": "wss://relay.damus.io,wss://nos.lol"
81
95
  }
82
96
  }
@@ -84,7 +98,25 @@ Add to your Claude/Cursor/Windsurf MCP config:
84
98
  }
85
99
  ```
86
100
 
87
- Or with a secret file (recommended):
101
+ **Or with an NIP-49 encrypted key (ncryptsec):**
102
+
103
+ ```json
104
+ {
105
+ "mcpServers": {
106
+ "nostr": {
107
+ "command": "npx",
108
+ "args": ["nostr-bray"],
109
+ "env": {
110
+ "NOSTR_NCRYPTSEC": "ncryptsec1...",
111
+ "NOSTR_NCRYPTSEC_PASSWORD": "your-password",
112
+ "NOSTR_RELAYS": "wss://relay.damus.io,wss://nos.lol"
113
+ }
114
+ }
115
+ }
116
+ }
117
+ ```
118
+
119
+ **Or with a secret file:**
88
120
 
89
121
  ```json
90
122
  {
@@ -101,9 +133,11 @@ Or with a secret file (recommended):
101
133
  }
102
134
  ```
103
135
 
136
+ Auth tier progression (safest to least safe): **bunker** > **ncryptsec** > **file** > **env var**
137
+
104
138
  ## Tool Groups
105
139
 
106
- ### Identity (12 tools) — create, derive, switch, prove, backup, and migrate Nostr identities
140
+ ### Identity (15 tools) -- create, derive, switch, prove, backup, migrate, and resolve NIP-05 identifiers
107
141
 
108
142
  | Tool | Description |
109
143
  |------|-------------|
@@ -111,7 +145,7 @@ Or with a secret file (recommended):
111
145
  | `identity-create` | Generate a fresh identity with BIP-39 mnemonic |
112
146
  | `identity-derive` | Derive a child identity by purpose and index |
113
147
  | `identity-derive-persona` | Derive a named persona (work, personal, anonymous) |
114
- | `identity-switch` | Switch active identity — all tools operate as the new identity |
148
+ | `identity-switch` | Switch active identity -- all tools operate as the new identity |
115
149
  | `identity-list` | List all known identities (public info only) |
116
150
  | `identity-prove` | Create blind/full linkage proof |
117
151
  | `identity-backup-shamir` | Split master secret into Shamir shard files |
@@ -119,8 +153,11 @@ Or with a secret file (recommended):
119
153
  | `identity-backup` | Fetch profile, contacts, relay list as portable bundle |
120
154
  | `identity-restore` | Re-sign migratable events under the active identity |
121
155
  | `identity-migrate` | Full migration with preview, confirmation, and linkage proof |
156
+ | `nip05-lookup` | Resolve a NIP-05 identifier (user@domain) to a pubkey and relay hints |
157
+ | `nip05-verify` | Confirm a pubkey matches a claimed NIP-05 identifier |
158
+ | `nip05-relays` | Fetch relay hints from a NIP-05 server for a given identifier |
122
159
 
123
- ### Social (14 tools) — post, reply, react, DM, follow, and read feeds
160
+ ### Social (15 tools) -- post, reply, react, DM, follow, and read feeds
124
161
 
125
162
  | Tool | Description |
126
163
  |------|-------------|
@@ -131,41 +168,69 @@ Or with a secret file (recommended):
131
168
  | `social-repost` | Repost/boost an event (kind 6) |
132
169
  | `social-profile-get` | Fetch and parse a kind 0 profile |
133
170
  | `social-profile-set` | Set profile with overwrite safety guard |
134
- | `dm-send` | Send encrypted DM (NIP-17 default, NIP-04 opt-in) |
135
- | `dm-read` | Read and decrypt received DMs |
171
+ | `social-notifications` | Fetch mentions, replies, reactions, zap receipts |
172
+ | `social-feed` | Fetch kind 1 text note feed |
173
+ | `feed-by-name` | Fetch a named persona's feed without switching identity |
174
+ | `profile-by-name` | Fetch a named persona's profile without switching identity |
136
175
  | `contacts-get` | Fetch contact list (kind 3 follows) |
176
+ | `contacts-search` | Search contacts by name or NIP-05 identifier |
137
177
  | `contacts-follow` | Follow a pubkey (publishes updated kind 3) |
138
178
  | `contacts-unfollow` | Unfollow a pubkey |
139
- | `social-notifications` | Fetch mentions, replies, reactions, zap receipts |
140
- | `social-feed` | Fetch kind 1 text note feed |
141
179
 
142
- ### Trust (11 tools) — attestations, ring signatures, linkage proofs, spoken verification
180
+ ### Direct Messages (4 tools) -- NIP-17 gift-wrapped DMs and conversation management
181
+
182
+ | Tool | Description |
183
+ |------|-------------|
184
+ | `dm-send` | Send encrypted DM (NIP-17 default, NIP-04 opt-in) |
185
+ | `dm-read` | Read and decrypt received DMs |
186
+ | `dm-by-name` | Read DMs for a named persona without switching identity |
187
+ | `dm-conversation` | Fetch a full conversation thread with a specific pubkey |
188
+
189
+ ### Trust (22 tools) -- attestations, ring signatures, linkage proofs, spoken verification
143
190
 
144
191
  | Tool | Description |
145
192
  |------|-------------|
146
193
  | `trust-attest` | Create kind 31000 verifiable attestation |
194
+ | `trust-claim` | Publish a self-signed trust claim |
147
195
  | `trust-read` | Read attestations by subject/type/attestor |
148
196
  | `trust-verify` | Validate attestation structure |
149
197
  | `trust-revoke` | Revoke an attestation (identity check) |
150
198
  | `trust-request` | Send attestation request via NIP-17 |
151
199
  | `trust-request-list` | Scan DMs for attestation requests |
152
200
  | `trust-proof-publish` | Publish linkage proof (kind 30078) with confirmation |
153
- | `trust-ring-prove` | Anonymous group membership proof (ring signature) |
154
- | `trust-ring-verify` | Verify ring signature proof |
201
+ | `trust-ring-prove` | Anonymous group membership proof (SAG ring signature) |
202
+ | `trust-ring-verify` | Verify SAG ring signature proof |
203
+ | `trust-ring-lsag-sign` | Linkable ring signature (LSAG) -- detects double-signing |
204
+ | `trust-ring-lsag-verify` | Verify LSAG ring signature with key-image linkability check |
205
+ | `trust-ring-key-image` | Derive the key image for your key in a given ring |
155
206
  | `trust-spoken-challenge` | Generate spoken verification token |
156
207
  | `trust-spoken-verify` | Verify spoken token response |
208
+ | `trust-spoken-directional` | Generate directional spoken token (sender to recipient binding) |
209
+ | `trust-spoken-encode` | Encode a spoken token for transmission |
210
+ | `trust-attest-parse` | Parse raw attestation event into structured fields |
211
+ | `trust-attest-filter` | Filter a list of attestations by type, issuer, or validity |
212
+ | `trust-attest-temporal` | Check attestation validity at a specific point in time |
213
+ | `trust-attest-chain` | Resolve a chain of delegated attestations |
214
+ | `trust-attest-check-revoked` | Check whether an attestation has been revoked |
157
215
 
158
- ### Relay (5 tools) — per-identity relay lists, NIP-65 management, direct queries
216
+ ### Relay (12 tools) -- per-identity relay lists, NIP-65 management, direct queries, and intelligence
159
217
 
160
218
  | Tool | Description |
161
219
  |------|-------------|
162
220
  | `relay-list` | List relays with shared-relay warnings |
163
221
  | `relay-set` | Publish kind 10002 relay list |
164
222
  | `relay-add` | Add relay to active identity (in-memory) |
165
- | `relay-query` | Query events from relays by kind, author, tags, or time range |
223
+ | `relay-query` | Query events by kind, author, tags, time range, or full-text search (NIP-50) |
166
224
  | `relay-info` | Fetch NIP-11 relay information document |
225
+ | `relay-count` | Count events matching a filter without fetching them (NIP-45) |
226
+ | `relay-auth` | Authenticate to a relay that requires NIP-42 AUTH |
227
+ | `relay-discover` | Discover relays via NIP-65 relay lists from your network |
228
+ | `relay-nip-search` | Find relays that support a specific NIP |
229
+ | `relay-compare` | Compare two relay sets for coverage overlap |
230
+ | `relay-diversity` | Score a relay set for geographic and operator diversity |
231
+ | `relay-recommend` | Recommend relays based on your network's NIP-65 lists |
167
232
 
168
- ### Zap (7 tools) — Lightning payments and invoices via Nostr Wallet Connect
233
+ ### Zap (7 tools) -- Lightning payments and invoices via Nostr Wallet Connect
169
234
 
170
235
  | Tool | Description |
171
236
  |------|-------------|
@@ -177,22 +242,67 @@ Or with a secret file (recommended):
177
242
  | `zap-receipts` | Parse zap receipts (amount, sender, message) |
178
243
  | `zap-decode` | Decode bolt11 invoice fields |
179
244
 
180
- ### Safety (2 tools) — duress personas for coercion resistance
245
+ ### Safety (14 tools) -- duress personas and CANARY liveness proofs
181
246
 
182
247
  | Tool | Description |
183
248
  |------|-------------|
184
249
  | `safety-configure` | Configure an alternative identity persona |
185
250
  | `safety-activate` | Switch to alternative identity |
251
+ | `canary-session-create` | Create a personal liveness canary (auto-expires) |
252
+ | `canary-session-current` | Fetch the current canary token for your session |
253
+ | `canary-session-verify` | Verify a canary is still live and un-tripped |
254
+ | `canary-group-create` | Create a shared CANARY group with member keys |
255
+ | `canary-group-join` | Join an existing CANARY group |
256
+ | `canary-group-current` | Fetch the current canary for a group |
257
+ | `canary-group-verify` | Verify a group canary is still live |
258
+ | `canary-group-members` | List members and admins of a CANARY group |
259
+ | `canary-beacon-create` | Publish a CANARY beacon event to Nostr |
260
+ | `canary-beacon-check` | Check whether a beacon is still live |
261
+ | `canary-duress-signal` | Emit a covert duress signal via canary trip |
262
+ | `canary-duress-detect` | Detect whether a canary trip was a duress signal |
263
+
264
+ ### Signet (7 tools) -- identity verification and credential checks
265
+
266
+ | Tool | Description |
267
+ |------|-------------|
268
+ | `signet-badge` | Fetch the Signet verification badge for a pubkey |
269
+ | `signet-vouch` | Vouch for another identity's Signet credential |
270
+ | `signet-credentials` | List credentials associated with a Signet identity |
271
+ | `signet-policy-check` | Check whether a pubkey satisfies a verification policy |
272
+ | `signet-policy-set` | Set a verification policy for an interaction context |
273
+ | `signet-verifiers` | List trusted verifiers in the Signet network |
274
+ | `signet-challenge` | Issue a Signet verification challenge |
275
+
276
+ ### Vault (9 tools) -- epoch-based encrypted access control via Dominion
186
277
 
187
- ### Blossom (3 tools) — media uploads and management
278
+ | Tool | Description |
279
+ |------|-------------|
280
+ | `vault-create` | Create an encrypted vault with Dominion epoch key |
281
+ | `vault-encrypt` | Encrypt content into a vault |
282
+ | `vault-share` | Share vault access with a pubkey at a given tier |
283
+ | `vault-read` | Decrypt and read vault content (own vault) |
284
+ | `vault-read-shared` | Decrypt content using a vault key shared by another identity |
285
+ | `vault-revoke` | Revoke a pubkey's vault access |
286
+ | `vault-members` | List members and their access tiers |
287
+ | `vault-config` | View or update vault configuration |
288
+ | `vault-rotate` | Rotate the vault epoch key |
289
+
290
+ ### Blossom (10 tools) -- media uploads, verification, and server management
188
291
 
189
292
  | Tool | Description |
190
293
  |------|-------------|
191
- | `blossom-upload` | Upload file to a blossom media server |
294
+ | `blossom-upload` | Upload file to a Blossom media server |
192
295
  | `blossom-list` | List blobs for a pubkey |
193
296
  | `blossom-delete` | Delete a blob by SHA-256 hash |
297
+ | `blossom-mirror` | Mirror a blob to an additional server |
298
+ | `blossom-check` | Check whether a blob exists on a server |
299
+ | `blossom-discover` | Discover Blossom servers from a pubkey's NIP-96 list |
300
+ | `blossom-verify` | Verify a blob's SHA-256 hash matches its content |
301
+ | `blossom-repair` | Re-upload a blob to servers that are missing it |
302
+ | `blossom-usage` | Report storage usage across all servers |
303
+ | `blossom-servers` | List configured Blossom servers |
194
304
 
195
- ### Groups — NIP-29 (4 tools) — group chat, metadata, and membership
305
+ ### Groups -- NIP-29 (4 tools) -- group chat, metadata, and membership
196
306
 
197
307
  | Tool | Description |
198
308
  |------|-------------|
@@ -201,14 +311,67 @@ Or with a secret file (recommended):
201
311
  | `group-send` | Send message to a group |
202
312
  | `group-members` | List group members |
203
313
 
204
- ### Community NIPs (2 tools) — publish and read community-proposed NIPs
314
+ ### Community NIPs (2 tools) -- publish and read community-proposed NIPs
205
315
 
206
316
  | Tool | Description |
207
317
  |------|-------------|
208
318
  | `nip-publish` | Publish a community NIP (kind 30817) |
209
319
  | `nip-read` | Fetch community NIPs |
210
320
 
211
- ### Utility (18 tools) — encode, decode, encrypt, verify, filter, fetch, browse NIPs
321
+ ### Moderation (16 tools) -- labels, mute lists, pin lists, follow sets, and bookmarks
322
+
323
+ | Tool | Description |
324
+ |------|-------------|
325
+ | `label-create` | Create a NIP-32 label event (kind 1985) for content or identity |
326
+ | `label-self` | Self-label your own content or identity |
327
+ | `label-read` | Read labels applied to an event or pubkey |
328
+ | `label-search` | Search labels by namespace, value, or target |
329
+ | `label-remove` | Delete a label event (kind 5 deletion) |
330
+ | `list-mute` | Add a pubkey, event, keyword, or hashtag to your mute list |
331
+ | `list-mute-read` | Read your current mute list (NIP-51, kind 10000) |
332
+ | `list-check-muted` | Check whether a pubkey or event is in your mute list |
333
+ | `list-pin` | Add or remove events from your pin list (NIP-51, kind 10001) |
334
+ | `list-pin-read` | Read your pinned events list |
335
+ | `list-followset-create` | Create a named follow set (NIP-51, kind 30000) |
336
+ | `list-followset-manage` | Add or remove pubkeys from a named follow set |
337
+ | `list-followset-read` | Read a named follow set |
338
+ | `list-bookmark` | Add an event to your bookmarks list (NIP-51, kind 10003) |
339
+ | `list-bookmark-read` | Read your bookmarks list |
340
+ | `moderation-filter` | Apply a moderation filter to a list of events or pubkeys |
341
+
342
+ ### Privacy (10 tools) -- Pedersen commitments and zero-knowledge range proofs
343
+
344
+ | Tool | Description |
345
+ |------|-------------|
346
+ | `privacy-commit` | Create a Pedersen commitment to a secret value |
347
+ | `privacy-open` | Verify a commitment opening (value + blinding factor) |
348
+ | `privacy-prove-range` | Prove a value is within [min, max] without revealing it |
349
+ | `privacy-verify-range` | Verify a range proof against a commitment |
350
+ | `privacy-prove-age` | Prove age is within a range (e.g. "18+") without revealing exact age |
351
+ | `privacy-verify-age` | Verify an age range proof |
352
+ | `privacy-prove-threshold` | Prove a value exceeds a threshold without revealing the exact value |
353
+ | `privacy-verify-threshold` | Verify a threshold proof |
354
+ | `privacy-publish-proof` | Publish a range proof as a kind 30078 Nostr event |
355
+ | `privacy-read-proof` | Fetch and verify range proof events from relays |
356
+
357
+ ### Marketplace (12 tools) -- discover, probe, call, and publish L402/x402 paid services
358
+
359
+ | Tool | Description |
360
+ |------|-------------|
361
+ | `marketplace-discover` | Discover L402/x402 paid API services announced on Nostr (kind 31402) |
362
+ | `marketplace-inspect` | Get full details of a specific L402 service by event ID or identifier |
363
+ | `marketplace-search` | Full-text search across L402 service names, descriptions, and topics |
364
+ | `marketplace-reputation` | Check a service provider's reputation via attestations and zap history |
365
+ | `marketplace-compare` | Compare two or more L402 services side-by-side |
366
+ | `marketplace-probe` | Test connectivity and response to a paid API endpoint |
367
+ | `marketplace-pay` | Pay the L402 challenge for a service and store the credential |
368
+ | `marketplace-call` | Call a paid API endpoint using a stored L402 credential |
369
+ | `marketplace-announce` | Publish a kind 31402 L402 service announcement |
370
+ | `marketplace-update` | Update a previously published service announcement |
371
+ | `marketplace-retire` | Retire a service announcement (kind 5 deletion) |
372
+ | `marketplace-credentials-clear` | Clear stored L402 credentials for a service |
373
+
374
+ ### Utility (19 tools) -- encode, decode, encrypt, verify, filter, fetch, and browse NIPs
212
375
 
213
376
  | Tool | Description |
214
377
  |------|-------------|
@@ -230,24 +393,106 @@ Or with a secret file (recommended):
230
393
  | `fetch` | Fetch events by nip19 code |
231
394
  | `nip-list` | List all official NIPs |
232
395
  | `nip-show` | Show a specific NIP's content |
396
+ | `tombstone` | Publish a kind 5 deletion event for any event you authored |
397
+
398
+ ### Workflow (7 tools) -- trust scoring, discovery, verification, identity setup, relay health
399
+
400
+ | Tool | Description |
401
+ |------|-------------|
402
+ | `trust-score` | Compute WoT trust score for any pubkey -- combines NIP-85 assertions, NIP-VA attestations, and social distance |
403
+ | `feed-discover` | Discover accounts to follow via trust-adjacent, topic, or activity strategies |
404
+ | `verify-person` | Verify identity with attestations, NIP-05, linkage proofs, ring endorsements, and spoken challenges |
405
+ | `identity-setup` | Guided safe identity creation -- derives personas, creates Shamir backup, configures relays |
406
+ | `identity-recover` | Recover identity from Shamir backup shards with verification |
407
+ | `relay-health` | Check relay set health -- reachability, NIP support, event presence, write access |
408
+ | `search-actions` | Search across all available tool actions by keyword |
409
+
410
+ ## NIP Support
411
+
412
+ | NIP | Description | Coverage |
413
+ |-----|-------------|----------|
414
+ | NIP-01 | Basic protocol, event signing | Full |
415
+ | NIP-02 | Follow list (kind 3) | Full |
416
+ | NIP-04 | Encrypted DMs (legacy) | Opt-in (`NIP04_ENABLED=1`) |
417
+ | NIP-05 | DNS identity verification | Full (lookup, verify, relay hints) |
418
+ | NIP-09 | Event deletion (kind 5) | Full |
419
+ | NIP-11 | Relay information document | Full |
420
+ | NIP-17 | Private DMs (gift wrap) | Full (default) |
421
+ | NIP-19 | bech32 encoding (npub, nsec, nprofile, nevent, naddr) | Full |
422
+ | NIP-29 | Group chat | Full |
423
+ | NIP-32 | Labels (kind 1985) | Full |
424
+ | NIP-40 | Expiration tag | Full |
425
+ | NIP-42 | Relay authentication | Full (`relay-auth`) |
426
+ | NIP-44 | Encrypted payloads v2 | Full (default for DMs and NWC) |
427
+ | NIP-45 | Event counts (COUNT) | Full (`relay-count`, with fallback) |
428
+ | NIP-46 | Nostr Connect (bunker) | Full |
429
+ | NIP-49 | Private key encryption | Full (ncryptsec) |
430
+ | NIP-50 | Search capability | Full (`search` param on `relay-query`) |
431
+ | NIP-51 | Lists (mute, pin, follow sets, bookmarks) | Full |
432
+ | NIP-57 | Lightning zaps | Full |
433
+ | NIP-65 | Relay list metadata | Full (per-identity, signature-verified) |
434
+ | NIP-78 | Application-specific data | Full (proofs, linkage events) |
435
+ | NIP-85 | Trust rankings | Full (WoT scoring) |
436
+ | NIP-96 | HTTP file storage | Full (Blossom) |
437
+ | NIP-VA | Verifiable attestations (kind 31000) | Full |
233
438
 
234
439
  ## Configuration
235
440
 
441
+ ### Config file (recommended)
442
+
443
+ Create `~/.config/bray/config.json` (or `~/.nostr/bray.json`):
444
+
445
+ ```json
446
+ {
447
+ "bunkerUriFile": "/Users/you/.nostr/bunker-uri",
448
+ "relays": ["wss://relay.damus.io", "wss://nos.lol"],
449
+ "trustMode": "annotate"
450
+ }
451
+ ```
452
+
453
+ Secrets are referenced by **file path** (`bunkerUriFile`, `secretKeyFile`, `nwcUriFile`) so they never appear in the config itself. This avoids exposing secrets in process environment listings.
454
+
455
+ Search order: `BRAY_CONFIG` env var > `$XDG_CONFIG_HOME/bray/config.json` > `~/.nostr/bray.json`.
456
+
457
+ MCP server config becomes minimal:
458
+
459
+ ```json
460
+ {
461
+ "mcpServers": {
462
+ "nostr-bray": {
463
+ "command": "nostr-bray"
464
+ }
465
+ }
466
+ }
467
+ ```
468
+
469
+ ### Environment variables
470
+
471
+ Env vars override config file values. Use when you need per-invocation overrides.
472
+
236
473
  | Variable | Description |
237
474
  |----------|-------------|
475
+ | `BRAY_CONFIG` | Path to config file (overrides default search) |
476
+ | `BUNKER_URI` | NIP-46 bunker URL -- safest option, key stays on your device |
477
+ | `BUNKER_URI_FILE` | Path to bunker URI file (preferred over env var) |
238
478
  | `NOSTR_SECRET_KEY` | nsec bech32, 64-char hex, or BIP-39 mnemonic |
239
- | `NOSTR_SECRET_KEY_FILE` | Path to secret key file (takes precedence) |
479
+ | `NOSTR_SECRET_KEY_FILE` | Path to secret key file (takes precedence over env var) |
480
+ | `NOSTR_NCRYPTSEC` | NIP-49 encrypted key (requires `NOSTR_NCRYPTSEC_PASSWORD`) |
481
+ | `NOSTR_NCRYPTSEC_FILE` | Path to NIP-49 encrypted key file (requires `NOSTR_NCRYPTSEC_PASSWORD`) |
482
+ | `NOSTR_NCRYPTSEC_PASSWORD` | Password to decrypt an ncryptsec key |
240
483
  | `NOSTR_RELAYS` | Comma-separated relay URLs |
241
484
  | `TOR_PROXY` | SOCKS5h proxy for Tor (blocks clearnet relays by default) |
242
485
  | `NIP04_ENABLED` | Set `1` to enable legacy NIP-04 DMs |
243
486
  | `TRANSPORT` | `stdio` (default) or `http` |
244
487
  | `PORT` | HTTP port (default 3000) |
245
488
 
489
+ All secret env vars are deleted from `process.env` immediately after parsing.
490
+
246
491
  ## Documentation
247
492
 
248
- - **[Usage Guide](./docs/guide.md)** — walkthroughs for identity management, DMs, attestations, NWC payments, Tor, and duress
249
- - **[Examples](./examples/)** — MCP config files (basic, NWC, Tor) and a CLI workflow script
250
- - **[Contributing](./CONTRIBUTING.md)** — setup, architecture, testing, and conventions
493
+ - **[Usage Guide](./docs/guide.md)** -- walkthroughs for identity management, DMs, attestations, NWC payments, and duress
494
+ - **[Examples](./examples/)** -- MCP config files (basic, NWC, bunker) and a CLI workflow script
495
+ - **[Contributing](./CONTRIBUTING.md)** -- setup, architecture, testing, and conventions
251
496
 
252
497
  ## For AI Assistants
253
498
 
package/dist/cli.js CHANGED
@@ -17,7 +17,7 @@ import { handleTrustAttest, handleTrustRead, handleTrustVerify, handleTrustRevok
17
17
  import { handleTrustRingProve, handleTrustRingVerify } from './trust/ring.js';
18
18
  import { handleTrustSpokenChallenge, handleTrustSpokenVerify } from './trust/spoken.js';
19
19
  import { handleDuressConfigure, handleDuressActivate } from './safety/handlers.js';
20
- import { handleZapSend, handleZapBalance, handleZapMakeInvoice, handleZapLookupInvoice, handleZapListTransactions, handleZapReceipts, handleZapDecode } from './zap/handlers.js';
20
+ import { handleZapSend, handleZapBalance, handleZapMakeInvoice, handleZapLookupInvoice, handleZapListTransactions, handleZapReceipts, handleZapDecode, resolveNwcUri } from './zap/handlers.js';
21
21
  import { handleDecode, handleEncodeNpub, handleEncodeNote, handleEncodeNprofile, handleEncodeNevent, handleVerify, handleEncrypt, handleDecrypt, handleCount, handleFetch, handleKeyPublic, handleEncodeNsec, handleFilter, handleNipList, handleNipShow } from './util/handlers.js';
22
22
  import { handleKeyEncrypt, handleKeyDecrypt } from './util/ncryptsec.js';
23
23
  import { getCommandHelp } from './help.js';
@@ -32,7 +32,7 @@ if (!command) {
32
32
  // Bunker = NIP-46 remote signer
33
33
  if (command === 'bunker' && !args.includes('--help')) {
34
34
  const { startBunker } = await import('./bunker.js');
35
- const config = (await import('./config.js')).loadConfig();
35
+ const config = await (await import('./config.js')).loadConfig();
36
36
  const { IdentityContext: IC } = await import('./context.js');
37
37
  const bCtx = new IC(config.secretKey, config.secretFormat);
38
38
  config.secretKey = '';
@@ -228,7 +228,7 @@ const OFFLINE_COMMANDS = new Set([
228
228
  'key-public', 'key-encrypt', 'key-decrypt', 'filter', 'verify', 'encrypt', 'decrypt',
229
229
  ]);
230
230
  // All other commands need config
231
- const config = loadConfig();
231
+ const config = await loadConfig();
232
232
  const pool = new RelayPool({
233
233
  torProxy: config.torProxy,
234
234
  allowClearnet: config.allowClearnetWithTor || !config.torProxy,
@@ -244,7 +244,8 @@ if (config.bunkerUri) {
244
244
  else {
245
245
  ctx = new IdentityContext(config.secretKey, config.secretFormat);
246
246
  }
247
- const nwcUri = config.nwcUri;
247
+ const globalNwcUri = config.nwcUri;
248
+ const walletsFile = config.walletsFile;
248
249
  config.secretKey = '';
249
250
  config.nwcUri = undefined;
250
251
  // Only fetch NIP-65 relay list for commands that need network access
@@ -549,7 +550,7 @@ async function run(cmdArgs) {
549
550
  break;
550
551
  // === Relay ===
551
552
  case 'relay-list':
552
- out(handleRelayList(ctx, pool, flag('compare')), fmt.formatRelays);
553
+ out(await handleRelayList(ctx, pool, flag('compare')), fmt.formatRelays);
553
554
  break;
554
555
  case 'relay-set': {
555
556
  const urls = args.slice(1).filter(a => !a.startsWith('--'));
@@ -570,25 +571,25 @@ async function run(cmdArgs) {
570
571
  break;
571
572
  // === Zap ===
572
573
  case 'zap-send':
573
- out(await handleZapSend(ctx, pool, { invoice: req(1, 'zap-send <bolt11>'), nwcUri }));
574
+ out(await handleZapSend(ctx, pool, { invoice: req(1, 'zap-send <bolt11>'), nwcUri: resolveNwcUri(ctx, walletsFile, globalNwcUri) }));
574
575
  break;
575
576
  case 'zap-balance':
576
- out(await handleZapBalance(ctx, pool, { nwcUri }));
577
+ out(await handleZapBalance(ctx, pool, { nwcUri: resolveNwcUri(ctx, walletsFile, globalNwcUri) }));
577
578
  break;
578
579
  case 'zap-invoice':
579
580
  out(await handleZapMakeInvoice(ctx, pool, {
580
581
  amountMsats: parseInt(req(1, 'zap-invoice <msats> [description]'), 10),
581
582
  description: cmdArgs[2],
582
- nwcUri,
583
+ nwcUri: resolveNwcUri(ctx, walletsFile, globalNwcUri),
583
584
  }));
584
585
  break;
585
586
  case 'zap-lookup':
586
- out(await handleZapLookupInvoice(ctx, pool, { paymentHash: req(1, 'zap-lookup <payment-hash>'), nwcUri }));
587
+ out(await handleZapLookupInvoice(ctx, pool, { paymentHash: req(1, 'zap-lookup <payment-hash>'), nwcUri: resolveNwcUri(ctx, walletsFile, globalNwcUri) }));
587
588
  break;
588
589
  case 'zap-transactions':
589
590
  out(await handleZapListTransactions(ctx, pool, {
590
591
  limit: parseInt(flag('limit', '10'), 10),
591
- nwcUri,
592
+ nwcUri: resolveNwcUri(ctx, walletsFile, globalNwcUri),
592
593
  }));
593
594
  break;
594
595
  case 'zap-receipts':
package/dist/config.d.ts CHANGED
@@ -1,5 +1,41 @@
1
1
  import type { BrayConfig } from './types.js';
2
2
  /** Auto-detect key format from string content */
3
3
  export declare function detectKeyFormat(key: string): 'nsec' | 'hex' | 'mnemonic';
4
- /** Load configuration from environment variables and optional secret files */
5
- export declare function loadConfig(): BrayConfig;
4
+ /** Supported config file fields (subset of BrayConfig + secret paths) */
5
+ interface ConfigFile {
6
+ secretKeyFile?: string;
7
+ bunkerUriFile?: string;
8
+ nwcUriFile?: string;
9
+ ncryptsecFile?: string;
10
+ ncryptsecPassword?: string;
11
+ relays?: string[];
12
+ walletsFile?: string;
13
+ torProxy?: string;
14
+ allowClearnetWithTor?: boolean;
15
+ nip04Enabled?: boolean;
16
+ transport?: 'stdio' | 'http';
17
+ port?: number;
18
+ bindAddress?: string;
19
+ trustMode?: 'strict' | 'annotate' | 'off';
20
+ vaultEpochLength?: 'daily' | 'weekly' | 'monthly';
21
+ veilCacheTtl?: number;
22
+ veilCacheMax?: number;
23
+ trustCacheTtl?: number;
24
+ trustCacheMax?: number;
25
+ dispatchIdentities?: string;
26
+ }
27
+ /**
28
+ * Load config from a JSON file. Searches in order:
29
+ * 1. BRAY_CONFIG env var (explicit path)
30
+ * 2. ~/.config/bray/config.json (XDG standard)
31
+ * 3. ~/.nostr/bray.json (Nostr convention)
32
+ *
33
+ * Returns empty object if no config file found.
34
+ * Secrets are referenced by file path (secretKeyFile, bunkerUriFile) —
35
+ * they never appear as values in the config file itself.
36
+ */
37
+ export declare function loadConfigFile(): ConfigFile;
38
+ /** Load configuration from config file, environment variables, and secret files.
39
+ * Priority: env vars > config file > defaults */
40
+ export declare function loadConfig(): Promise<BrayConfig>;
41
+ export {};