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 +8 -3
- package/bin/nappup/index.js +15 -1
- package/package.json +1 -1
- package/src/index.js +1 -1
- package/src/services/bunker-signer.js +55 -0
- package/src/services/nostr-signer.js +1 -0
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
|
|
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. **
|
|
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
|
-
|
|
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
|
|
package/bin/nappup/index.js
CHANGED
|
@@ -34,4 +34,18 @@ await confirmArgs(args)
|
|
|
34
34
|
|
|
35
35
|
const fileList = await toFileList(getFiles(dir), dir)
|
|
36
36
|
|
|
37
|
-
|
|
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
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 {
|