Compare commits

...
Author SHA1 Message Date
Yasuhiro Matsumoto 9df857a076 group chat: handle events synchronously in select, drop goroutine and mutex 2026-06-04 21:41:11 +09:00
52cf03d423 gift unwrap: override rumor pubkey from seal and recompute ID
NIP-17 requires clients verify that the kind:14 pubkey matches
the kind:13 pubkey to prevent sender impersonation. The CLI
gift unwrap path outputs the rumor as-is without setting the
pubkey from the verified seal or recomputing the event ID.

This mirrors nip59.GiftUnwrap() which does both operations.
The override is placed after the ZeroID sentinel check to
preserve decryption failure detection.

Fixes https://github.com/fiatjaf/nak/issues/110

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-04 09:16:03 -03:00
d0c291b25c gift unwrap: verify seal signature before trusting pubkey
The CLI gift unwrap path skips seal signature verification that
nip59.GiftUnwrap() performs. Without this check, a forged seal
with an arbitrary pubkey would be trusted for decryption key
lookup and sender identity.

Fixes https://github.com/fiatjaf/nak/issues/110

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-04 09:16:03 -03:00
mattnandGitHub 6ee60cb197 Merge pull request #139 from mattn/fix/count-auth-handler-consistency
count: use Pool.AuthRequiredHandler to match other commands
2026-06-04 01:56:59 +00:00
mattnandGitHub c2eb2d7bdd Merge pull request #138 from mattn/fix/req-sync-save-error
req: propagate non-duplicate save errors when loading sync file
2026-06-04 01:56:34 +00:00
Yasuhiro Matsumoto 168a182d50 count: use Pool.AuthRequiredHandler to match other commands. 2026-06-04 10:55:14 +09:00
Yasuhiro Matsumoto f202f038a4 req: propagate non-duplicate save errors when loading sync file. 2026-06-04 10:52:46 +09:00
mattnandGitHub 1043b2a7af Merge pull request #137 from mattn/fix/req-tag-decode-dead-code
req: actually decode bech32 values for single-letter --tag flags
2026-06-04 01:51:56 +00:00
Yasuhiro Matsumoto 9c3b4b69b6 req: actually decode bech32 values for single-letter --tag flags. 2026-06-04 10:50:53 +09:00
mattnandGitHub 1c542deb5e Merge pull request #136 from mattn/fix/group-chat-race
group: fix data race and out-of-bounds panic in chat command
2026-06-04 01:50:08 +00:00
Yasuhiro Matsumoto cefc0b91d0 group: fix data race and out-of-bounds panic in chat command. 2026-06-04 09:49:48 +09:00
mattnandGitHub 8c97c7becb Merge pull request #135 from mattn/refactor/lmdb-early-return
lmdb: invert configPath check to flatten setupLocalDatabases
2026-06-04 00:49:14 +00:00
mattnandGitHub ada938c3a8 Merge pull request #134 from mattn/refactor/blossom-mirror-early-return
blossom: drop unnecessary else after early return in mirror command
2026-06-04 00:48:35 +00:00
mattnandGitHub c2d74b846e Merge pull request #133 from mattn/refactor/git-init-flatten-nesting
git: flatten nested ifs in ngit remote migration with guard clauses
2026-06-04 00:48:07 +00:00
Yasuhiro Matsumoto 06bd1ddb11 lmdb: invert configPath check to flatten setupLocalDatabases. 2026-06-04 09:48:03 +09:00
Yasuhiro Matsumoto ebd93964de blossom: drop unnecessary else after early return in mirror command. 2026-06-04 09:21:52 +09:00
Yasuhiro Matsumoto a285ac6723 git: flatten nested ifs in ngit remote migration with guard clauses. 2026-06-04 09:20:14 +09:00
fiatjaf e2f791ab85 fetch: support --auth. 2026-06-02 21:35:58 -03:00
mattnandGitHub ed5af14305 Merge pull request #130 from fiatjaf/go-tty-v2
go-tty: bump to v2.
2026-05-29 15:30:27 +00:00
Yasuhiro Matsumoto e5f4b07c3d go-tty: bump to v2. 2026-05-30 00:29:51 +09:00
fiatjaf b41260f663 --outbox help text better. 2026-05-26 19:51:18 -03:00
fiatjaf c9e1511865 bunker: fix relay handling, don't mix custom with main relay URLs. 2026-05-21 20:25:07 -03:00
fiatjaf 5af1d111ba key-combine: support parsing npubs and nprofiles. 2026-05-20 16:14:11 -03:00
15 changed files with 151 additions and 132 deletions
+19 -20
View File
@@ -283,28 +283,27 @@ if any of the files are not found the command will fail, otherwise it will succe
out, _ := json.Marshal(bd)
stdout(out)
return nil
} else {
for input := range getJsonsOrBlank() {
if input == "{}" {
continue
}
blobURL := input
if err := json.Unmarshal([]byte(input), &bd); err == nil {
blobURL = bd.URL
}
bd, err := client.MirrorBlob(ctx, blobURL)
if err != nil {
ctx = lineProcessingError(ctx, "failed to mirror '%s': %s", blobURL, err)
continue
}
out, _ := json.Marshal(bd)
stdout(out)
}
exitIfLineProcessingError(ctx)
}
for input := range getJsonsOrBlank() {
if input == "{}" {
continue
}
blobURL := input
if err := json.Unmarshal([]byte(input), &bd); err == nil {
blobURL = bd.URL
}
bd, err := client.MirrorBlob(ctx, blobURL)
if err != nil {
ctx = lineProcessingError(ctx, "failed to mirror '%s': %s", blobURL, err)
continue
}
out, _ := json.Marshal(bd)
stdout(out)
}
exitIfLineProcessingError(ctx)
return nil
},
},
+9 -11
View File
@@ -225,17 +225,15 @@ var bunker = &cli.Command{
}
}
}
relayURLs := make([]string, 0, len(allRelays))
relays := connectToAllRelays(ctx, c, allRelays, nil)
if len(relays) == 0 {
log("failed to connect to any of the given relays.\n")
os.Exit(3)
}
for _, relay := range relays {
relayURLs = append(relayURLs, relay.URL)
qs.Add("relay", relay.URL)
for _, relay := range config.Relays {
qs.Add("relay", relay)
}
if len(relayURLs) == 0 {
if len(relays) == 0 {
return fmt.Errorf("not connected to any relays: please specify at least one")
}
@@ -295,8 +293,8 @@ var bunker = &cli.Command{
secretKeyFlag = "--sec " + sec
}
relayURLsPossiblyWithoutSchema := make([]string, len(relayURLs))
for i, url := range relayURLs {
relayURLsPossiblyWithoutSchema := make([]string, len(config.Relays))
for i, url := range config.Relays {
if strings.HasPrefix(url, "wss://") {
relayURLsPossiblyWithoutSchema[i] = url[6:]
} else {
@@ -313,7 +311,7 @@ var bunker = &cli.Command{
)
log("listening at %v:\n pubkey: %s \n npub: %s%s%s\n to restart: %s\n bunker: %s\n\n",
colors.bold(relayURLs),
colors.bold(config.Relays),
colors.bold(pubkey.Hex()),
colors.bold(npub),
authorizedKeysStr,
@@ -324,7 +322,7 @@ var bunker = &cli.Command{
} 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(config.Relays),
colors.bold(pubkey.Hex()),
colors.bold(npub),
authorizedKeysStr,
@@ -343,7 +341,7 @@ var bunker = &cli.Command{
printBunkerInfo()
// subscribe to relays
events := sys.Pool.SubscribeMany(ctx, relayURLs, nostr.Filter{
events := sys.Pool.SubscribeMany(ctx, allRelays, nostr.Filter{
Kinds: []nostr.Kind{nostr.KindNostrConnect},
Tags: nostr.TagMap{"p": []string{pubkey.Hex()}},
Since: nostr.Now(),
@@ -475,7 +473,7 @@ var bunker = &cli.Command{
// use custom relays if they are defined for this client
// (normally if the initial connection came from a nostrconnect:// URL)
relays := relayURLs
relays := config.Relays
for _, c := range config.Clients {
if c.PubKey == from && len(c.CustomRelays) > 0 {
relays = c.CustomRelays
+3 -3
View File
@@ -44,17 +44,17 @@ var count = &cli.Command{
forcePreAuthSigner = nil
}
sys.Pool.RelayOptions.AuthHandler = func(ctx context.Context, _ *nostr.Relay, aevt *nostr.Event) error {
sys.Pool.AuthRequiredHandler = func(ctx context.Context, authEvent *nostr.Event) error {
return authSigner(ctx, c, func(s string, args ...any) {
if strings.HasPrefix(s, "authenticating as") {
cleanUrl, _ := strings.CutPrefix(
nip42.GetRelayURLFromAuthEvent(*aevt),
nip42.GetRelayURLFromAuthEvent(*authEvent),
"wss://",
)
s = "authenticating to " + color.CyanString(cleanUrl) + " as" + s[len("authenticating as"):]
}
log(s+"\n", args...)
}, aevt)
}, authEvent)
}
relays := connectToAllRelays(
ctx,
+1 -1
View File
@@ -100,7 +100,7 @@ example:
},
&cli.BoolFlag{
Name: "outbox",
Usage: "use outbox relays from specified public keys",
Usage: "publish to the \"write\" relays of the author and to the \"read\" relays of anyone mentioned in \"p\" tags",
DefaultText: "false, will only use manually-specified relays",
Category: CATEGORY_EXTRAS,
},
+28 -6
View File
@@ -3,11 +3,14 @@ package main
import (
"context"
"fmt"
"strings"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/nip05"
"fiatjaf.com/nostr/nip19"
"fiatjaf.com/nostr/nip42"
"fiatjaf.com/nostr/sdk/hints"
"github.com/fatih/color"
"github.com/urfave/cli/v3"
)
@@ -18,12 +21,18 @@ var fetch = &cli.Command{
nak fetch nevent1qqsxrwm0hd3s3fddh4jc2574z3xzufq6qwuyz2rvv3n087zvym3dpaqprpmhxue69uhhqatzd35kxtnjv4kxz7tfdenju6t0xpnej4
echo npub1h8spmtw9m2huyv6v2j2qd5zv956z2zdugl6mgx02f2upffwpm3nqv0j4ps | nak fetch --relay wss://relay.nostr.band`,
DisableSliceFlagSeparator: true,
Flags: append(reqFilterFlags,
&cli.StringSliceFlag{
Name: "relay",
Aliases: []string{"r"},
Usage: "also use these relays to fetch from",
},
Flags: append(defaultKeyFlags,
append(reqFilterFlags,
&cli.StringSliceFlag{
Name: "relay",
Aliases: []string{"r"},
Usage: "also use these relays to fetch from",
},
&cli.BoolFlag{
Name: "auth",
Usage: "always perform nip42 \"AUTH\" when facing an \"auth-required: \" rejection and try again",
},
)...,
),
ArgsUsage: "[nip05_or_nip19_code]",
Action: func(ctx context.Context, c *cli.Command) error {
@@ -106,6 +115,19 @@ var fetch = &cli.Command{
continue
}
sys.Pool.AuthRequiredHandler = func(ctx context.Context, authEvent *nostr.Event) error {
return authSigner(ctx, c, func(s string, args ...any) {
if strings.HasPrefix(s, "authenticating as") {
cleanUrl, _ := strings.CutPrefix(
nip42.GetRelayURLFromAuthEvent(*authEvent),
"wss://",
)
s = "authenticating to " + color.CyanString(cleanUrl) + " as" + s[len("authenticating as"):]
}
log(s+"\n", args...)
}, authEvent)
}
found := false
for ie := range sys.Pool.FetchMany(ctx, relays, filter, nostr.SubscriptionOptions{
Label: "nak-fetch",
+7
View File
@@ -239,6 +239,10 @@ a decoupled key (if it has been created or received with "nak dekey" previously)
return fmt.Errorf("not a seal event (kind %d)", seal.Kind)
}
if !seal.VerifySignature() {
return fmt.Errorf("seal signature is invalid")
}
senderEncryptionPublicKeys := []nostr.PubKey{seal.PubKey}
if theirEPub, exists := getDecoupledEncryptionPublicKey(ctx, seal.PubKey); exists {
senderEncryptionPublicKeys = append(senderEncryptionPublicKeys, seal.PubKey)
@@ -278,6 +282,9 @@ a decoupled key (if it has been created or received with "nak dekey" previously)
return fmt.Errorf("failed to decrypt rumor: %w", err)
}
rumor.PubKey = seal.PubKey
rumor.ID = rumor.GetID()
// output the unwrapped event (rumor)
stdout(rumor.String())
}
+16 -13
View File
@@ -115,20 +115,23 @@ aside from those, there is also:
defaultOwner = existingConfig.Owner
} else {
// extract info from nostr:// git remotes (this is just for migrating from ngit)
if output, err := exec.Command("git", "remote", "-v").Output(); err == nil {
remotes := strings.Split(strings.TrimSpace(string(output)), "\n")
for _, remote := range remotes {
if strings.Contains(remote, "nostr://") {
parts := strings.Fields(remote)
if len(parts) >= 2 {
nostrURL := parts[1]
// parse nostr://npub.../relay_hostname/identifier
if remoteOwner, remoteIdentifier, relays, err := parseRepositoryAddress(ctx, nostrURL); err == nil && len(relays) > 0 {
defaultIdentifier = remoteIdentifier
defaultOwner = nip19.EncodeNpub(remoteOwner)
}
}
output, err := exec.Command("git", "remote", "-v").Output()
if err == nil {
for _, remote := range strings.Split(strings.TrimSpace(string(output)), "\n") {
if !strings.Contains(remote, "nostr://") {
continue
}
parts := strings.Fields(remote)
if len(parts) < 2 {
continue
}
// parse nostr://npub.../relay_hostname/identifier
remoteOwner, remoteIdentifier, relays, err := parseRepositoryAddress(ctx, parts[1])
if err != nil || len(relays) == 0 {
continue
}
defaultIdentifier = remoteIdentifier
defaultOwner = nip19.EncodeNpub(remoteOwner)
}
}
}
+2 -2
View File
@@ -3,7 +3,7 @@ module github.com/fiatjaf/nak
go 1.25
require (
fiatjaf.com/nostr v0.0.0-20260508155207-03a55cc0b816
fiatjaf.com/nostr v0.0.0-20260602223326-015842e96d86
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/bep/debounce v1.2.1
github.com/btcsuite/btcd/btcec/v2 v2.3.6
@@ -17,7 +17,6 @@ require (
github.com/mark3labs/mcp-go v0.8.3
github.com/markusmobius/go-dateparser v1.2.3
github.com/mattn/go-isatty v0.0.20
github.com/mattn/go-tty v0.0.7
github.com/mdp/qrterminal/v3 v3.2.1
github.com/puzpuzpuz/xsync/v3 v3.5.1
github.com/stretchr/testify v1.10.0
@@ -32,6 +31,7 @@ require (
fiatjaf.com/lib v0.3.7
github.com/hanwen/go-fuse/v2 v2.9.0
github.com/itchyny/gojq v0.12.19
github.com/mattn/go-tty/v2 v2.0.0
)
require (
+4 -4
View File
@@ -1,7 +1,7 @@
fiatjaf.com/lib v0.3.7 h1:mXZOn7NrUcjSdy4oNvwQyAmes7Ueb+Zr5hjqMIe2dxI=
fiatjaf.com/lib v0.3.7/go.mod h1:UlHaZvPHj25PtKLh9GjZkUHRmQ2xZ8Jkoa4VRaLeeQ8=
fiatjaf.com/nostr v0.0.0-20260508155207-03a55cc0b816 h1:96hiRZUwfiL1Nf3yp0G2TBM85EVZ0deJLl/bVrJy2Gs=
fiatjaf.com/nostr v0.0.0-20260508155207-03a55cc0b816/go.mod h1:b1EIUDnd133Ie8Pg8O/biaKdFyCMz28aD4n64g1GqvM=
fiatjaf.com/nostr v0.0.0-20260602223326-015842e96d86 h1:p3HnX1UDT/CfiMvTc4yTcxHQm08ri7DM32P1uKkFNKg=
fiatjaf.com/nostr v0.0.0-20260602223326-015842e96d86/go.mod h1:b1EIUDnd133Ie8Pg8O/biaKdFyCMz28aD4n64g1GqvM=
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
github.com/FastFilter/xorfilter v0.2.1 h1:lbdeLG9BdpquK64ZsleBS8B4xO/QW1IM0gMzF7KaBKc=
@@ -200,8 +200,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mattn/go-tty v0.0.7 h1:KJ486B6qI8+wBO7kQxYgmmEFDaFEE96JMBQ7h400N8Q=
github.com/mattn/go-tty v0.0.7/go.mod h1:f2i5ZOvXBU/tCABmLmOfzLz9azMo5wdAaElRNnJKr+k=
github.com/mattn/go-tty/v2 v2.0.0 h1:AgXDfbKcENaiQbjdQ+o2ZusbtiaOhK1ArFD75d5U6qk=
github.com/mattn/go-tty/v2 v2.0.0/go.mod h1:azVwsnH46TUJRQPRp/IrUT+8nRkkvjvkESJJxAA4Y48=
github.com/mdp/qrterminal/v3 v3.2.1 h1:6+yQjiiOsSuXT5n9/m60E54vdgFsw0zhADHhHLrFet4=
github.com/mdp/qrterminal/v3 v3.2.1/go.mod h1:jOTmXvnBsMy5xqLniO0R++Jmjs2sTm9dFSuQ5kpz/SU=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
+13 -37
View File
@@ -276,56 +276,32 @@ var group = &cli.Command{
return err
}
// stored events arrive newest-first before EOSE, so we buffer them
// from the end and print them in chronological order once EOSE
// arrives; live events are printed as they come.
eosed := false
messages := make([]struct {
message string
rendered bool
}, 200)
messages := make([]string, 200)
base := len(messages)
tryRender := func(i int) {
// if all messages before these are loaded we can render this,
// otherwise we render whatever we can and stop
for m, msg := range messages[base:] {
if msg.rendered {
continue
}
if msg.message == "" {
break
}
messages[base+m].rendered = true
stdout(msg.message)
}
}
for {
select {
case evt := <-sub.Events:
var i int
meta := sys.FetchProfileMetadata(ctx, evt.PubKey)
line := color.HiBlueString(meta.ShortName()) + " " + color.HiCyanString(evt.CreatedAt.Time().Format(time.DateTime)) + ": " + evt.Content
if eosed {
i = len(messages)
messages = append(messages, struct {
message string
rendered bool
}{})
} else {
stdout(line)
} else if base > 0 {
base--
i = base
messages[base] = line
}
go func() {
meta := sys.FetchProfileMetadata(ctx, evt.PubKey)
messages[i].message = color.HiBlueString(meta.ShortName()) + " " + color.HiCyanString(evt.CreatedAt.Time().Format(time.DateTime)) + ": " + evt.Content
if eosed {
tryRender(i)
}
}()
// else: pre-EOSE buffer is full (relay returned more than the limit); drop.
case reason := <-sub.ClosedReason:
stdout("closed:" + color.YellowString(reason))
case <-sub.EndOfStoredEvents:
eosed = true
tryRender(len(messages) - 1)
for _, msg := range messages[base:] {
stdout(msg)
}
case <-sub.Context.Done():
return fmt.Errorf("subscription ended: %w", context.Cause(sub.Context))
}
+1 -1
View File
@@ -27,7 +27,7 @@ import (
"github.com/fatih/color"
jsoniter "github.com/json-iterator/go"
"github.com/mattn/go-isatty"
"github.com/mattn/go-tty"
"github.com/mattn/go-tty/v2"
"github.com/urfave/cli/v3"
"golang.org/x/term"
)
+1 -1
View File
@@ -13,7 +13,7 @@ import (
"fiatjaf.com/nostr/nip49"
"github.com/chzyer/readline"
"github.com/fatih/color"
"github.com/mattn/go-tty"
"github.com/mattn/go-tty/v2"
"github.com/urfave/cli/v3"
)
+16 -7
View File
@@ -263,15 +263,13 @@ However, if the intent is to check if two existing Nostr pubkeys match a given c
keyGroups := make([][]*btcec.PublicKey, 0, len(result.Keys))
for i, keyhex := range result.Keys {
keyb, err := hex.DecodeString(keyhex)
if err != nil {
return fmt.Errorf("error parsing key %s: %w", keyhex, err)
}
pk32, err := parsePubKey(keyhex)
if err == nil { /* we'll try both the 02 and the 03 prefix versions */
result.Keys[i] = pk32.Hex()
if len(keyb) == 32 /* we'll use both the 02 and the 03 prefix versions */ {
group := make([]*btcec.PublicKey, 2)
for i, prefix := range []byte{0x02, 0x03} {
pubk, err := btcec.ParsePubKey(append([]byte{prefix}, keyb...))
pubk, err := btcec.ParsePubKey(append([]byte{prefix}, pk32[:]...))
if err != nil {
log("error parsing key %s: %s", keyhex, err)
continue
@@ -279,7 +277,16 @@ However, if the intent is to check if two existing Nostr pubkeys match a given c
group[i] = pubk
}
keyGroups = append(keyGroups, group)
} else /* assume it's 33 */ {
continue
}
keyb, err := hex.DecodeString(keyhex)
if err != nil {
return fmt.Errorf("error parsing key %s: %w", keyhex, err)
}
if len(keyb) == 33 {
result.Keys[i] = hex.EncodeToString(keyb[1:])
pubk, err := btcec.ParsePubKey(keyb)
if err != nil {
return fmt.Errorf("error parsing key %s: %w", keyhex, err)
@@ -288,6 +295,8 @@ However, if the intent is to check if two existing Nostr pubkeys match a given c
// remove the leading byte from the output just so it is all uniform
result.Keys[i] = result.Keys[i][2:]
continue
}
}
+23 -21
View File
@@ -16,28 +16,30 @@ import (
func setupLocalDatabases(c *cli.Command, sys *sdk.System) {
configPath := c.String("config-path")
if configPath != "" {
hintsPath := filepath.Join(configPath, "outbox/hints")
os.MkdirAll(hintsPath, 0755)
_, err := lmdbh.NewLMDBHints(hintsPath)
if err != nil {
log("failed to create lmdb hints db at '%s': %s\n", hintsPath, err)
}
if configPath == "" {
return
}
eventsPath := filepath.Join(configPath, "events")
os.MkdirAll(eventsPath, 0755)
sys.Store = &lmdb.LMDBBackend{Path: eventsPath}
if err := sys.Store.Init(); err != nil {
log("failed to create boltdb events db at '%s': %s\n", eventsPath, err)
sys.Store = &nullstore.NullStore{}
}
hintsPath := filepath.Join(configPath, "outbox/hints")
os.MkdirAll(hintsPath, 0755)
_, err := lmdbh.NewLMDBHints(hintsPath)
if err != nil {
log("failed to create lmdb hints db at '%s': %s\n", hintsPath, err)
}
kvPath := filepath.Join(configPath, "kvstore")
os.MkdirAll(kvPath, 0755)
if kv, err := lmdbkv.NewStore(kvPath); err != nil {
log("failed to create boltdb kvstore db at '%s': %s\n", kvPath, err)
} else {
sys.KVStore = kv
}
eventsPath := filepath.Join(configPath, "events")
os.MkdirAll(eventsPath, 0755)
sys.Store = &lmdb.LMDBBackend{Path: eventsPath}
if err := sys.Store.Init(); err != nil {
log("failed to create boltdb events db at '%s': %s\n", eventsPath, err)
sys.Store = &nullstore.NullStore{}
}
kvPath := filepath.Join(configPath, "kvstore")
os.MkdirAll(kvPath, 0755)
if kv, err := lmdbkv.NewStore(kvPath); err != nil {
log("failed to create boltdb kvstore db at '%s': %s\n", kvPath, err)
} else {
sys.KVStore = kv
}
}
+8 -5
View File
@@ -68,7 +68,7 @@ example:
},
&cli.BoolFlag{
Name: "outbox",
Usage: "use outbox relays from specified public keys",
Usage: "read from \"write\" relays of \"authors\" and/or from the \"read\" relays of any \"#p\" or \"#P\" tags",
DefaultText: "false, will only use manually-specified relays",
},
&cli.UintFlag{
@@ -215,8 +215,11 @@ example:
if err := easyjson.Unmarshal([]byte(scanner.Text()), &evt); err != nil {
continue
}
if err := store.SaveEvent(evt); err != nil || err == eventstore.ErrDupEvent {
continue
if err := store.SaveEvent(evt); err != nil {
if err == eventstore.ErrDupEvent {
continue
}
return fmt.Errorf("failed to save event from sync file: %w", err)
}
}
if err := scanner.Err(); err != nil {
@@ -595,8 +598,8 @@ func applyFlagsToFilter(c *cli.Command, filter *nostr.Filter) error {
spl := strings.SplitN(tagFlag, "=", 2)
if len(spl) == 2 {
val := spl[1]
if len(spl) == 1 {
val = decodeTagValue(val, []rune(spl[0])[0])
if len(spl[0]) == 1 {
val = decodeTagValue(val, rune(spl[0][0]))
}
tags = append(tags, flagTag{spl[0], val})
} else {