mirror of
https://github.com/fiatjaf/nak.git
synced 2025-12-08 16:48:51 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd19855543 | ||
|
|
ecfe3a298e | ||
|
|
9c5f68a955 | ||
|
|
0aef173e8b | ||
|
|
6e4a546212 | ||
|
|
55c9d4ee45 | ||
|
|
550c89d8d7 | ||
|
|
1e9be3ed84 | ||
|
|
79cbc57dde | ||
|
|
1e237b4c42 | ||
|
|
89ec8b9822 | ||
|
|
fba83ea39e | ||
|
|
bd5569955c | ||
|
|
35ea2582d8 |
31
README.md
31
README.md
@@ -128,11 +128,18 @@ type the password to decrypt your secret key: **********
|
|||||||
985d66d2644dfa7676e26046914470d66ebc7fa783a3f57f139fde32d0d631d7
|
985d66d2644dfa7676e26046914470d66ebc7fa783a3f57f139fde32d0d631d7
|
||||||
```
|
```
|
||||||
|
|
||||||
### sign an event using a remote NIP-46 bunker
|
### sign an event using [Amber](https://github.com/greenart7c3/Amber) (or other bunker provider)
|
||||||
```shell
|
```shell
|
||||||
|
~> export NOSTR_CLIENT_KEY="$(nak key generate)"
|
||||||
~> nak event --sec 'bunker://a9e0f110f636f3191644110c19a33448daf09d7cda9708a769e91b7e91340208?relay=wss%3A%2F%2Frelay.damus.io&relay=wss%3A%2F%2Frelay.nsecbunker.com&relay=wss%3A%2F%2Fnos.lol&secret=TWfGbjQCLxUf' -c 'hello from bunker'
|
~> nak event --sec 'bunker://a9e0f110f636f3191644110c19a33448daf09d7cda9708a769e91b7e91340208?relay=wss%3A%2F%2Frelay.damus.io&relay=wss%3A%2F%2Frelay.nsecbunker.com&relay=wss%3A%2F%2Fnos.lol&secret=TWfGbjQCLxUf' -c 'hello from bunker'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> Remember to set a `NOSTR_CLIENT_KEY` permanently on your shell, otherwise you'll only be able to use the bunker once. For `bash`:
|
||||||
|
> ```shell
|
||||||
|
> echo 'export NOSTR_CLIENT_KEY="$(nak key generate)"' >> ~/.bashrc
|
||||||
|
> ```
|
||||||
|
|
||||||
### sign an event using a NIP-49 encrypted key
|
### sign an event using a NIP-49 encrypted key
|
||||||
```shell
|
```shell
|
||||||
~> nak event --sec ncryptsec1qggx54cg270zy9y8krwmfz29jyypsuxken2fkk99gr52qhje968n6mwkrfstqaqhq9eq94pnzl4nff437l4lp4ur2cs4f9um8738s35l2esx2tas48thtfhrk5kq94pf9j2tpk54yuermra0xu6hl5ls -c 'hello from encrypted key'
|
~> nak event --sec ncryptsec1qggx54cg270zy9y8krwmfz29jyypsuxken2fkk99gr52qhje968n6mwkrfstqaqhq9eq94pnzl4nff437l4lp4ur2cs4f9um8738s35l2esx2tas48thtfhrk5kq94pf9j2tpk54yuermra0xu6hl5ls -c 'hello from encrypted key'
|
||||||
@@ -167,6 +174,23 @@ listening at [wss://relay.damus.io wss://nos.lol wss://relay.nsecbunker.com]:
|
|||||||
bunker: bunker://f59911b561c37c90b01e9e5c2557307380835c83399756f4d62d8167227e420a?relay=wss%3A%2F%2Frelay.damus.io&relay=wss%3A%2F%2Fnos.lol&relay=wss%3A%2F%2Frelay.nsecbunker.com&secret=XuuiMbcLwuwL
|
bunker: bunker://f59911b561c37c90b01e9e5c2557307380835c83399756f4d62d8167227e420a?relay=wss%3A%2F%2Frelay.damus.io&relay=wss%3A%2F%2Fnos.lol&relay=wss%3A%2F%2Frelay.nsecbunker.com&secret=XuuiMbcLwuwL
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### start a bunker that persists its list of authorized keys to disc
|
||||||
|
```shell
|
||||||
|
~> nak bunker --persist --sec ncryptsec1... relay.nsec.app nos.lol
|
||||||
|
```
|
||||||
|
|
||||||
|
then later just
|
||||||
|
|
||||||
|
```shell
|
||||||
|
~> nak bunker --persist
|
||||||
|
```
|
||||||
|
|
||||||
|
or give it a named profile:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
~> nak bunker --profile myself ...
|
||||||
|
```
|
||||||
|
|
||||||
### generate a NIP-70 protected event with a date set to two weeks ago and some multi-value tags
|
### generate a NIP-70 protected event with a date set to two weeks ago and some multi-value tags
|
||||||
```shell
|
```shell
|
||||||
~> nak event --ts 'two weeks ago' -t '-' -t 'e=f59911b561c37c90b01e9e5c2557307380835c83399756f4d62d8167227e420a;wss://relay.whatever.com;root;a9e0f110f636f3191644110c19a33448daf09d7cda9708a769e91b7e91340208' -t 'p=a9e0f110f636f3191644110c19a33448daf09d7cda9708a769e91b7e91340208;wss://p-relay.com' -c 'I know the future'
|
~> nak event --ts 'two weeks ago' -t '-' -t 'e=f59911b561c37c90b01e9e5c2557307380835c83399756f4d62d8167227e420a;wss://relay.whatever.com;root;a9e0f110f636f3191644110c19a33448daf09d7cda9708a769e91b7e91340208' -t 'p=a9e0f110f636f3191644110c19a33448daf09d7cda9708a769e91b7e91340208;wss://p-relay.com' -c 'I know the future'
|
||||||
@@ -276,6 +300,11 @@ echo "#surely you're joking, mr npub1l2vyh47mk2p0qlsku7hg0vn29faehy9hy34ygaclpn6
|
|||||||
ffmpeg -f alsa -i default -f webm -t 00:00:03 pipe:1 | nak blossom --server blossom.primal.net upload | jq -rc '{content: .url}' | nak event -k 1222 --sec 'bunker://urlgoeshere' pyramid.fiatjaf.com nostr.wine
|
ffmpeg -f alsa -i default -f webm -t 00:00:03 pipe:1 | nak blossom --server blossom.primal.net upload | jq -rc '{content: .url}' | nak event -k 1222 --sec 'bunker://urlgoeshere' pyramid.fiatjaf.com nostr.wine
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### from a file with events get only those that have kind 1111 and were created by a given pubkey
|
||||||
|
```shell
|
||||||
|
~> cat all.jsonl | nak filter -k 1111 > filtered.jsonl
|
||||||
|
```
|
||||||
|
|
||||||
## contributing to this repository
|
## contributing to this repository
|
||||||
|
|
||||||
Use NIP-34 to send your patches to `naddr1qqpkucttqy28wumn8ghj7un9d3shjtnwdaehgu3wvfnsz9nhwden5te0wfjkccte9ehx7um5wghxyctwvsq3gamnwvaz7tmjv4kxz7fwv3sk6atn9e5k7q3q80cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsxpqqqpmej2wctpn`.
|
Use NIP-34 to send your patches to `naddr1qqpkucttqy28wumn8ghj7un9d3shjtnwdaehgu3wvfnsz9nhwden5te0wfjkccte9ehx7um5wghxyctwvsq3gamnwvaz7tmjv4kxz7fwv3sk6atn9e5k7q3q80cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsxpqqqpmej2wctpn`.
|
||||||
|
|||||||
261
bunker.go
261
bunker.go
@@ -1,10 +1,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -17,6 +20,8 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const PERSISTENCE = "PERSISTENCE"
|
||||||
|
|
||||||
var bunker = &cli.Command{
|
var bunker = &cli.Command{
|
||||||
Name: "bunker",
|
Name: "bunker",
|
||||||
Usage: "starts a nip46 signer daemon with the given --sec key",
|
Usage: "starts a nip46 signer daemon with the given --sec key",
|
||||||
@@ -24,6 +29,18 @@ var bunker = &cli.Command{
|
|||||||
Description: ``,
|
Description: ``,
|
||||||
DisableSliceFlagSeparator: true,
|
DisableSliceFlagSeparator: true,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "persist",
|
||||||
|
Usage: "whether to read and store authorized keys from and to a config file",
|
||||||
|
Category: PERSISTENCE,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "profile",
|
||||||
|
Value: "default",
|
||||||
|
Usage: "config file name to use for --persist mode (implies that if provided) -- based on --config-path, i.e. ~/.config/nak/",
|
||||||
|
OnlyOnce: true,
|
||||||
|
Category: PERSISTENCE,
|
||||||
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "sec",
|
Name: "sec",
|
||||||
Usage: "secret key to sign the event, as hex or nsec",
|
Usage: "secret key to sign the event, as hex or nsec",
|
||||||
@@ -43,13 +60,148 @@ var bunker = &cli.Command{
|
|||||||
Aliases: []string{"k"},
|
Aliases: []string{"k"},
|
||||||
Usage: "pubkeys for which we will always respond",
|
Usage: "pubkeys for which we will always respond",
|
||||||
},
|
},
|
||||||
|
&cli.StringSliceFlag{
|
||||||
|
Name: "relay",
|
||||||
|
Usage: "relays to connect to (can also be provided as naked arguments)",
|
||||||
|
Hidden: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Action: func(ctx context.Context, c *cli.Command) error {
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
|
// read config from file
|
||||||
|
config := struct {
|
||||||
|
AuthorizedKeys []nostr.PubKey `json:"authorized-keys"`
|
||||||
|
Secret plainOrEncryptedKey `json:"sec"`
|
||||||
|
Relays []string `json:"relays"`
|
||||||
|
}{
|
||||||
|
AuthorizedKeys: make([]nostr.PubKey, 0, 3),
|
||||||
|
}
|
||||||
|
baseRelaysUrls := appendUnique(c.Args().Slice(), c.StringSlice("relay")...)
|
||||||
|
for i, url := range baseRelaysUrls {
|
||||||
|
baseRelaysUrls[i] = nostr.NormalizeURL(url)
|
||||||
|
}
|
||||||
|
baseAuthorizedKeys := getPubKeySlice(c, "authorized-keys")
|
||||||
|
|
||||||
|
var baseSecret plainOrEncryptedKey
|
||||||
|
{
|
||||||
|
sec := c.String("sec")
|
||||||
|
if c.Bool("prompt-sec") {
|
||||||
|
var err error
|
||||||
|
sec, err = askPassword("type your secret key as ncryptsec, nsec or hex: ", nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get secret key: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(sec, "ncryptsec1") {
|
||||||
|
baseSecret.Encrypted = &sec
|
||||||
|
} else if sec != "" {
|
||||||
|
if prefix, ski, err := nip19.Decode(sec); err == nil && prefix == "nsec" {
|
||||||
|
sk := ski.(nostr.SecretKey)
|
||||||
|
baseSecret.Plain = &sk
|
||||||
|
} else if sk, err := nostr.SecretKeyFromHex(sec); err != nil {
|
||||||
|
return fmt.Errorf("invalid secret key: %w", err)
|
||||||
|
} else {
|
||||||
|
baseSecret.Plain = &sk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// default case: persist() is nil
|
||||||
|
var persist func()
|
||||||
|
|
||||||
|
if c.Bool("persist") || c.IsSet("profile") {
|
||||||
|
path := filepath.Join(c.String("config-path"), "bunker")
|
||||||
|
if err := os.MkdirAll(path, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
path = filepath.Join(path, c.String("profile"))
|
||||||
|
|
||||||
|
persist = func() {
|
||||||
|
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||||
|
log(color.RedString("failed to persist: %w\n"), err)
|
||||||
|
os.Exit(4)
|
||||||
|
}
|
||||||
|
data, err := json.MarshalIndent(config, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
log(color.RedString("failed to persist: %w\n"), err)
|
||||||
|
os.Exit(4)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(path, data, 0600); err != nil {
|
||||||
|
log(color.RedString("failed to persist: %w\n"), err)
|
||||||
|
os.Exit(4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log(color.YellowString("reading config from %s\n"), path)
|
||||||
|
b, err := os.ReadFile(path)
|
||||||
|
if err == nil {
|
||||||
|
if err := json.Unmarshal(b, &config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, url := range config.Relays {
|
||||||
|
config.Relays[i] = nostr.NormalizeURL(url)
|
||||||
|
}
|
||||||
|
config.Relays = appendUnique(config.Relays, baseRelaysUrls...)
|
||||||
|
config.AuthorizedKeys = appendUnique(config.AuthorizedKeys, baseAuthorizedKeys...)
|
||||||
|
|
||||||
|
if config.Secret.Plain == nil && config.Secret.Encrypted == nil {
|
||||||
|
// we don't have any secret key stored, so just use whatever was given via flags
|
||||||
|
config.Secret = baseSecret
|
||||||
|
} else if baseSecret.Plain == nil && baseSecret.Encrypted == nil {
|
||||||
|
// we didn't provide any keys, so we just use the stored
|
||||||
|
} else {
|
||||||
|
// we have a secret key stored
|
||||||
|
// if we also provided a key we check if they match and fail otherwise
|
||||||
|
if !baseSecret.equals(config.Secret) {
|
||||||
|
return fmt.Errorf("--sec provided conflicts with stored, you should create a new --profile or omit the --sec flag")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
config.Secret = baseSecret
|
||||||
|
config.Relays = baseRelaysUrls
|
||||||
|
config.AuthorizedKeys = baseAuthorizedKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we got here without any keys set (no flags, first time using a profile), use the default
|
||||||
|
{
|
||||||
|
sec := os.Getenv("NOSTR_SECRET_KEY")
|
||||||
|
if sec == "" {
|
||||||
|
sec = defaultKey
|
||||||
|
}
|
||||||
|
sk, err := nostr.SecretKeyFromHex(sec)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("default key is wrong: %w", err)
|
||||||
|
}
|
||||||
|
config.Secret.Plain = &sk
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(config.Relays) == 0 {
|
||||||
|
return fmt.Errorf("no relays given")
|
||||||
|
}
|
||||||
|
|
||||||
|
// decrypt key here if necessary
|
||||||
|
var sec nostr.SecretKey
|
||||||
|
if config.Secret.Plain != nil {
|
||||||
|
sec = *config.Secret.Plain
|
||||||
|
} else {
|
||||||
|
plain, err := promptDecrypt(*config.Secret.Encrypted)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to decrypt: %w", err)
|
||||||
|
}
|
||||||
|
sec = plain
|
||||||
|
}
|
||||||
|
|
||||||
|
if persist != nil {
|
||||||
|
persist()
|
||||||
|
}
|
||||||
|
|
||||||
// try to connect to the relays here
|
// try to connect to the relays here
|
||||||
qs := url.Values{}
|
qs := url.Values{}
|
||||||
relayURLs := make([]string, 0, c.Args().Len())
|
relayURLs := make([]string, 0, len(config.Relays))
|
||||||
if relayUrls := c.Args().Slice(); len(relayUrls) > 0 {
|
relays := connectToAllRelays(ctx, c, config.Relays, nil, nostr.PoolOptions{})
|
||||||
relays := connectToAllRelays(ctx, c, relayUrls, nil, nostr.PoolOptions{})
|
|
||||||
if len(relays) == 0 {
|
if len(relays) == 0 {
|
||||||
log("failed to connect to any of the given relays.\n")
|
log("failed to connect to any of the given relays.\n")
|
||||||
os.Exit(3)
|
os.Exit(3)
|
||||||
@@ -58,19 +210,11 @@ var bunker = &cli.Command{
|
|||||||
relayURLs = append(relayURLs, relay.URL)
|
relayURLs = append(relayURLs, relay.URL)
|
||||||
qs.Add("relay", relay.URL)
|
qs.Add("relay", relay.URL)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if len(relayURLs) == 0 {
|
if len(relayURLs) == 0 {
|
||||||
return fmt.Errorf("not connected to any relays: please specify at least one")
|
return fmt.Errorf("not connected to any relays: please specify at least one")
|
||||||
}
|
}
|
||||||
|
|
||||||
// gather the secret key
|
|
||||||
sec, _, err := gatherSecretKeyOrBunkerFromArguments(ctx, c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// other arguments
|
// other arguments
|
||||||
authorizedKeys := getPubKeySlice(c, "authorized-keys")
|
|
||||||
authorizedSecrets := c.StringSlice("authorized-secrets")
|
authorizedSecrets := c.StringSlice("authorized-secrets")
|
||||||
|
|
||||||
// this will be used to auto-authorize the next person who connects who isn't pre-authorized
|
// this will be used to auto-authorize the next person who connects who isn't pre-authorized
|
||||||
@@ -87,9 +231,9 @@ var bunker = &cli.Command{
|
|||||||
bunkerURI := fmt.Sprintf("bunker://%s?%s", pubkey.Hex(), qs.Encode())
|
bunkerURI := fmt.Sprintf("bunker://%s?%s", pubkey.Hex(), qs.Encode())
|
||||||
|
|
||||||
authorizedKeysStr := ""
|
authorizedKeysStr := ""
|
||||||
if len(authorizedKeys) != 0 {
|
if len(config.AuthorizedKeys) != 0 {
|
||||||
authorizedKeysStr = "\n authorized keys:"
|
authorizedKeysStr = "\n authorized keys:"
|
||||||
for _, pubkey := range authorizedKeys {
|
for _, pubkey := range config.AuthorizedKeys {
|
||||||
authorizedKeysStr += "\n - " + colors.italic(pubkey.Hex())
|
authorizedKeysStr += "\n - " + colors.italic(pubkey.Hex())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,7 +244,7 @@ var bunker = &cli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
preauthorizedFlags := ""
|
preauthorizedFlags := ""
|
||||||
for _, k := range authorizedKeys {
|
for _, k := range config.AuthorizedKeys {
|
||||||
preauthorizedFlags += " -k " + k.Hex()
|
preauthorizedFlags += " -k " + k.Hex()
|
||||||
}
|
}
|
||||||
for _, s := range authorizedSecrets {
|
for _, s := range authorizedSecrets {
|
||||||
@@ -121,6 +265,8 @@ var bunker = &cli.Command{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only print the restart command if not persisting:
|
||||||
|
if persist == nil {
|
||||||
restartCommand := fmt.Sprintf("nak bunker %s%s %s",
|
restartCommand := fmt.Sprintf("nak bunker %s%s %s",
|
||||||
secretKeyFlag,
|
secretKeyFlag,
|
||||||
preauthorizedFlags,
|
preauthorizedFlags,
|
||||||
@@ -136,6 +282,17 @@ var bunker = &cli.Command{
|
|||||||
color.CyanString(restartCommand),
|
color.CyanString(restartCommand),
|
||||||
colors.bold(bunkerURI),
|
colors.bold(bunkerURI),
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
// otherwise just print the data
|
||||||
|
log("listening at %v:\n pubkey: %s \n npub: %s%s%s\n bunker: %s\n\n",
|
||||||
|
colors.bold(relayURLs),
|
||||||
|
colors.bold(pubkey.Hex()),
|
||||||
|
colors.bold(npub),
|
||||||
|
authorizedKeysStr,
|
||||||
|
authorizedSecretsStr,
|
||||||
|
colors.bold(bunkerURI),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
printBunkerInfo()
|
printBunkerInfo()
|
||||||
|
|
||||||
@@ -162,7 +319,7 @@ var bunker = &cli.Command{
|
|||||||
signer.AuthorizeRequest = func(harmless bool, from nostr.PubKey, secret string) bool {
|
signer.AuthorizeRequest = func(harmless bool, from nostr.PubKey, secret string) bool {
|
||||||
if secret == newSecret {
|
if secret == newSecret {
|
||||||
// store this key
|
// store this key
|
||||||
authorizedKeys = append(authorizedKeys, from)
|
config.AuthorizedKeys = appendUnique(config.AuthorizedKeys, from)
|
||||||
// discard this and generate a new secret
|
// discard this and generate a new secret
|
||||||
newSecret = randString(12)
|
newSecret = randString(12)
|
||||||
// print bunker info again after this
|
// print bunker info again after this
|
||||||
@@ -170,9 +327,13 @@ var bunker = &cli.Command{
|
|||||||
time.Sleep(3 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
printBunkerInfo()
|
printBunkerInfo()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if persist != nil {
|
||||||
|
persist()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return slices.Contains(authorizedKeys, from) || slices.Contains(authorizedSecrets, secret)
|
return slices.Contains(config.AuthorizedKeys, from) || slices.Contains(authorizedSecrets, secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
for ie := range events {
|
for ie := range events {
|
||||||
@@ -248,3 +409,71 @@ var bunker = &cli.Command{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type plainOrEncryptedKey struct {
|
||||||
|
Plain *nostr.SecretKey
|
||||||
|
Encrypted *string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pe plainOrEncryptedKey) MarshalJSON() ([]byte, error) {
|
||||||
|
if pe.Plain != nil {
|
||||||
|
res := make([]byte, 66)
|
||||||
|
hex.Encode(res[1:], (*pe.Plain)[:])
|
||||||
|
res[0] = '"'
|
||||||
|
res[65] = '"'
|
||||||
|
return res, nil
|
||||||
|
} else if pe.Encrypted != nil {
|
||||||
|
return json.Marshal(*pe.Encrypted)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("no key to marshal")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pe *plainOrEncryptedKey) UnmarshalJSON(buf []byte) error {
|
||||||
|
if len(buf) == 66 {
|
||||||
|
sk, err := nostr.SecretKeyFromHex(string(buf[1 : 1+64]))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pe.Plain = &sk
|
||||||
|
return nil
|
||||||
|
} else if bytes.HasPrefix(buf, []byte("\"nsec")) {
|
||||||
|
_, v, err := nip19.Decode(string(buf[1 : len(buf)-1]))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sk := v.(nostr.SecretKey)
|
||||||
|
pe.Plain = &sk
|
||||||
|
return nil
|
||||||
|
} else if bytes.HasPrefix(buf, []byte("\"ncryptsec1")) {
|
||||||
|
ncryptsec := string(buf[1 : len(buf)-1])
|
||||||
|
pe.Encrypted = &ncryptsec
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("unrecognized key format '%s'", string(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a plainOrEncryptedKey) equals(b plainOrEncryptedKey) bool {
|
||||||
|
if a.Plain == nil && b.Plain != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a.Plain != nil && b.Plain == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a.Plain != nil && b.Plain != nil && *a.Plain != *b.Plain {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Encrypted == nil && b.Encrypted != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a.Encrypted != nil && b.Encrypted == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a.Encrypted != nil && b.Encrypted != nil && *a.Encrypted != *b.Encrypted {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
8
event.go
8
event.go
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
@@ -9,6 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"fiatjaf.com/nostr"
|
"fiatjaf.com/nostr"
|
||||||
|
"fiatjaf.com/nostr/keyer"
|
||||||
"fiatjaf.com/nostr/nip13"
|
"fiatjaf.com/nostr/nip13"
|
||||||
"fiatjaf.com/nostr/nip19"
|
"fiatjaf.com/nostr/nip19"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
@@ -168,6 +170,7 @@ example:
|
|||||||
evt.Content = ""
|
evt.Content = ""
|
||||||
|
|
||||||
kindWasSupplied := strings.Contains(stdinEvent, `"kind"`)
|
kindWasSupplied := strings.Contains(stdinEvent, `"kind"`)
|
||||||
|
contentWasSupplied := strings.Contains(stdinEvent, `"content"`)
|
||||||
mustRehashAndResign := false
|
mustRehashAndResign := false
|
||||||
|
|
||||||
if err := easyjson.Unmarshal([]byte(stdinEvent), &evt); err != nil {
|
if err := easyjson.Unmarshal([]byte(stdinEvent), &evt); err != nil {
|
||||||
@@ -194,7 +197,7 @@ example:
|
|||||||
evt.Content = content
|
evt.Content = content
|
||||||
}
|
}
|
||||||
mustRehashAndResign = true
|
mustRehashAndResign = true
|
||||||
} else if evt.Content == "" && evt.Kind == 1 {
|
} else if !contentWasSupplied && evt.Content == "" && evt.Kind == 1 {
|
||||||
evt.Content = "hello from the nostr army knife"
|
evt.Content = "hello from the nostr army knife"
|
||||||
mustRehashAndResign = true
|
mustRehashAndResign = true
|
||||||
}
|
}
|
||||||
@@ -287,6 +290,9 @@ example:
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else if err := kr.SignEvent(ctx, &evt); err != nil {
|
} else if err := kr.SignEvent(ctx, &evt); err != nil {
|
||||||
|
if _, isBunker := kr.(keyer.BunkerSigner); isBunker && errors.Is(ctx.Err(), context.DeadlineExceeded) {
|
||||||
|
err = fmt.Errorf("timeout waiting for bunker to respond")
|
||||||
|
}
|
||||||
return fmt.Errorf("error signing with provided key: %w", err)
|
return fmt.Errorf("error signing with provided key: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
95
filter.go
Normal file
95
filter.go
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"fiatjaf.com/nostr"
|
||||||
|
"github.com/mailru/easyjson"
|
||||||
|
"github.com/urfave/cli/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var filter = &cli.Command{
|
||||||
|
Name: "filter",
|
||||||
|
Usage: "applies an event filter to an event to see if it matches.",
|
||||||
|
Description: `
|
||||||
|
example:
|
||||||
|
echo '{"kind": 1, "content": "hello"}' | nak filter -k 1
|
||||||
|
nak filter '{"kind": 1, "content": "hello"}' -k 1
|
||||||
|
nak filter '{"kind": 1, "content": "hello"}' '{"kinds": [1]}' -k 0
|
||||||
|
`,
|
||||||
|
DisableSliceFlagSeparator: true,
|
||||||
|
Flags: reqFilterFlags,
|
||||||
|
ArgsUsage: "[event_json] [base_filter_json]",
|
||||||
|
Action: func(ctx context.Context, c *cli.Command) error {
|
||||||
|
args := c.Args().Slice()
|
||||||
|
|
||||||
|
var baseFilter nostr.Filter
|
||||||
|
var baseEvent nostr.Event
|
||||||
|
|
||||||
|
if len(args) == 2 {
|
||||||
|
// two arguments: first is event, second is base filter
|
||||||
|
if err := easyjson.Unmarshal([]byte(args[0]), &baseEvent); err != nil {
|
||||||
|
return fmt.Errorf("invalid base event: %w", err)
|
||||||
|
}
|
||||||
|
if err := easyjson.Unmarshal([]byte(args[1]), &baseFilter); err != nil {
|
||||||
|
return fmt.Errorf("invalid base filter: %w", err)
|
||||||
|
}
|
||||||
|
} else if len(args) == 1 {
|
||||||
|
if isPiped() {
|
||||||
|
// one argument + stdin: argument is base filter
|
||||||
|
if err := easyjson.Unmarshal([]byte(args[0]), &baseFilter); err != nil {
|
||||||
|
return fmt.Errorf("invalid base filter: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// one argument, no stdin: argument is event
|
||||||
|
if err := easyjson.Unmarshal([]byte(args[0]), &baseEvent); err != nil {
|
||||||
|
return fmt.Errorf("invalid base event: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply flags to filter
|
||||||
|
if err := applyFlagsToFilter(c, &baseFilter); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is no stdin we'll still get an empty object here
|
||||||
|
for evtj := range getJsonsOrBlank() {
|
||||||
|
var evt nostr.Event
|
||||||
|
if err := easyjson.Unmarshal([]byte(evtj), &evt); err != nil {
|
||||||
|
ctx = lineProcessingError(ctx, "invalid event: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge that with the base event
|
||||||
|
if evt.ID == nostr.ZeroID {
|
||||||
|
evt.ID = baseEvent.ID
|
||||||
|
}
|
||||||
|
if evt.PubKey == nostr.ZeroPK {
|
||||||
|
evt.PubKey = baseEvent.PubKey
|
||||||
|
}
|
||||||
|
if evt.Sig == [64]byte{} {
|
||||||
|
evt.Sig = baseEvent.Sig
|
||||||
|
}
|
||||||
|
if evt.Content == "" {
|
||||||
|
evt.Content = baseEvent.Content
|
||||||
|
}
|
||||||
|
if len(evt.Tags) == 0 {
|
||||||
|
evt.Tags = baseEvent.Tags
|
||||||
|
}
|
||||||
|
if evt.CreatedAt == 0 {
|
||||||
|
evt.CreatedAt = baseEvent.CreatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
if baseFilter.Matches(evt) {
|
||||||
|
stdout(evt)
|
||||||
|
} else {
|
||||||
|
logverbose("event %s didn't match %s", evt, baseFilter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exitIfLineProcessingError(ctx)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
4
go.mod
4
go.mod
@@ -4,7 +4,7 @@ go 1.24.1
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
fiatjaf.com/lib v0.3.1
|
fiatjaf.com/lib v0.3.1
|
||||||
fiatjaf.com/nostr v0.0.0-20250610194330-027d016d9706
|
fiatjaf.com/nostr v0.0.0-20250627165101-028a1637fbd0
|
||||||
github.com/bep/debounce v1.2.1
|
github.com/bep/debounce v1.2.1
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.5
|
github.com/btcsuite/btcd/btcec/v2 v2.3.5
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
||||||
@@ -55,7 +55,7 @@ require (
|
|||||||
github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 // indirect
|
github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/klauspost/compress v1.18.0 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.11 // indirect
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/magefile/mage v1.14.0 // indirect
|
github.com/magefile/mage v1.14.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
|||||||
19
go.sum
19
go.sum
@@ -1,12 +1,10 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
fiatjaf.com/lib v0.3.1 h1:/oFQwNtFRfV+ukmOCxfBEAuayoLwXp4wu2/fz5iHpwA=
|
fiatjaf.com/lib v0.3.1 h1:/oFQwNtFRfV+ukmOCxfBEAuayoLwXp4wu2/fz5iHpwA=
|
||||||
fiatjaf.com/lib v0.3.1/go.mod h1:Ycqq3+mJ9jAWu7XjbQI1cVr+OFgnHn79dQR5oTII47g=
|
fiatjaf.com/lib v0.3.1/go.mod h1:Ycqq3+mJ9jAWu7XjbQI1cVr+OFgnHn79dQR5oTII47g=
|
||||||
fiatjaf.com/nostr v0.0.0-20250521022139-d3fb25441ab3 h1:JRtme8g4UQ5KYlxI31wBa8YMWmAxvxdwtNn+PiI/XCs=
|
|
||||||
fiatjaf.com/nostr v0.0.0-20250521022139-d3fb25441ab3/go.mod h1:VPs38Fc8J1XAErV750CXAmMUqIq3XEX9VZVj/LuQzzM=
|
|
||||||
fiatjaf.com/nostr v0.0.0-20250522115245-f38ce069a93d h1:sl/BOXW5eK7v+cchMMEZvnzQW+n/jWiHGQn+CRt5m5Q=
|
|
||||||
fiatjaf.com/nostr v0.0.0-20250522115245-f38ce069a93d/go.mod h1:VPs38Fc8J1XAErV750CXAmMUqIq3XEX9VZVj/LuQzzM=
|
|
||||||
fiatjaf.com/nostr v0.0.0-20250610194330-027d016d9706 h1:G0xS5h9dsbODWh+f8rYvDkY328h79MsNs2dGPGqm8nY=
|
fiatjaf.com/nostr v0.0.0-20250610194330-027d016d9706 h1:G0xS5h9dsbODWh+f8rYvDkY328h79MsNs2dGPGqm8nY=
|
||||||
fiatjaf.com/nostr v0.0.0-20250610194330-027d016d9706/go.mod h1:VPs38Fc8J1XAErV750CXAmMUqIq3XEX9VZVj/LuQzzM=
|
fiatjaf.com/nostr v0.0.0-20250610194330-027d016d9706/go.mod h1:VPs38Fc8J1XAErV750CXAmMUqIq3XEX9VZVj/LuQzzM=
|
||||||
|
fiatjaf.com/nostr v0.0.0-20250627165101-028a1637fbd0 h1:Se07jECWueD3fZyaHO08oIzFOPwT6A6wNPQ8QWccX5c=
|
||||||
|
fiatjaf.com/nostr v0.0.0-20250627165101-028a1637fbd0/go.mod h1:VPs38Fc8J1XAErV750CXAmMUqIq3XEX9VZVj/LuQzzM=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/FastFilter/xorfilter v0.2.1 h1:lbdeLG9BdpquK64ZsleBS8B4xO/QW1IM0gMzF7KaBKc=
|
github.com/FastFilter/xorfilter v0.2.1 h1:lbdeLG9BdpquK64ZsleBS8B4xO/QW1IM0gMzF7KaBKc=
|
||||||
github.com/FastFilter/xorfilter v0.2.1/go.mod h1:aumvdkhscz6YBZF9ZA/6O4fIoNod4YR50kIVGGZ7l9I=
|
github.com/FastFilter/xorfilter v0.2.1/go.mod h1:aumvdkhscz6YBZF9ZA/6O4fIoNod4YR50kIVGGZ7l9I=
|
||||||
@@ -26,8 +24,6 @@ github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY=
|
|||||||
github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg=
|
github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg=
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA=
|
github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA=
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
|
github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
|
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
|
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.5 h1:dpAlnAwmT1yIBm3exhT1/8iUSD98RDJM5vqJVQDQLiU=
|
github.com/btcsuite/btcd/btcec/v2 v2.3.5 h1:dpAlnAwmT1yIBm3exhT1/8iUSD98RDJM5vqJVQDQLiU=
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.3.5/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ=
|
github.com/btcsuite/btcd/btcec/v2 v2.3.5/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ=
|
||||||
github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A=
|
github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A=
|
||||||
@@ -47,8 +43,6 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku
|
|||||||
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||||
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
|
|
||||||
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
|
||||||
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
|
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
|
||||||
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
@@ -163,6 +157,8 @@ github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYW
|
|||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
@@ -249,8 +245,6 @@ github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZ
|
|||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU=
|
|
||||||
golang.org/x/arch v0.17.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
|
||||||
golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
|
golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
|
||||||
golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
@@ -259,8 +253,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
|
||||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
|
||||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
||||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
@@ -282,8 +274,7 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
|
|||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
|
||||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
|||||||
@@ -17,14 +17,16 @@ import (
|
|||||||
"github.com/urfave/cli/v3"
|
"github.com/urfave/cli/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var defaultKey = nostr.KeyOne.Hex()
|
||||||
|
|
||||||
var defaultKeyFlags = []cli.Flag{
|
var defaultKeyFlags = []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "sec",
|
Name: "sec",
|
||||||
Usage: "secret key to sign the event, as nsec, ncryptsec or hex, or a bunker URL",
|
Usage: "secret key to sign the event, as nsec, ncryptsec or hex, or a bunker URL",
|
||||||
DefaultText: "the key '1'",
|
DefaultText: "the key '01'",
|
||||||
Category: CATEGORY_SIGNER,
|
Category: CATEGORY_SIGNER,
|
||||||
Sources: cli.EnvVars("NOSTR_SECRET_KEY"),
|
Sources: cli.EnvVars("NOSTR_SECRET_KEY"),
|
||||||
Value: nostr.KeyOne.Hex(),
|
Value: defaultKey,
|
||||||
HideDefault: true,
|
HideDefault: true,
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
@@ -75,9 +77,14 @@ func gatherSecretKeyOrBunkerFromArguments(ctx context.Context, c *cli.Command) (
|
|||||||
clientKey = nostr.Generate()
|
clientKey = nostr.Generate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logverbose("[nip46]: connecting to %s with client key %s", bunkerURL, clientKey.Hex())
|
||||||
|
|
||||||
bunker, err := nip46.ConnectBunker(ctx, clientKey, bunkerURL, nil, func(s string) {
|
bunker, err := nip46.ConnectBunker(ctx, clientKey, bunkerURL, nil, func(s string) {
|
||||||
log(color.CyanString("[nip46]: open the following URL: %s"), s)
|
log(color.CyanString("[nip46]: open the following URL: %s"), s)
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nostr.SecretKey{}, nil, fmt.Errorf("failed to connect to %s: %w", bunkerURL, err)
|
||||||
|
}
|
||||||
|
|
||||||
return nostr.SecretKey{}, bunker, err
|
return nostr.SecretKey{}, bunker, err
|
||||||
}
|
}
|
||||||
|
|||||||
11
main.go
11
main.go
@@ -6,6 +6,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"fiatjaf.com/nostr"
|
"fiatjaf.com/nostr"
|
||||||
"fiatjaf.com/nostr/sdk"
|
"fiatjaf.com/nostr/sdk"
|
||||||
@@ -27,6 +28,7 @@ var app = &cli.Command{
|
|||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
event,
|
event,
|
||||||
req,
|
req,
|
||||||
|
filter,
|
||||||
fetch,
|
fetch,
|
||||||
count,
|
count,
|
||||||
decode,
|
decode,
|
||||||
@@ -51,6 +53,13 @@ var app = &cli.Command{
|
|||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "config-path",
|
Name: "config-path",
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
|
Value: (func() string {
|
||||||
|
if home, err := os.UserHomeDir(); err == nil {
|
||||||
|
return filepath.Join(home, ".config/nak")
|
||||||
|
} else {
|
||||||
|
return filepath.Join("/dev/null")
|
||||||
|
}
|
||||||
|
})(),
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "quiet",
|
Name: "quiet",
|
||||||
@@ -124,7 +133,7 @@ func main() {
|
|||||||
|
|
||||||
if err := app.Run(context.Background(), os.Args); err != nil {
|
if err := app.Run(context.Background(), os.Args); err != nil {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log(color.YellowString(err.Error()) + "\n")
|
log("%s\n", color.RedString(err.Error()))
|
||||||
}
|
}
|
||||||
colors.reset()
|
colors.reset()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@@ -20,11 +20,6 @@ var (
|
|||||||
|
|
||||||
func initializeOutboxHintsDB(c *cli.Command, sys *sdk.System) error {
|
func initializeOutboxHintsDB(c *cli.Command, sys *sdk.System) error {
|
||||||
configPath := c.String("config-path")
|
configPath := c.String("config-path")
|
||||||
if configPath == "" {
|
|
||||||
if home, err := os.UserHomeDir(); err == nil {
|
|
||||||
configPath = filepath.Join(home, ".config/nak")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if configPath != "" {
|
if configPath != "" {
|
||||||
hintsFilePath = filepath.Join(configPath, "outbox/hints.bg")
|
hintsFilePath = filepath.Join(configPath, "outbox/hints.bg")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user