nappup 1.8.5 → 1.9.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.
package/README.md CHANGED
@@ -26,7 +26,7 @@ nappup [directory] [options]
26
26
 
27
27
  | Flag | Description |
28
28
  |------|-------------|
29
- | `-s <secret_key>` | Your Nostr secret key (hex or nsec format) used to sign the application event. See [Authentication](#authentication) for alternatives. |
29
+ | `-s <secret_key>` | Your Nostr secret key (hex, nsec, or `bunker://` URL) used to sign the application event. See [Authentication](#authentication) for alternatives. |
30
30
  | `-d <d_tag>` | The identifier (`d` tag) for your application. Any UTF-8 text up to 260 characters. If omitted, defaults to the directory name. Avoid generic names like `dist` or `build` - use something unique among your other apps like `mycoolapp`. |
31
31
  | `-y` | Skip confirmation prompt. Useful for CI/CD pipelines or automated scripts. |
32
32
  | `-r` | Force re-upload. By default, Napp Up! might skip files that haven't changed. Use this flag to ensure everything is pushed fresh. |
@@ -43,13 +43,18 @@ Napp Up! supports multiple ways to provide your Nostr secret key:
43
43
  nappup -s nsec1...
44
44
  ```
45
45
 
46
- 2. **Environment variable**: Set `NOSTR_SECRET_KEY` in your environment or a `.env` file:
46
+ 2. **Remote signer (NIP-46)**: Pass a `bunker://` URL to sign events via a remote signer like [nak](https://github.com/fiatjaf/nak?tab=readme-ov-file#start-a-bunker-that-persists-its-metadata-secret-key-relays-authorized-client-pubkeys-to-disc) or [Amber](https://github.com/greenart7c3/Amber):
47
+ ```bash
48
+ nappup -s 'bunker://<pubkey>?relay=wss://relay.example.com&secret=<token>'
49
+ ```
50
+
51
+ 3. **Environment variable**: Set `NOSTR_SECRET_KEY` in your environment or a `.env` file (also supports `bunker://` URLs):
47
52
  ```bash
48
53
  export NOSTR_SECRET_KEY=nsec1...
49
54
  nappup ./dist
50
55
  ```
51
56
 
52
- 3. **Auto-generated key**: If no key is provided, Napp Up! will generate a new keypair automatically and store it (as nsec) in your project's `.env` file for future use.
57
+ 4. **Auto-generated key**: If no key is provided, Napp Up! will generate a new keypair automatically and store it (as nsec) in your project's `.env` file for future use.
53
58
 
54
59
  ### Examples
55
60
 
@@ -34,4 +34,18 @@ await confirmArgs(args)
34
34
 
35
35
  const fileList = await toFileList(getFiles(dir), dir)
36
36
 
37
- await toApp(fileList, await NostrSigner.create(sk), { log: console.log.bind(console), dTag, channel, shouldReupload })
37
+ const bunkerUrl = sk?.startsWith('bunker://') ? sk : (!sk && process.env.NOSTR_SECRET_KEY?.startsWith('bunker://')) ? process.env.NOSTR_SECRET_KEY : null
38
+
39
+ let signer
40
+ if (bunkerUrl) {
41
+ const { default: NostrBunkerSigner } = await import('#services/bunker-signer.js')
42
+ signer = await NostrBunkerSigner.create(bunkerUrl)
43
+ } else {
44
+ signer = await NostrSigner.create(sk)
45
+ }
46
+
47
+ try {
48
+ await toApp(fileList, signer, { log: console.log.bind(console), dTag, channel, shouldReupload })
49
+ } finally {
50
+ await signer.close?.()
51
+ }
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "git+https://github.com/44billion/nappup.git"
7
7
  },
8
8
  "license": "MIT",
9
- "version": "1.8.5",
9
+ "version": "1.9.0",
10
10
  "description": "Nostr App Uploader",
11
11
  "type": "module",
12
12
  "scripts": {
package/src/index.js CHANGED
@@ -75,7 +75,7 @@ export async function toApp (fileList, nostrSigner, { log = () => {}, onEvent =
75
75
  nostrSigner.getRelays = getRelays
76
76
  }
77
77
  const writeRelays = [...new Set((await nostrSigner.getRelays()).write.map(r => r.trim().replace(/\/$/, '')))]
78
- log(`Found ${writeRelays.length} outbox relays for pubkey ${nostrSigner.getPublicKey()}:\n${writeRelays.join(', ')}`)
78
+ log(`Found ${writeRelays.length} outbox relays for pubkey ${await nostrSigner.getPublicKey()}:\n${writeRelays.join(', ')}`)
79
79
  if (writeRelays.length === 0) throw new Error('No outbox relays found')
80
80
 
81
81
  if (typeof dTag === 'string') {
@@ -0,0 +1,55 @@
1
+ import { generateSecretKey } from 'nostr-tools/pure'
2
+ import { BunkerSigner, parseBunkerInput } from 'nostr-tools/nip46'
3
+ import { getRelays } from '#helpers/signer.js'
4
+
5
+ const CONNECT_TIMEOUT = 30_000
6
+ const createToken = Symbol('createToken')
7
+
8
+ export default class NostrBunkerSigner {
9
+ #bunker
10
+ #publicKey // hex, cached
11
+
12
+ constructor (token, bunker, publicKey) {
13
+ if (token !== createToken) throw new Error('Use NostrBunkerSigner.create(bunkerUrl) to instantiate this class.')
14
+ this.#bunker = bunker
15
+ this.#publicKey = publicKey
16
+ }
17
+
18
+ static async create (bunkerUrl) {
19
+ const bp = await parseBunkerInput(bunkerUrl)
20
+ if (!bp) throw new Error('Invalid bunker URL')
21
+ if (bp.relays.length === 0) throw new Error('Bunker URL must include at least one relay (?relay=wss://...)')
22
+
23
+ const clientSk = generateSecretKey()
24
+ const bunker = new BunkerSigner(clientSk, bp)
25
+
26
+ await Promise.race([
27
+ bunker.connect(),
28
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Bunker connection timed out')), CONNECT_TIMEOUT))
29
+ ])
30
+
31
+ const publicKey = await bunker.getPublicKey()
32
+ return new this(createToken, bunker, publicKey)
33
+ }
34
+
35
+ getPublicKey () {
36
+ return this.#publicKey
37
+ }
38
+
39
+ signEvent (event) {
40
+ return this.#bunker.signEvent(event)
41
+ }
42
+
43
+ async getRelays () {
44
+ return getRelays.call(this)
45
+ }
46
+
47
+ nip44 = {
48
+ encrypt: (pubkey, plaintext) => this.#bunker.nip44Encrypt(pubkey, plaintext),
49
+ decrypt: (pubkey, ciphertext) => this.#bunker.nip44Decrypt(pubkey, ciphertext)
50
+ }
51
+
52
+ async close () {
53
+ await this.#bunker.close()
54
+ }
55
+ }
@@ -46,6 +46,7 @@ export default class NostrSigner {
46
46
  let isNewSk = false
47
47
  if (process.env.NOSTR_SECRET_KEY) {
48
48
  let envSk = process.env.NOSTR_SECRET_KEY
49
+ if (envSk.startsWith('bunker://')) throw new Error('bunker:// URLs are not supported by NostrSigner. Use NostrBunkerSigner.create() instead.')
49
50
  if (envSk.startsWith('nsec')) envSk = nsecDecode(envSk)
50
51
  skBytes = base16ToBytes(envSk)
51
52
  } else {