Compare commits

...

10 Commits

Author SHA1 Message Date
fiatjaf
6e4a546212 release with fixes. 2025-06-27 16:34:59 -03:00
fiatjaf
55c9d4ee45 remove the bunker context timeout because it causes the entire bunker to disconnect. 2025-06-27 16:28:21 -03:00
fiatjaf
550c89d8d7 slightly improve some error messages. 2025-06-27 13:50:28 -03:00
fiatjaf
1e9be3ed84 nak filter 2025-06-27 13:49:40 -03:00
fiatjaf
79cbc57dde fix main command error handler printing wrongly formatted stuff. 2025-06-27 13:48:07 -03:00
fiatjaf
1e237b4c42 do not fill .Content when "content" is received empty from stdin.
fixes https://github.com/fiatjaf/nak/issues/71
2025-06-23 17:57:45 -03:00
fiatjaf
89ec8b9822 simplify README about $NOSTR_CLIENT_KEY. 2025-06-20 21:06:42 -03:00
Anthony Accioly
fba83ea39e docs(readme): clarify NIP-46 signing with remote bunker
- Add example linking to Amber for NIP-46 bunker usage.
- Include note on setting `NOSTR_CLIENT_KEY`
2025-06-20 21:02:57 -03:00
Anthony Accioly
bd5569955c fix(helpers): add timeout and verbose logging for bunker connection
- Add a 10-second timeout to the bunker connection process using context
- Include detailed verbose logging for debugging.
2025-06-20 21:02:57 -03:00
Rui Chen
35ea2582d8 fix: update go.sum to fix build
Signed-off-by: Rui Chen <rui@chenrui.dev>
2025-06-20 16:24:15 -03:00
7 changed files with 125 additions and 19 deletions

View File

@@ -128,11 +128,18 @@ type the password to decrypt your secret key: **********
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
~> 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'
```
> [!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
```shell
~> nak event --sec ncryptsec1qggx54cg270zy9y8krwmfz29jyypsuxken2fkk99gr52qhje968n6mwkrfstqaqhq9eq94pnzl4nff437l4lp4ur2cs4f9um8738s35l2esx2tas48thtfhrk5kq94pf9j2tpk54yuermra0xu6hl5ls -c 'hello from encrypted key'

View File

@@ -2,6 +2,7 @@ package main
import (
"context"
"errors"
"fmt"
"os"
"slices"
@@ -9,6 +10,7 @@ import (
"time"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/keyer"
"fiatjaf.com/nostr/nip13"
"fiatjaf.com/nostr/nip19"
"github.com/fatih/color"
@@ -168,6 +170,7 @@ example:
evt.Content = ""
kindWasSupplied := strings.Contains(stdinEvent, `"kind"`)
contentWasSupplied := strings.Contains(stdinEvent, `"content"`)
mustRehashAndResign := false
if err := easyjson.Unmarshal([]byte(stdinEvent), &evt); err != nil {
@@ -194,7 +197,7 @@ example:
evt.Content = content
}
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"
mustRehashAndResign = true
}
@@ -287,6 +290,9 @@ example:
return 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)
}
}

96
filter.go Normal file
View File

@@ -0,0 +1,96 @@
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 {
fmt.Println(baseFilter.LimitZero)
logverbose("event %s didn't match %s", evt, baseFilter)
}
}
exitIfLineProcessingError(ctx)
return nil
},
}

4
go.mod
View File

@@ -4,7 +4,7 @@ go 1.24.1
require (
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/btcsuite/btcd/btcec/v2 v2.3.5
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/josharian/intern v1.0.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/magefile/mage v1.14.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect

19
go.sum
View File

@@ -1,12 +1,10 @@
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/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/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/FastFilter/xorfilter v0.2.1 h1:lbdeLG9BdpquK64ZsleBS8B4xO/QW1IM0gMzF7KaBKc=
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/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.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/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ=
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/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/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/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
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.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
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=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
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/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
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/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-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/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
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-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.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@@ -75,9 +75,14 @@ func gatherSecretKeyOrBunkerFromArguments(ctx context.Context, c *cli.Command) (
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) {
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
}

View File

@@ -27,6 +27,7 @@ var app = &cli.Command{
Commands: []*cli.Command{
event,
req,
filter,
fetch,
count,
decode,
@@ -124,7 +125,7 @@ func main() {
if err := app.Run(context.Background(), os.Args); err != nil {
if err != nil {
log(color.YellowString(err.Error()) + "\n")
log("%s\n", color.YellowString(err.Error()))
}
colors.reset()
os.Exit(1)