diff --git a/bunker.go b/bunker.go
index 29eaa69..73fef86 100644
--- a/bunker.go
+++ b/bunker.go
@@ -141,13 +141,11 @@ var bunker = &cli.Command{
 
 		// subscribe to relays
 		now := nostr.Now()
-		events := sys.Pool.SubMany(ctx, relayURLs, nostr.Filters{
-			{
-				Kinds:     []int{nostr.KindNostrConnect},
-				Tags:      nostr.TagMap{"p": []string{pubkey}},
-				Since:     &now,
-				LimitZero: true,
-			},
+		events := sys.Pool.SubscribeMany(ctx, relayURLs, nostr.Filter{
+			Kinds:     []int{nostr.KindNostrConnect},
+			Tags:      nostr.TagMap{"p": []string{pubkey}},
+			Since:     &now,
+			LimitZero: true,
 		})
 
 		signer := nip46.NewStaticKeySigner(sec)
@@ -227,4 +225,23 @@ var bunker = &cli.Command{
 
 		return nil
 	},
+	Commands: []*cli.Command{
+		{
+			Name:      "connect",
+			Usage:     "use the client-initiated NostrConnect flow of NIP46",
+			ArgsUsage: "<nostrconnect-uri>",
+			Action: func(ctx context.Context, c *cli.Command) error {
+				if c.Args().Len() != 1 {
+					return fmt.Errorf("must be called with a nostrconnect://... uri")
+				}
+
+				uri, err := url.Parse(c.Args().First())
+				if err != nil || uri.Scheme != "nostrconnect" || !nostr.IsValidPublicKey(uri.Host) {
+					return fmt.Errorf("invalid uri")
+				}
+
+				return nil
+			},
+		},
+	},
 }
diff --git a/fetch.go b/fetch.go
index 5972415..fb4c0ab 100644
--- a/fetch.go
+++ b/fetch.go
@@ -113,7 +113,7 @@ var fetch = &cli.Command{
 				continue
 			}
 
-			for ie := range sys.Pool.SubManyEose(ctx, relays, nostr.Filters{filter}) {
+			for ie := range sys.Pool.FetchMany(ctx, relays, filter) {
 				stdout(ie.Event)
 			}
 		}
diff --git a/go.mod b/go.mod
index beb0af0..e7ec183 100644
--- a/go.mod
+++ b/go.mod
@@ -17,7 +17,7 @@ require (
 	github.com/mailru/easyjson v0.9.0
 	github.com/mark3labs/mcp-go v0.8.3
 	github.com/markusmobius/go-dateparser v1.2.3
-	github.com/nbd-wtf/go-nostr v0.49.7
+	github.com/nbd-wtf/go-nostr v0.50.0
 	golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8
 )
 
diff --git a/mcp.go b/mcp.go
index daf36ed..bb56a4a 100644
--- a/mcp.go
+++ b/mcp.go
@@ -221,7 +221,7 @@ var mcpServer = &cli.Command{
 				filter.Authors = []string{pubkey}
 			}
 
-			events := sys.Pool.SubManyEose(ctx, []string{relay}, nostr.Filters{filter})
+			events := sys.Pool.FetchMany(ctx, []string{relay}, filter)
 
 			result := strings.Builder{}
 			for ie := range events {
diff --git a/paginate.go b/paginate.go
deleted file mode 100644
index 0f871e3..0000000
--- a/paginate.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package main
-
-import (
-	"context"
-	"math"
-	"slices"
-	"time"
-
-	"github.com/nbd-wtf/go-nostr"
-)
-
-func paginateWithParams(
-	interval time.Duration,
-	globalLimit uint64,
-) func(ctx context.Context, urls []string, filters nostr.Filters, opts ...nostr.SubscriptionOption) chan nostr.RelayEvent {
-	return func(ctx context.Context, urls []string, filters nostr.Filters, opts ...nostr.SubscriptionOption) chan nostr.RelayEvent {
-		// filters will always be just one
-		filter := filters[0]
-
-		nextUntil := nostr.Now()
-		if filter.Until != nil {
-			nextUntil = *filter.Until
-		}
-
-		if globalLimit == 0 {
-			globalLimit = uint64(filter.Limit)
-			if globalLimit == 0 && !filter.LimitZero {
-				globalLimit = math.MaxUint64
-			}
-		}
-		var globalCount uint64 = 0
-		globalCh := make(chan nostr.RelayEvent)
-
-		repeatedCache := make([]string, 0, 300)
-		nextRepeatedCache := make([]string, 0, 300)
-
-		go func() {
-			defer close(globalCh)
-
-			for {
-				filter.Until = &nextUntil
-				time.Sleep(interval)
-
-				keepGoing := false
-				for evt := range sys.Pool.SubManyEose(ctx, urls, nostr.Filters{filter}, opts...) {
-					if slices.Contains(repeatedCache, evt.ID) {
-						continue
-					}
-
-					keepGoing = true // if we get one that isn't repeated, then keep trying to get more
-					nextRepeatedCache = append(nextRepeatedCache, evt.ID)
-
-					globalCh <- evt
-
-					globalCount++
-					if globalCount >= globalLimit {
-						return
-					}
-
-					if evt.CreatedAt < *filter.Until {
-						nextUntil = evt.CreatedAt
-					}
-				}
-
-				if !keepGoing {
-					return
-				}
-
-				repeatedCache = nextRepeatedCache
-				nextRepeatedCache = nextRepeatedCache[:0]
-			}
-		}()
-
-		return globalCh
-	}
-}
diff --git a/req.go b/req.go
index 3a1a4bf..dd719fc 100644
--- a/req.go
+++ b/req.go
@@ -143,14 +143,14 @@ example:
 						}
 					}
 				} else {
-					fn := sys.Pool.SubManyEose
+					fn := sys.Pool.FetchMany
 					if c.Bool("paginate") {
-						fn = paginateWithParams(c.Duration("paginate-interval"), c.Uint("paginate-global-limit"))
+						fn = sys.Pool.PaginatorWithInterval(c.Duration("paginate-interval"))
 					} else if c.Bool("stream") {
-						fn = sys.Pool.SubMany
+						fn = sys.Pool.SubscribeMany
 					}
 
-					for ie := range fn(ctx, relayUrls, nostr.Filters{filter}) {
+					for ie := range fn(ctx, relayUrls, filter) {
 						stdout(ie.Event)
 					}
 				}