+25
.dockerignore
+25
.dockerignore
···
1
+
# Git
2
+
.git
3
+
.gitignore
4
+
.github
5
+
6
+
# Documentation
7
+
README.md
8
+
LICENSE
9
+
10
+
# Development files
11
+
.env
12
+
.env.*
13
+
*.md
14
+
15
+
# IDE
16
+
.vscode
17
+
.idea
18
+
*.swp
19
+
*.swo
20
+
*~
21
+
22
+
# Docker
23
+
Dockerfile
24
+
docker-compose.yml
25
+
.dockerignore
+62
.github/workflows/docker.yml
+62
.github/workflows/docker.yml
···
1
+
name: Build and Push Docker Image
2
+
3
+
on:
4
+
push:
5
+
branches:
6
+
- main
7
+
tags:
8
+
- 'v*'
9
+
pull_request:
10
+
branches:
11
+
- main
12
+
13
+
env:
14
+
REGISTRY: ghcr.io
15
+
IMAGE_NAME: ${{ github.repository }}
16
+
17
+
jobs:
18
+
build-and-push:
19
+
runs-on: ubuntu-latest
20
+
permissions:
21
+
contents: read
22
+
packages: write
23
+
24
+
steps:
25
+
- name: Checkout repository
26
+
uses: actions/checkout@v4
27
+
28
+
- name: Set up Docker Buildx
29
+
uses: docker/setup-buildx-action@v3
30
+
31
+
- name: Log in to GitHub Container Registry
32
+
if: github.event_name != 'pull_request'
33
+
uses: docker/login-action@v3
34
+
with:
35
+
registry: ${{ env.REGISTRY }}
36
+
username: ${{ github.actor }}
37
+
password: ${{ secrets.GITHUB_TOKEN }}
38
+
39
+
- name: Extract metadata (tags, labels) for Docker
40
+
id: meta
41
+
uses: docker/metadata-action@v5
42
+
with:
43
+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
44
+
tags: |
45
+
type=ref,event=branch
46
+
type=ref,event=pr
47
+
type=semver,pattern={{version}}
48
+
type=semver,pattern={{major}}.{{minor}}
49
+
type=semver,pattern={{major}}
50
+
type=sha,prefix={{branch}}-
51
+
type=raw,value=latest,enable={{is_default_branch}}
52
+
53
+
- name: Build and push Docker image
54
+
uses: docker/build-push-action@v5
55
+
with:
56
+
context: .
57
+
push: ${{ github.event_name != 'pull_request' }}
58
+
tags: ${{ steps.meta.outputs.tags }}
59
+
labels: ${{ steps.meta.outputs.labels }}
60
+
cache-from: type=gha
61
+
cache-to: type=gha,mode=max
62
+
platforms: linux/amd64,linux/arm64
+43
Dockerfile
+43
Dockerfile
···
1
+
# Build stage
2
+
FROM golang:1.25.4-alpine AS builder
3
+
4
+
WORKDIR /build
5
+
6
+
# Install build dependencies
7
+
RUN apk add --no-cache git ca-certificates
8
+
9
+
# Copy go mod files
10
+
COPY go.mod go.sum ./
11
+
RUN go mod download
12
+
13
+
# Copy source code
14
+
COPY . .
15
+
16
+
# Build the binary
17
+
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o /build/bin/atkafka ./cmd/atkafka
18
+
19
+
# Runtime stage
20
+
FROM alpine:latest
21
+
22
+
# Install ca-certificates for HTTPS connections
23
+
RUN apk --no-cache add ca-certificates
24
+
25
+
WORKDIR /app
26
+
27
+
# Create non-root user
28
+
RUN addgroup -g 1000 atkafka && \
29
+
adduser -D -u 1000 -G atkafka atkafka
30
+
31
+
# Copy binary from builder
32
+
COPY --from=builder /build/bin/atkafka /app/atkafka
33
+
34
+
# Change ownership
35
+
RUN chown -R atkafka:atkafka /app
36
+
37
+
# Switch to non-root user
38
+
USER atkafka
39
+
40
+
# Expose metrics port (default from bluesky-social/go-util)
41
+
EXPOSE 2112
42
+
43
+
ENTRYPOINT ["/app/atkafka"]
+19
LICENSE
+19
LICENSE
···
1
+
MIT License
2
+
3
+
Permission is hereby granted, free of charge, to any person obtaining a copy
4
+
of this software and associated documentation files (the "Software"), to deal
5
+
in the Software without restriction, including without limitation the rights
6
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+
copies of the Software, and to permit persons to whom the Software is
8
+
furnished to do so, subject to the following conditions:
9
+
10
+
The above copyright notice and this permission notice shall be included in all
11
+
copies or substantial portions of the Software.
12
+
13
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+
SOFTWARE.
+135
README.md
+135
README.md
···
1
+
# at-kafka
2
+
3
+
A small service that receives events from the AT firehose and produces them to Kafka. Supports standard JSON outputs as well as [Osprey](https://github.com/roostorg/osprey)
4
+
formatted events.
5
+
6
+
## Installation
7
+
8
+
```bash
9
+
go install github.com/haileyok/at-kafka/cmd/atkafka@latest
10
+
```
11
+
12
+
Or build from source:
13
+
14
+
```bash
15
+
git clone https://github.com/haileyok/at-kafka.git
16
+
cd at-kafka
17
+
go build -o atkafka ./cmd/atkafka
18
+
```
19
+
20
+
## Usage
21
+
22
+
### Basic Usage
23
+
24
+
```bash
25
+
atkafka \
26
+
--bootstrap-servers localhost:9092 \
27
+
--output-topic atproto-events
28
+
```
29
+
30
+
### With Osprey Event Formatting
31
+
32
+
```bash
33
+
atkafka \
34
+
--bootstrap-servers localhost:9092 \
35
+
--output-topic atproto-events \
36
+
--osprey-compatible
37
+
```
38
+
39
+
## Configuration
40
+
41
+
| Flag | Environment Variable | Default | Description |
42
+
|------|---------------------|---------|-------------|
43
+
| `--relay-host` | `ATKAFKA_RELAY_HOST` | `wss://bsky.network` | AT Protocol relay host to connect to |
44
+
| `--bootstrap-servers` | `ATKAFKA_BOOTSTRAP_SERVERS` | (required) | Comma-separated list of Kafka bootstrap servers |
45
+
| `--output-topic` | `ATKAFKA_OUTPUT_TOPIC` | (required) | Kafka topic to publish events to |
46
+
| `--osprey-compatible` | `ATKAFKA_OSPREY_COMPATIBLE` | `false` | Enable Osprey-compatible event format |
47
+
48
+
## Event Structure
49
+
50
+
### Standard Mode
51
+
52
+
Events are structured similarly to the raw AT Protocol firehose, with one key difference: **commit events are split into individual operation events**.
53
+
54
+
#### Operation Event
55
+
```json
56
+
{
57
+
"did": "did:plc:...",
58
+
"timestamp": "2024-01-01T12:00:00.000Z",
59
+
"operation": {
60
+
"action": "create",
61
+
"collection": "app.bsky.feed.post",
62
+
"rkey": "some-rkey",
63
+
"uri": "at://did:plc:123/app.bsky.feed.post/some-rkey",
64
+
"cid": "bafyrei...",
65
+
"path": "app.bsky.feed.post/...",
66
+
"record": {
67
+
"text": "Hello world!",
68
+
"$type": "app.bsky.feed.post",
69
+
"createdAt": "2024-01-01T12:00:00.000Z"
70
+
}
71
+
}
72
+
}
73
+
```
74
+
75
+
#### Account Event
76
+
```json
77
+
{
78
+
"did": "did:plc:...",
79
+
"timestamp": "2024-01-01T12:00:00.000Z",
80
+
"account": {
81
+
"active": true,
82
+
"seq": 12345,
83
+
"status": "active"
84
+
}
85
+
}
86
+
```
87
+
88
+
#### Identity Event
89
+
```json
90
+
{
91
+
"did": "did:plc:...",
92
+
"timestamp": "2024-01-01T12:00:00.000Z",
93
+
"identity": {
94
+
"seq": 12345,
95
+
"handle": "user.bsky.social"
96
+
}
97
+
}
98
+
```
99
+
100
+
### Osprey-Compatible Mode
101
+
102
+
When `--osprey-compatible` is enabled, events are wrapped in the Osprey event format:
103
+
104
+
```json
105
+
{
106
+
"data": {
107
+
"action_name": "operation#create",
108
+
"action_id": 1234567890,
109
+
"data": {
110
+
"did": "did:plc:...",
111
+
"timestamp": "2024-01-01T12:00:00.000Z",
112
+
"operation": { ... }
113
+
},
114
+
"timestamp": "2024-01-01T12:00:00.000Z",
115
+
"secret_data": {},
116
+
"encoding": "UTF8"
117
+
},
118
+
"send_time": "2024-01-01T12:00:00Z"
119
+
}
120
+
```
121
+
122
+
Action names in Osprey mode:
123
+
- `operation#create` - Record creation
124
+
- `operation#update` - Record update
125
+
- `operation#delete` - Record deletion
126
+
- `account` - Account status changes
127
+
- `identity` - Identity/handle changes
128
+
- `info` - Informational messages
129
+
130
+
## Monitoring
131
+
132
+
The service exposes Prometheus metrics on the default metrics port.
133
+
134
+
- `atkafka_handled_events` - Total events that are received on the firehose and handled
135
+
- `atkafka_produced_events` - Total messages that are output on the bus
+409
atkafka/atkafka.go
+409
atkafka/atkafka.go
···
1
+
package atkafka
2
+
3
+
import (
4
+
"bytes"
5
+
"context"
6
+
"encoding/json"
7
+
"fmt"
8
+
"log/slog"
9
+
"net/http"
10
+
"net/url"
11
+
"os"
12
+
"os/signal"
13
+
"strings"
14
+
"syscall"
15
+
"time"
16
+
17
+
"github.com/bluesky-social/indigo/atproto/atdata"
18
+
"github.com/bluesky-social/indigo/events"
19
+
"github.com/bluesky-social/indigo/events/schedulers/parallel"
20
+
"github.com/bluesky-social/indigo/repo"
21
+
"github.com/bluesky-social/indigo/repomgr"
22
+
"github.com/gorilla/websocket"
23
+
"github.com/twmb/franz-go/pkg/kgo"
24
+
)
25
+
26
+
type Server struct {
27
+
relayHost string
28
+
bootstrapServers []string
29
+
outputTopic string
30
+
ospreyCompat bool
31
+
logger *slog.Logger
32
+
33
+
atkProducer *Producer
34
+
ospProducer *Producer
35
+
}
36
+
37
+
type ServerArgs struct {
38
+
RelayHost string
39
+
BootstrapServers []string
40
+
OutputTopic string
41
+
OspreyCompat bool
42
+
Logger *slog.Logger
43
+
}
44
+
45
+
func NewServer(args *ServerArgs) *Server {
46
+
if args.Logger == nil {
47
+
args.Logger = slog.Default()
48
+
}
49
+
50
+
return &Server{
51
+
relayHost: args.RelayHost,
52
+
bootstrapServers: args.BootstrapServers,
53
+
outputTopic: args.OutputTopic,
54
+
ospreyCompat: args.OspreyCompat,
55
+
logger: args.Logger,
56
+
}
57
+
}
58
+
59
+
func (s *Server) Run(ctx context.Context) error {
60
+
wsDialer := websocket.DefaultDialer
61
+
u, err := url.Parse(s.relayHost)
62
+
if err != nil {
63
+
return fmt.Errorf("invalid relayHost: %w", err)
64
+
}
65
+
u.Path = "/xrpc/com.atproto.sync.subscribeRepos"
66
+
67
+
wsErr := make(chan error, 1)
68
+
shutdownWs := make(chan struct{}, 1)
69
+
go func() {
70
+
logger := s.logger.With("component", "websocket")
71
+
72
+
logger.Info("subscribing to repo event stream", "upstream", s.relayHost)
73
+
74
+
conn, _, err := wsDialer.Dial(u.String(), http.Header{
75
+
"User-Agent": []string{"at-kafka/0.0.0"},
76
+
})
77
+
if err != nil {
78
+
wsErr <- err
79
+
return
80
+
}
81
+
82
+
parallelism := 400
83
+
scheduler := parallel.NewScheduler(parallelism, 1000, s.relayHost, s.handleEvent)
84
+
defer scheduler.Shutdown()
85
+
86
+
logger.Info("firehose scheduler configured", "parallelism", parallelism)
87
+
88
+
go func() {
89
+
if err := events.HandleRepoStream(ctx, conn, scheduler, logger); err != nil {
90
+
wsErr <- err
91
+
return
92
+
}
93
+
}()
94
+
95
+
<-shutdownWs
96
+
97
+
wsErr <- nil
98
+
}()
99
+
100
+
producerLogger := s.logger.With("component", "producer")
101
+
if s.ospreyCompat {
102
+
kafProducer, err := NewProducer(ctx, producerLogger, s.bootstrapServers, s.outputTopic,
103
+
WithEnsureTopic(true),
104
+
WithTopicPartitions(200),
105
+
)
106
+
if err != nil {
107
+
return fmt.Errorf("failed to create producer: %w", err)
108
+
}
109
+
defer kafProducer.Close()
110
+
s.ospProducer = kafProducer
111
+
} else {
112
+
kafProducer, err := NewProducer(ctx, producerLogger, s.bootstrapServers, s.outputTopic,
113
+
WithEnsureTopic(true),
114
+
WithTopicPartitions(200),
115
+
)
116
+
if err != nil {
117
+
return fmt.Errorf("failed to create producer: %w", err)
118
+
}
119
+
defer kafProducer.Close()
120
+
s.atkProducer = kafProducer
121
+
}
122
+
123
+
signals := make(chan os.Signal, 1)
124
+
signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT)
125
+
126
+
select {
127
+
case sig := <-signals:
128
+
s.logger.Info("shutting down on signal", "signal", sig)
129
+
case err := <-wsErr:
130
+
if err != nil {
131
+
s.logger.Error("websocket error", "err", err)
132
+
} else {
133
+
s.logger.Info("websocket shutdown unexpectedly")
134
+
}
135
+
}
136
+
137
+
close(shutdownWs)
138
+
139
+
return nil
140
+
}
141
+
142
+
func (s *Server) handleEvent(ctx context.Context, evt *events.XRPCStreamEvent) error {
143
+
logger := s.logger.With("component", "handleEvent")
144
+
logger.Info("event", "seq", evt.Sequence())
145
+
146
+
var collection string
147
+
var actionName string
148
+
149
+
if evt.RepoCommit != nil {
150
+
// read the repo
151
+
rr, err := repo.ReadRepoFromCar(ctx, bytes.NewReader(evt.RepoCommit.Blocks))
152
+
if err != nil {
153
+
logger.Error("failed to read repo from car", "error", err)
154
+
return nil
155
+
}
156
+
157
+
for _, op := range evt.RepoCommit.Ops {
158
+
kind := repomgr.EventKind(op.Action)
159
+
collection = strings.Split(op.Path, "/")[0]
160
+
rkey := strings.Split(op.Path, "/")[1]
161
+
atUri := fmt.Sprintf("at://%s/%s/%s", evt.RepoCommit.Repo, collection, rkey)
162
+
163
+
kindStr := "create"
164
+
switch kind {
165
+
case repomgr.EvtKindUpdateRecord:
166
+
kindStr = "update"
167
+
case repomgr.EvtKindDeleteRecord:
168
+
kindStr = "delete"
169
+
}
170
+
actionName = "operation#" + kindStr
171
+
172
+
handledEvents.WithLabelValues(actionName, collection).Inc()
173
+
174
+
var rec map[string]any
175
+
var recCid string
176
+
if kind == repomgr.EvtKindCreateRecord || kind == repomgr.EvtKindUpdateRecord {
177
+
rcid, recB, err := rr.GetRecordBytes(ctx, op.Path)
178
+
if err != nil {
179
+
logger.Error("failed to get record bytes", "error", err)
180
+
continue
181
+
}
182
+
183
+
// verify the cids match
184
+
recCid = rcid.String()
185
+
if recCid != op.Cid.String() {
186
+
logger.Error("record cid mismatch", "expected", *op.Cid, "actual", rcid)
187
+
continue
188
+
}
189
+
190
+
// unmarshal the cbor into a map[string]any
191
+
maybeRec, err := atdata.UnmarshalCBOR(*recB)
192
+
if err != nil {
193
+
logger.Error("failed to unmarshal record", "error", err)
194
+
continue
195
+
}
196
+
rec = maybeRec
197
+
}
198
+
199
+
// create the formatted operation
200
+
atkOp := AtKafkaOp{
201
+
Action: op.Action,
202
+
Collection: collection,
203
+
Rkey: rkey,
204
+
Uri: atUri,
205
+
Cid: recCid,
206
+
Path: op.Path,
207
+
Record: rec,
208
+
}
209
+
210
+
// create the evt to put on kafka, regardless of if we are using osprey or not
211
+
kafkaEvt := AtKafkaEvent{
212
+
Did: evt.RepoCommit.Repo,
213
+
Timestamp: evt.RepoCommit.Time,
214
+
Operation: &atkOp,
215
+
}
216
+
217
+
var kafkaEvtBytes []byte
218
+
if s.ospreyCompat {
219
+
220
+
// create the wrapper event for osprey
221
+
ospreyKafkaEvent := OspreyAtKafkaEvent{
222
+
Data: OspreyEventData{
223
+
ActionName: actionName,
224
+
ActionId: time.Now().UnixNano(), // TODO: this should be a snowflake
225
+
Data: kafkaEvt,
226
+
Timestamp: evt.RepoCommit.Time,
227
+
SecretData: map[string]string{},
228
+
Encoding: "UTF8",
229
+
},
230
+
SendTime: time.Now().Format(time.RFC3339),
231
+
}
232
+
233
+
kafkaEvtBytes, err = json.Marshal(&ospreyKafkaEvent)
234
+
} else {
235
+
kafkaEvtBytes, err = json.Marshal(&kafkaEvt)
236
+
}
237
+
if err != nil {
238
+
return fmt.Errorf("failed to marshal kafka event: %w", err)
239
+
}
240
+
241
+
if err := s.produceAsync(ctx, evt.RepoCommit.Repo, kafkaEvtBytes); err != nil {
242
+
return err
243
+
}
244
+
}
245
+
} else {
246
+
defer func() {
247
+
handledEvents.WithLabelValues(actionName, "").Inc()
248
+
}()
249
+
250
+
// start with a kafka event and an action name
251
+
var kafkaEvt AtKafkaEvent
252
+
var timestamp string
253
+
var did string
254
+
255
+
if evt.RepoAccount != nil {
256
+
actionName = "account"
257
+
timestamp = evt.RepoAccount.Time
258
+
did = evt.RepoAccount.Did
259
+
260
+
kafkaEvt = AtKafkaEvent{
261
+
Did: evt.RepoAccount.Did,
262
+
Timestamp: evt.RepoAccount.Time,
263
+
Account: &AtKafkaAccount{
264
+
Active: evt.RepoAccount.Active,
265
+
Seq: evt.RepoAccount.Seq,
266
+
Status: evt.RepoAccount.Status,
267
+
},
268
+
}
269
+
} else if evt.RepoIdentity != nil {
270
+
actionName = "identity"
271
+
timestamp = evt.RepoIdentity.Time
272
+
did = evt.RepoIdentity.Did
273
+
274
+
kafkaEvt = AtKafkaEvent{
275
+
Did: evt.RepoIdentity.Did,
276
+
Timestamp: evt.RepoIdentity.Time,
277
+
Identity: &AtKafkaIdentity{
278
+
Seq: evt.RepoIdentity.Seq,
279
+
Handle: *evt.RepoIdentity.Handle,
280
+
},
281
+
}
282
+
} else if evt.RepoInfo != nil {
283
+
actionName = "info"
284
+
timestamp = time.Now().Format(time.RFC3339Nano)
285
+
286
+
kafkaEvt = AtKafkaEvent{
287
+
Info: &AtKafkaInfo{
288
+
Name: evt.RepoInfo.Name,
289
+
Message: evt.RepoInfo.Message,
290
+
},
291
+
}
292
+
} else {
293
+
return fmt.Errorf("unhandled event received")
294
+
}
295
+
296
+
// create the kafka event bytes
297
+
var kafkaEvtBytes []byte
298
+
var err error
299
+
300
+
if s.ospreyCompat {
301
+
// wrap the event in an osprey event
302
+
ospreyKafkaEvent := OspreyAtKafkaEvent{
303
+
Data: OspreyEventData{
304
+
ActionName: actionName,
305
+
ActionId: time.Now().UnixNano(), // TODO: this should be a snowflake
306
+
Data: kafkaEvt,
307
+
Timestamp: timestamp,
308
+
SecretData: map[string]string{},
309
+
Encoding: "UTF8",
310
+
},
311
+
SendTime: time.Now().Format(time.RFC3339),
312
+
}
313
+
314
+
kafkaEvtBytes, err = json.Marshal(&ospreyKafkaEvent)
315
+
} else {
316
+
kafkaEvtBytes, err = json.Marshal(&kafkaEvt)
317
+
}
318
+
if err != nil {
319
+
return fmt.Errorf("failed to marshal kafka event: %w", err)
320
+
}
321
+
322
+
if err := s.produceAsync(ctx, did, kafkaEvtBytes); err != nil {
323
+
return err
324
+
}
325
+
}
326
+
327
+
return nil
328
+
}
329
+
330
+
func (s *Server) produceAsync(ctx context.Context, key string, msg []byte) error {
331
+
if !s.ospreyCompat && s.atkProducer != nil {
332
+
if err := s.atkProducer.ProduceAsync(ctx, key, msg, func(r *kgo.Record, err error) {
333
+
status := "ok"
334
+
if err != nil {
335
+
status = "error"
336
+
s.logger.Error("error producing message", "err", err)
337
+
}
338
+
producedEvents.WithLabelValues(status).Inc()
339
+
}); err != nil {
340
+
return fmt.Errorf("failed to produce message: %w", err)
341
+
}
342
+
} else if s.ospreyCompat && s.ospProducer != nil {
343
+
if err := s.ospProducer.ProduceAsync(ctx, key, msg, func(r *kgo.Record, err error) {
344
+
status := "ok"
345
+
if err != nil {
346
+
status = "error"
347
+
s.logger.Error("error producing message", "err", err)
348
+
}
349
+
producedEvents.WithLabelValues(status).Inc()
350
+
}); err != nil {
351
+
return fmt.Errorf("failed to produce message: %w", err)
352
+
}
353
+
} else {
354
+
return fmt.Errorf("failed to produce message. no compatible producer registered.")
355
+
}
356
+
357
+
return nil
358
+
}
359
+
360
+
type AtKafkaOp struct {
361
+
Action string `json:"action"`
362
+
Collection string `json:"collection"`
363
+
Rkey string `json:"rkey"`
364
+
Uri string `json:"uri"`
365
+
Cid string `json:"cid"`
366
+
Path string `json:"path"`
367
+
Record map[string]any `json:"record"`
368
+
}
369
+
370
+
type AtKafkaIdentity struct {
371
+
Seq int64 `json:"seq"`
372
+
Handle string `json:"handle"`
373
+
}
374
+
375
+
type AtKafkaInfo struct {
376
+
Name string `json:"name"`
377
+
Message *string `json:"message,omitempty"`
378
+
}
379
+
380
+
type AtKafkaAccount struct {
381
+
Active bool `json:"active"`
382
+
Seq int64 `json:"seq"`
383
+
Status *string `json:"status,omitempty"`
384
+
}
385
+
386
+
type AtKafkaEvent struct {
387
+
Did string `json:"did"`
388
+
Timestamp string `json:"timestamp"`
389
+
390
+
Operation *AtKafkaOp `json:"operation,omitempty"`
391
+
Account *AtKafkaAccount `json:"account,omitempty"`
392
+
Identity *AtKafkaIdentity `json:"identity,omitempty"`
393
+
Info *AtKafkaInfo `json:"info,omitempty"`
394
+
}
395
+
396
+
// Intentionally using snake case since that is what Osprey expects
397
+
type OspreyEventData struct {
398
+
ActionName string `json:"action_name"`
399
+
ActionId int64 `json:"action_id"`
400
+
Data AtKafkaEvent `json:"data"`
401
+
Timestamp string `json:"timestamp"`
402
+
SecretData map[string]string `json:"secret_data"`
403
+
Encoding string `json:"encoding"`
404
+
}
405
+
406
+
type OspreyAtKafkaEvent struct {
407
+
Data OspreyEventData `json:"data"`
408
+
SendTime string `json:"send_time"`
409
+
}
+16
atkafka/metrics.go
+16
atkafka/metrics.go
···
1
+
package atkafka
2
+
3
+
import (
4
+
"github.com/prometheus/client_golang/prometheus"
5
+
"github.com/prometheus/client_golang/prometheus/promauto"
6
+
)
7
+
8
+
var handledEvents = promauto.NewCounterVec(prometheus.CounterOpts{
9
+
Namespace: "atkafka",
10
+
Name: "handled_events",
11
+
}, []string{"action_name", "collection"})
12
+
13
+
var producedEvents = promauto.NewCounterVec(prometheus.CounterOpts{
14
+
Namespace: "atkafka",
15
+
Name: "produced_events",
16
+
}, []string{"status"})
+284
atkafka/producer.go
+284
atkafka/producer.go
···
1
+
// This is copied from https://github.com/bluesky-social/go-util/blob/main/pkg/bus/producer/producer.go, except it
2
+
// removes the proto.message type and removes serialization. Should pass raw JSON bytes instead.
3
+
4
+
package atkafka
5
+
6
+
import (
7
+
"context"
8
+
"errors"
9
+
"fmt"
10
+
"log/slog"
11
+
"os"
12
+
"strings"
13
+
"sync"
14
+
"time"
15
+
16
+
"github.com/bluesky-social/go-util/pkg/bus/kafka"
17
+
"github.com/twmb/franz-go/pkg/kgo"
18
+
)
19
+
20
+
const (
21
+
DefaultClientID = "go-bus-producer"
22
+
ModeAsync = "async"
23
+
ModeSync = "sync"
24
+
)
25
+
26
+
// Producer is an example producer to a given topic using given Protobuf message type.
27
+
//
28
+
// A Producer takes a Kafka client and a topic, and sends one of two types of data:
29
+
//
30
+
// - A Protobuf message of the given type.
31
+
// - Invalid data that could not be parsed as any Protobuf message.
32
+
type Producer struct {
33
+
clientID string
34
+
client *kgo.Client
35
+
topic string
36
+
topicConfig []string
37
+
topicPartitions int
38
+
replicationFactor int
39
+
ensureTopic bool
40
+
clientConfig kafka.Config
41
+
logger *slog.Logger
42
+
defaultAsyncCallback func(r *kgo.Record, err error)
43
+
44
+
saslUsername string
45
+
saslPassword string
46
+
47
+
// Close the producer and flush any async writes.
48
+
// We can safely call this concurrently and multiple times, as it uses a sync.Once to ensure the client is only flushed and closed once.
49
+
Close func()
50
+
}
51
+
52
+
// New returns a new Producer.
53
+
//
54
+
// Always use this constructor to construct Producers.
55
+
func NewProducer(
56
+
ctx context.Context,
57
+
logger *slog.Logger,
58
+
bootstrapServers []string,
59
+
topic string,
60
+
options ...ProducerOption,
61
+
) (*Producer, error) {
62
+
if len(bootstrapServers) == 0 {
63
+
return nil, errors.New("at least one bootstrap server must be provided")
64
+
}
65
+
66
+
// By default, append the first bootstrap server's host to the client ID.
67
+
// This should allow us to connect to the Kafka broker using a different hostname
68
+
// than what the broker advertises, which is useful when connecting to a Kafka cluster
69
+
// from outside of the k8s cluster
70
+
parts := strings.Split(bootstrapServers[0], ":")
71
+
firstBootstrapHost := parts[0]
72
+
var clientID string
73
+
74
+
clientHostname, err := os.Hostname()
75
+
if err != nil {
76
+
clientID = fmt.Sprintf("%s;host_override=%s", DefaultClientID, firstBootstrapHost)
77
+
} else {
78
+
clientID = fmt.Sprintf("%s;host_override=%s", clientHostname, firstBootstrapHost)
79
+
}
80
+
81
+
producer := &Producer{
82
+
topic: topic,
83
+
clientID: clientID,
84
+
topicPartitions: 1,
85
+
replicationFactor: 1,
86
+
}
87
+
if logger != nil {
88
+
producer.logger = logger.With("component", "bus-producer", "topic", topic)
89
+
}
90
+
91
+
for _, option := range options {
92
+
option(producer)
93
+
}
94
+
95
+
producer.clientConfig = kafka.Config{
96
+
BootstrapServers: bootstrapServers,
97
+
ClientID: producer.clientID,
98
+
Topic: topic,
99
+
TopicConfig: producer.topicConfig,
100
+
TopicPartitions: producer.topicPartitions,
101
+
ReplicationFactor: producer.replicationFactor,
102
+
SASLUsername: producer.saslUsername,
103
+
SASLPassword: producer.saslPassword,
104
+
}
105
+
106
+
// Initialize the Kafka client if not already set.
107
+
if producer.client == nil {
108
+
// Default Kafka client configuration.
109
+
client, err := kafka.NewKafkaClient(producer.clientConfig, nil)
110
+
if err != nil {
111
+
return nil, fmt.Errorf("failed to create Kafka client: %w", err)
112
+
}
113
+
producer.client = client
114
+
}
115
+
116
+
// Ensure the topic exists if requested.
117
+
if producer.ensureTopic {
118
+
if err := kafka.EnsureTopic(ctx, producer.client, producer.clientConfig); err != nil {
119
+
return nil, fmt.Errorf("failed to ensure Kafka topic: %w", err)
120
+
}
121
+
}
122
+
123
+
producer.defaultAsyncCallback = func(r *kgo.Record, err error) {
124
+
if err != nil {
125
+
producer.log(ctx, slog.LevelError, "failed to async produce record", "key", string(r.Key), "err", err)
126
+
}
127
+
}
128
+
129
+
producer.Close = sync.OnceFunc(func() {
130
+
producer.close()
131
+
})
132
+
133
+
return producer, nil
134
+
}
135
+
136
+
// ProducerOption is an option when constructing a new Producer.
137
+
//
138
+
// All parameters except options are required. ProducerOptions allow
139
+
// for optional parameters.
140
+
type ProducerOption func(*Producer)
141
+
142
+
// WithTopicConfig allows setting topic configuration options for the producer.
143
+
func WithTopicConfig(config ...string) ProducerOption {
144
+
return func(p *Producer) {
145
+
p.topicConfig = config
146
+
}
147
+
}
148
+
149
+
// WithTopicPartitions allows setting the number of partitions for the topic.
150
+
func WithTopicPartitions(partitions int) ProducerOption {
151
+
return func(p *Producer) {
152
+
p.topicPartitions = partitions
153
+
}
154
+
}
155
+
156
+
// WithReplicationFactor allows setting the replication factor for the topic.
157
+
func WithReplicationFactor(replicationFactor int) ProducerOption {
158
+
return func(p *Producer) {
159
+
p.replicationFactor = replicationFactor
160
+
}
161
+
}
162
+
163
+
// WithRetentionBytes allows setting the retention bytes for the topic.
164
+
func WithRetentionBytes(bytes int) ProducerOption {
165
+
return func(p *Producer) {
166
+
p.topicConfig = append(p.topicConfig, fmt.Sprintf("retention.bytes=%d", bytes))
167
+
}
168
+
}
169
+
170
+
// WithRetentionTime allows setting the retention time for the topic (to millisecond precision).
171
+
func WithRetentionTime(duration time.Duration) ProducerOption {
172
+
return func(p *Producer) {
173
+
p.topicConfig = append(p.topicConfig, fmt.Sprintf("retention.ms=%d", int64(duration/time.Millisecond)))
174
+
}
175
+
}
176
+
177
+
// WithMaxMessageBytes allows setting the maximum message size for the topic.
178
+
// Default is 1MB (1048576 bytes).
179
+
func WithMaxMessageBytes(bytes int) ProducerOption {
180
+
return func(p *Producer) {
181
+
p.topicConfig = append(p.topicConfig, fmt.Sprintf("max.message.bytes=%d", bytes))
182
+
}
183
+
}
184
+
185
+
// WithInfiniteRetention allows setting the topic to have infinite retention time.
186
+
// By default Topics have a retention time of 7 days and infinite retention bytes.
187
+
func WithInfiniteRetention() ProducerOption {
188
+
return func(p *Producer) {
189
+
p.topicConfig = append(p.topicConfig, "retention.ms=-1")
190
+
}
191
+
}
192
+
193
+
// WithClient allows initializing the producer with the specified Kafka client.
194
+
// This overrides the provided topic configuration and partitions.
195
+
func WithClient(client *kgo.Client) ProducerOption {
196
+
return func(p *Producer) {
197
+
p.client = client
198
+
}
199
+
}
200
+
201
+
// WithHostOverride returns a new Producer Option that overrides the default broker host
202
+
// This allows us to connect to the Kafka broker using a different hostname than what the broker advertises.
203
+
func WithHostOverride(host string) ProducerOption {
204
+
return func(producer *Producer) {
205
+
if host != "" {
206
+
producer.clientID += ";host_override=" + host
207
+
}
208
+
}
209
+
}
210
+
211
+
// WithEnsureTopic allows setting whether to ensure the topic exists before producing messages.
212
+
// If true, the producer will attempt to create the topic if it does not exist.
213
+
func WithEnsureTopic(ensure bool) ProducerOption {
214
+
return func(p *Producer) {
215
+
p.ensureTopic = ensure
216
+
}
217
+
}
218
+
219
+
// WithCredentials sets the SASL username and password for the producer.
220
+
func WithCredentials(username, password string) ProducerOption {
221
+
return func(p *Producer) {
222
+
p.saslUsername = username
223
+
p.saslPassword = password
224
+
}
225
+
}
226
+
227
+
// ProduceAsync asynchronously sends the given message to
228
+
// the Producer's topic with the given partition key.
229
+
// This should return immediately, and the message will be sent in the background.
230
+
// Errors returned from this method are only related to serialization issues.
231
+
func (p *Producer) ProduceAsync(ctx context.Context, key string, message []byte, cb func(r *kgo.Record, err error)) error {
232
+
if cb == nil {
233
+
cb = p.defaultAsyncCallback
234
+
}
235
+
236
+
rec := &kgo.Record{
237
+
Key: []byte(key),
238
+
Value: message,
239
+
Topic: p.topic,
240
+
}
241
+
242
+
p.client.Produce(ctx, rec, cb)
243
+
244
+
return nil
245
+
}
246
+
247
+
// ProduceSync serializes the given Protobuf messages, and synchronously
248
+
// sends it to the Producer's topic with the given partition key.
249
+
// This will block until the message is sent or an error occurs.
250
+
func (p *Producer) ProduceSync(ctx context.Context, key string, message []byte) error {
251
+
if err := p.client.ProduceSync(ctx, &kgo.Record{
252
+
Key: []byte(key),
253
+
Value: message,
254
+
Topic: p.topic,
255
+
}).FirstErr(); err != nil {
256
+
return fmt.Errorf("failed to synchronously produce record with key %q: %w", key, err)
257
+
}
258
+
return nil
259
+
}
260
+
261
+
func (p *Producer) close() {
262
+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
263
+
defer cancel()
264
+
265
+
p.log(ctx, slog.LevelInfo, "closing producer, waiting up to 5 seconds to flush async writes")
266
+
267
+
err := p.client.Flush(ctx)
268
+
if err != nil {
269
+
if errors.Is(err, context.DeadlineExceeded) {
270
+
p.log(context.Background(), slog.LevelWarn, "producer flush timed out, closing client without waiting for all messages to be sent")
271
+
} else {
272
+
p.log(context.Background(), slog.LevelError, "failed to flush producer", "err", err)
273
+
}
274
+
} else {
275
+
p.logger.Info("producer flushed successfully on close")
276
+
}
277
+
p.client.Close()
278
+
}
279
+
280
+
func (p *Producer) log(ctx context.Context, level slog.Level, msg string, args ...any) {
281
+
if p.logger != nil {
282
+
p.logger.Log(ctx, level, msg, args...)
283
+
}
284
+
}
+65
cmd/atkafka/main.go
+65
cmd/atkafka/main.go
···
1
+
package main
2
+
3
+
import (
4
+
"context"
5
+
"fmt"
6
+
"log"
7
+
"os"
8
+
9
+
"github.com/bluesky-social/go-util/pkg/telemetry"
10
+
"github.com/haileyok/at-kafka/atkafka"
11
+
_ "github.com/joho/godotenv/autoload"
12
+
"github.com/urfave/cli/v2"
13
+
)
14
+
15
+
func main() {
16
+
app := cli.App{
17
+
Name: "at-kafka",
18
+
Flags: []cli.Flag{
19
+
&cli.StringFlag{
20
+
Name: "relay-host",
21
+
Value: "wss://bsky.network",
22
+
EnvVars: []string{"ATKAFKA_RELAY_HOST"},
23
+
},
24
+
&cli.StringSliceFlag{
25
+
Name: "bootstrap-servers",
26
+
EnvVars: []string{"ATKAFKA_BOOTSTRAP_SERVERS"},
27
+
Required: true,
28
+
},
29
+
&cli.StringFlag{
30
+
Name: "output-topic",
31
+
EnvVars: []string{"ATKAFKA_OUTPUT_TOPIC"},
32
+
Required: true,
33
+
},
34
+
&cli.BoolFlag{
35
+
Name: "osprey-compatible",
36
+
EnvVars: []string{"ATKAFKA_OSPREY_COMPATIBLE"},
37
+
Value: false,
38
+
},
39
+
},
40
+
Action: func(cmd *cli.Context) error {
41
+
ctx := context.Background()
42
+
43
+
telemetry.StartMetrics(cmd)
44
+
logger := telemetry.StartLogger(cmd)
45
+
46
+
s := atkafka.NewServer(&atkafka.ServerArgs{
47
+
RelayHost: cmd.String("relay-host"),
48
+
BootstrapServers: cmd.StringSlice("bootstrap-servers"),
49
+
OutputTopic: cmd.String("output-topic"),
50
+
OspreyCompat: cmd.Bool("osprey-compatible"),
51
+
Logger: logger,
52
+
})
53
+
54
+
if err := s.Run(ctx); err != nil {
55
+
return fmt.Errorf("error running server: %w", err)
56
+
}
57
+
58
+
return nil
59
+
},
60
+
}
61
+
62
+
if err := app.Run(os.Args); err != nil {
63
+
log.Fatal(err)
64
+
}
65
+
}
+58
docker-compose.yml
+58
docker-compose.yml
···
1
+
services:
2
+
zookeeper:
3
+
image: confluentinc/cp-zookeeper:7.6.0
4
+
hostname: zookeeper
5
+
container_name: zookeeper
6
+
ports:
7
+
- "2181:2181"
8
+
environment:
9
+
ZOOKEEPER_CLIENT_PORT: 2181
10
+
ZOOKEEPER_TICK_TIME: 2000
11
+
volumes:
12
+
- zookeeper-data:/var/lib/zookeeper/data
13
+
- zookeeper-logs:/var/lib/zookeeper/log
14
+
15
+
kafka:
16
+
image: confluentinc/cp-kafka:7.6.0
17
+
hostname: kafka
18
+
container_name: kafka
19
+
depends_on:
20
+
- zookeeper
21
+
ports:
22
+
- "9092:9092"
23
+
- "9101:9101"
24
+
environment:
25
+
KAFKA_BROKER_ID: 1
26
+
KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
27
+
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
28
+
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
29
+
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
30
+
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
31
+
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
32
+
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
33
+
KAFKA_JMX_PORT: 9101
34
+
KAFKA_JMX_HOSTNAME: localhost
35
+
KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
36
+
volumes:
37
+
- kafka-data:/var/lib/kafka/data
38
+
39
+
atkafka:
40
+
build:
41
+
context: .
42
+
dockerfile: Dockerfile
43
+
container_name: atkafka
44
+
depends_on:
45
+
- kafka
46
+
ports:
47
+
- "2112:2112"
48
+
environment:
49
+
ATKAFKA_RELAY_HOST: "wss://bsky.network"
50
+
ATKAFKA_BOOTSTRAP_SERVERS: "kafka:29092"
51
+
ATKAFKA_OUTPUT_TOPIC: "atproto-events"
52
+
ATKAFKA_OSPREY_COMPATIBLE: "false"
53
+
restart: unless-stopped
54
+
55
+
volumes:
56
+
zookeeper-data:
57
+
zookeeper-logs:
58
+
kafka-data:
+107
go.mod
+107
go.mod
···
1
+
module github.com/haileyok/at-kafka
2
+
3
+
go 1.25.4
4
+
5
+
require (
6
+
github.com/bluesky-social/go-util v0.0.0-20251012040650-2ebbf57f5934
7
+
github.com/bluesky-social/indigo v0.0.0-20251125184450-35c1e15d2e5f
8
+
github.com/gorilla/websocket v1.5.3
9
+
github.com/joho/godotenv v1.5.1
10
+
github.com/prometheus/client_golang v1.23.2
11
+
github.com/twmb/franz-go v1.19.5
12
+
github.com/urfave/cli/v2 v2.25.7
13
+
)
14
+
15
+
require (
16
+
github.com/RussellLuo/slidingwindow v0.0.0-20200528002341-535bb99d338b // indirect
17
+
github.com/beorn7/perks v1.0.1 // indirect
18
+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
19
+
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
20
+
github.com/earthboundkid/versioninfo/v2 v2.24.1 // indirect
21
+
github.com/felixge/httpsnoop v1.0.4 // indirect
22
+
github.com/go-logr/logr v1.4.2 // indirect
23
+
github.com/go-logr/stdr v1.2.2 // indirect
24
+
github.com/gocql/gocql v1.7.0 // indirect
25
+
github.com/gogo/protobuf v1.3.2 // indirect
26
+
github.com/golang/snappy v0.0.4 // indirect
27
+
github.com/google/uuid v1.6.0 // indirect
28
+
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
29
+
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
30
+
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
31
+
github.com/hashicorp/golang-lru v1.0.2 // indirect
32
+
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
33
+
github.com/ipfs/bbloom v0.0.4 // indirect
34
+
github.com/ipfs/go-block-format v0.2.0 // indirect
35
+
github.com/ipfs/go-blockservice v0.5.2 // indirect
36
+
github.com/ipfs/go-cid v0.4.1 // indirect
37
+
github.com/ipfs/go-datastore v0.6.0 // indirect
38
+
github.com/ipfs/go-ipfs-blockstore v1.3.1 // indirect
39
+
github.com/ipfs/go-ipfs-ds-help v1.1.1 // indirect
40
+
github.com/ipfs/go-ipfs-exchange-interface v0.2.1 // indirect
41
+
github.com/ipfs/go-ipfs-util v0.0.3 // indirect
42
+
github.com/ipfs/go-ipld-cbor v0.1.0 // indirect
43
+
github.com/ipfs/go-ipld-format v0.6.0 // indirect
44
+
github.com/ipfs/go-ipld-legacy v0.2.1 // indirect
45
+
github.com/ipfs/go-libipfs v0.7.0 // indirect
46
+
github.com/ipfs/go-log v1.0.5 // indirect
47
+
github.com/ipfs/go-log/v2 v2.5.1 // indirect
48
+
github.com/ipfs/go-merkledag v0.11.0 // indirect
49
+
github.com/ipfs/go-metrics-interface v0.0.1 // indirect
50
+
github.com/ipfs/go-verifcid v0.0.3 // indirect
51
+
github.com/ipld/go-car v0.6.1-0.20230509095817-92d28eb23ba4 // indirect
52
+
github.com/ipld/go-codec-dagpb v1.6.0 // indirect
53
+
github.com/ipld/go-ipld-prime v0.21.0 // indirect
54
+
github.com/jackc/pgpassfile v1.0.0 // indirect
55
+
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
56
+
github.com/jackc/pgx/v5 v5.5.0 // indirect
57
+
github.com/jackc/puddle/v2 v2.2.1 // indirect
58
+
github.com/jbenet/goprocess v0.1.4 // indirect
59
+
github.com/jinzhu/inflection v1.0.0 // indirect
60
+
github.com/jinzhu/now v1.1.5 // indirect
61
+
github.com/klauspost/compress v1.18.0 // indirect
62
+
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
63
+
github.com/mattn/go-isatty v0.0.20 // indirect
64
+
github.com/mattn/go-sqlite3 v1.14.22 // indirect
65
+
github.com/minio/sha256-simd v1.0.1 // indirect
66
+
github.com/mr-tron/base58 v1.2.0 // indirect
67
+
github.com/multiformats/go-base32 v0.1.0 // indirect
68
+
github.com/multiformats/go-base36 v0.2.0 // indirect
69
+
github.com/multiformats/go-multibase v0.2.0 // indirect
70
+
github.com/multiformats/go-multihash v0.2.3 // indirect
71
+
github.com/multiformats/go-varint v0.0.7 // indirect
72
+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
73
+
github.com/opentracing/opentracing-go v1.2.0 // indirect
74
+
github.com/pierrec/lz4/v4 v4.1.22 // indirect
75
+
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect
76
+
github.com/prometheus/client_model v0.6.2 // indirect
77
+
github.com/prometheus/common v0.66.1 // indirect
78
+
github.com/prometheus/procfs v0.16.1 // indirect
79
+
github.com/russross/blackfriday/v2 v2.1.0 // indirect
80
+
github.com/spaolacci/murmur3 v1.1.0 // indirect
81
+
github.com/twmb/franz-go/pkg/kadm v1.16.1 // indirect
82
+
github.com/twmb/franz-go/pkg/kmsg v1.11.2 // indirect
83
+
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e // indirect
84
+
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
85
+
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect
86
+
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect
87
+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
88
+
go.opentelemetry.io/otel v1.24.0 // indirect
89
+
go.opentelemetry.io/otel/metric v1.24.0 // indirect
90
+
go.opentelemetry.io/otel/trace v1.24.0 // indirect
91
+
go.uber.org/atomic v1.11.0 // indirect
92
+
go.uber.org/multierr v1.11.0 // indirect
93
+
go.uber.org/zap v1.26.0 // indirect
94
+
go.yaml.in/yaml/v2 v2.4.2 // indirect
95
+
golang.org/x/crypto v0.40.0 // indirect
96
+
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
97
+
golang.org/x/sync v0.16.0 // indirect
98
+
golang.org/x/sys v0.35.0 // indirect
99
+
golang.org/x/text v0.28.0 // indirect
100
+
golang.org/x/time v0.6.0 // indirect
101
+
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
102
+
google.golang.org/protobuf v1.36.9 // indirect
103
+
gopkg.in/inf.v0 v0.9.1 // indirect
104
+
gorm.io/driver/postgres v1.5.7 // indirect
105
+
gorm.io/gorm v1.25.9 // indirect
106
+
lukechampine.com/blake3 v1.2.1 // indirect
107
+
)
+413
go.sum
+413
go.sum
···
1
+
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
2
+
github.com/RussellLuo/slidingwindow v0.0.0-20200528002341-535bb99d338b h1:5/++qT1/z812ZqBvqQt6ToRswSuPZ/B33m6xVHRzADU=
3
+
github.com/RussellLuo/slidingwindow v0.0.0-20200528002341-535bb99d338b/go.mod h1:4+EPqMRApwwE/6yo6CxiHoSnBzjRr3jsqer7frxP8y4=
4
+
github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 h1:iW0a5ljuFxkLGPNem5Ui+KBjFJzKg4Fv2fnxe4dvzpM=
5
+
github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5/go.mod h1:Y2QMoi1vgtOIfc+6DhrMOGkLoGzqSV2rKp4Sm+opsyA=
6
+
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
7
+
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
8
+
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
9
+
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
10
+
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
11
+
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
12
+
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
13
+
github.com/bluesky-social/go-util v0.0.0-20251012040650-2ebbf57f5934 h1:btHMur2kTRgWEnCHn6LaI3BE9YRgsqTpwpJ1UdB7VEk=
14
+
github.com/bluesky-social/go-util v0.0.0-20251012040650-2ebbf57f5934/go.mod h1:LWamyZfbQGW7PaVc5jumFfjgrshJ5mXgDUnR6fK7+BI=
15
+
github.com/bluesky-social/indigo v0.0.0-20251125184450-35c1e15d2e5f h1:DNWIa5AsHDenBFyycb0CRxwSr+5GUN61nbbpKEpP4pc=
16
+
github.com/bluesky-social/indigo v0.0.0-20251125184450-35c1e15d2e5f/go.mod h1:GuGAU33qKulpZCZNPcUeIQ4RW6KzNvOy7s8MSUXbAng=
17
+
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
18
+
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
19
+
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
20
+
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
21
+
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
22
+
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
23
+
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
24
+
github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0=
25
+
github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis=
26
+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
27
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
28
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
29
+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
30
+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
31
+
github.com/earthboundkid/versioninfo/v2 v2.24.1 h1:SJTMHaoUx3GzjjnUO1QzP3ZXK6Ee/nbWyCm58eY3oUg=
32
+
github.com/earthboundkid/versioninfo/v2 v2.24.1/go.mod h1:VcWEooDEuyUJnMfbdTh0uFN4cfEIg+kHMuWB2CDCLjw=
33
+
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
34
+
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
35
+
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
36
+
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
37
+
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
38
+
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
39
+
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
40
+
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
41
+
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
42
+
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
43
+
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
44
+
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
45
+
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
46
+
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
47
+
github.com/gocql/gocql v1.7.0 h1:O+7U7/1gSN7QTEAaMEsJc1Oq2QHXvCWoF3DFK9HDHus=
48
+
github.com/gocql/gocql v1.7.0/go.mod h1:vnlvXyFZeLBF0Wy+RS8hrOdbn0UWsWtdg07XJnFxZ+4=
49
+
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
50
+
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
51
+
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
52
+
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
53
+
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
54
+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
55
+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
56
+
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
57
+
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
58
+
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
59
+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
60
+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
61
+
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
62
+
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
63
+
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
64
+
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
65
+
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
66
+
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
67
+
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
68
+
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
69
+
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
70
+
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
71
+
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
72
+
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
73
+
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
74
+
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
75
+
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
76
+
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
77
+
github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ=
78
+
github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y=
79
+
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
80
+
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
81
+
github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ=
82
+
github.com/ipfs/go-bitswap v0.11.0/go.mod h1:05aE8H3XOU+LXpTedeAS0OZpcO1WFsj5niYQH9a1Tmk=
83
+
github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs=
84
+
github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM=
85
+
github.com/ipfs/go-blockservice v0.5.2 h1:in9Bc+QcXwd1apOVM7Un9t8tixPKdaHQFdLSUM1Xgk8=
86
+
github.com/ipfs/go-blockservice v0.5.2/go.mod h1:VpMblFEqG67A/H2sHKAemeH9vlURVavlysbdUI632yk=
87
+
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
88
+
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=
89
+
github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk=
90
+
github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8=
91
+
github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=
92
+
github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
93
+
github.com/ipfs/go-ds-flatfs v0.5.1 h1:ZCIO/kQOS/PSh3vcF1H6a8fkRGS7pOfwfPdx4n/KJH4=
94
+
github.com/ipfs/go-ds-flatfs v0.5.1/go.mod h1:RWTV7oZD/yZYBKdbVIFXTX2fdY2Tbvl94NsWqmoyAX4=
95
+
github.com/ipfs/go-ipfs-blockstore v1.3.1 h1:cEI9ci7V0sRNivqaOr0elDsamxXFxJMMMy7PTTDQNsQ=
96
+
github.com/ipfs/go-ipfs-blockstore v1.3.1/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE=
97
+
github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ=
98
+
github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk=
99
+
github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ=
100
+
github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
101
+
github.com/ipfs/go-ipfs-ds-help v1.1.1 h1:B5UJOH52IbcfS56+Ul+sv8jnIV10lbjLF5eOO0C66Nw=
102
+
github.com/ipfs/go-ipfs-ds-help v1.1.1/go.mod h1:75vrVCkSdSFidJscs8n4W+77AtTpCIAdDGAwjitJMIo=
103
+
github.com/ipfs/go-ipfs-exchange-interface v0.2.1 h1:jMzo2VhLKSHbVe+mHNzYgs95n0+t0Q69GQ5WhRDZV/s=
104
+
github.com/ipfs/go-ipfs-exchange-interface v0.2.1/go.mod h1:MUsYn6rKbG6CTtsDp+lKJPmVt3ZrCViNyH3rfPGsZ2E=
105
+
github.com/ipfs/go-ipfs-exchange-offline v0.3.0 h1:c/Dg8GDPzixGd0MC8Jh6mjOwU57uYokgWRFidfvEkuA=
106
+
github.com/ipfs/go-ipfs-exchange-offline v0.3.0/go.mod h1:MOdJ9DChbb5u37M1IcbrRB02e++Z7521fMxqCNRrz9s=
107
+
github.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE=
108
+
github.com/ipfs/go-ipfs-pq v0.0.3/go.mod h1:btNw5hsHBpRcSSgZtiNm/SLj5gYIZ18AKtv3kERkRb4=
109
+
github.com/ipfs/go-ipfs-routing v0.3.0 h1:9W/W3N+g+y4ZDeffSgqhgo7BsBSJwPMcyssET9OWevc=
110
+
github.com/ipfs/go-ipfs-routing v0.3.0/go.mod h1:dKqtTFIql7e1zYsEuWLyuOU+E0WJWW8JjbTPLParDWo=
111
+
github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0=
112
+
github.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs=
113
+
github.com/ipfs/go-ipld-cbor v0.1.0 h1:dx0nS0kILVivGhfWuB6dUpMa/LAwElHPw1yOGYopoYs=
114
+
github.com/ipfs/go-ipld-cbor v0.1.0/go.mod h1:U2aYlmVrJr2wsUBU67K4KgepApSZddGRDWBYR0H4sCk=
115
+
github.com/ipfs/go-ipld-format v0.6.0 h1:VEJlA2kQ3LqFSIm5Vu6eIlSxD/Ze90xtc4Meten1F5U=
116
+
github.com/ipfs/go-ipld-format v0.6.0/go.mod h1:g4QVMTn3marU3qXchwjpKPKgJv+zF+OlaKMyhJ4LHPg=
117
+
github.com/ipfs/go-ipld-legacy v0.2.1 h1:mDFtrBpmU7b//LzLSypVrXsD8QxkEWxu5qVxN99/+tk=
118
+
github.com/ipfs/go-ipld-legacy v0.2.1/go.mod h1:782MOUghNzMO2DER0FlBR94mllfdCJCkTtDtPM51otM=
119
+
github.com/ipfs/go-libipfs v0.7.0 h1:Mi54WJTODaOL2/ZSm5loi3SwI3jI2OuFWUrQIkJ5cpM=
120
+
github.com/ipfs/go-libipfs v0.7.0/go.mod h1:KsIf/03CqhICzyRGyGo68tooiBE2iFbI/rXW7FhAYr0=
121
+
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
122
+
github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=
123
+
github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
124
+
github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=
125
+
github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=
126
+
github.com/ipfs/go-merkledag v0.11.0 h1:DgzwK5hprESOzS4O1t/wi6JDpyVQdvm9Bs59N/jqfBY=
127
+
github.com/ipfs/go-merkledag v0.11.0/go.mod h1:Q4f/1ezvBiJV0YCIXvt51W/9/kqJGH4I1LsA7+djsM4=
128
+
github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg=
129
+
github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY=
130
+
github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVztekOF0pg=
131
+
github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU=
132
+
github.com/ipfs/go-verifcid v0.0.3 h1:gmRKccqhWDocCRkC+a59g5QW7uJw5bpX9HWBevXa0zs=
133
+
github.com/ipfs/go-verifcid v0.0.3/go.mod h1:gcCtGniVzelKrbk9ooUSX/pM3xlH73fZZJDzQJRvOUw=
134
+
github.com/ipld/go-car v0.6.1-0.20230509095817-92d28eb23ba4 h1:oFo19cBmcP0Cmg3XXbrr0V/c+xU9U1huEZp8+OgBzdI=
135
+
github.com/ipld/go-car v0.6.1-0.20230509095817-92d28eb23ba4/go.mod h1:6nkFF8OmR5wLKBzRKi7/YFJpyYR7+oEn1DX+mMWnlLA=
136
+
github.com/ipld/go-car/v2 v2.13.1 h1:KnlrKvEPEzr5IZHKTXLAEub+tPrzeAFQVRlSQvuxBO4=
137
+
github.com/ipld/go-car/v2 v2.13.1/go.mod h1:QkdjjFNGit2GIkpQ953KBwowuoukoM75nP/JI1iDJdo=
138
+
github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc=
139
+
github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s=
140
+
github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E=
141
+
github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ=
142
+
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
143
+
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
144
+
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
145
+
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
146
+
github.com/jackc/pgx/v5 v5.5.0 h1:NxstgwndsTRy7eq9/kqYc/BZh5w2hHJV86wjvO+1xPw=
147
+
github.com/jackc/pgx/v5 v5.5.0/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
148
+
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
149
+
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
150
+
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
151
+
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
152
+
github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=
153
+
github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o=
154
+
github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
155
+
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
156
+
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
157
+
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
158
+
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
159
+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
160
+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
161
+
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
162
+
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
163
+
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
164
+
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
165
+
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
166
+
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
167
+
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
168
+
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
169
+
github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8=
170
+
github.com/koron/go-ssdp v0.0.3/go.mod h1:b2MxI6yh02pKrsyNoQUsk4+YNikaGhe4894J+Q5lDvA=
171
+
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
172
+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
173
+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
174
+
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
175
+
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
176
+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
177
+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
178
+
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
179
+
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
180
+
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
181
+
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
182
+
github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c=
183
+
github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic=
184
+
github.com/libp2p/go-libp2p v0.25.1 h1:YK+YDCHpYyTvitKWVxa5PfElgIpOONU01X5UcLEwJGA=
185
+
github.com/libp2p/go-libp2p v0.25.1/go.mod h1:xnK9/1d9+jeQCVvi/f1g12KqtVi/jP/SijtKV1hML3g=
186
+
github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw=
187
+
github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI=
188
+
github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0=
189
+
github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk=
190
+
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
191
+
github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg=
192
+
github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0=
193
+
github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM=
194
+
github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg=
195
+
github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM=
196
+
github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=
197
+
github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ=
198
+
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
199
+
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
200
+
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
201
+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
202
+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
203
+
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
204
+
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
205
+
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
206
+
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
207
+
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
208
+
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
209
+
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
210
+
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
211
+
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
212
+
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
213
+
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
214
+
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
215
+
github.com/multiformats/go-multiaddr v0.8.0 h1:aqjksEcqK+iD/Foe1RRFsGZh8+XFiGo7FgUCZlpv3LU=
216
+
github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs=
217
+
github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A=
218
+
github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk=
219
+
github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=
220
+
github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=
221
+
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
222
+
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
223
+
github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg=
224
+
github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k=
225
+
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
226
+
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
227
+
github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo=
228
+
github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q=
229
+
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
230
+
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
231
+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
232
+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
233
+
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
234
+
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
235
+
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk=
236
+
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw=
237
+
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
238
+
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
239
+
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
240
+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
241
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
242
+
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f h1:VXTQfuJj9vKR4TCkEuWIckKvdHFeJH/huIFJ9/cXOB0=
243
+
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw=
244
+
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
245
+
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
246
+
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
247
+
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
248
+
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
249
+
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
250
+
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
251
+
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
252
+
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
253
+
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
254
+
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
255
+
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
256
+
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
257
+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
258
+
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
259
+
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
260
+
github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
261
+
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
262
+
github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
263
+
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
264
+
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
265
+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
266
+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
267
+
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
268
+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
269
+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
270
+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
271
+
github.com/twmb/franz-go v1.19.5 h1:W7+o8D0RsQsedqib71OVlLeZ0zI6CbFra7yTYhZTs5Y=
272
+
github.com/twmb/franz-go v1.19.5/go.mod h1:4kFJ5tmbbl7asgwAGVuyG1ZMx0NNpYk7EqflvWfPCpM=
273
+
github.com/twmb/franz-go/pkg/kadm v1.16.1 h1:IEkrhTljgLHJ0/hT/InhXGjPdmWfFvxp7o/MR7vJ8cw=
274
+
github.com/twmb/franz-go/pkg/kadm v1.16.1/go.mod h1:Ue/ye1cc9ipsQFg7udFbbGiFNzQMqiH73fGC2y0rwyc=
275
+
github.com/twmb/franz-go/pkg/kmsg v1.11.2 h1:hIw75FpwcAjgeyfIGFqivAvwC5uNIOWRGvQgZhH4mhg=
276
+
github.com/twmb/franz-go/pkg/kmsg v1.11.2/go.mod h1:CFfkkLysDNmukPYhGzuUcDtf46gQSqCZHMW1T4Z+wDE=
277
+
github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
278
+
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
279
+
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
280
+
github.com/warpfork/go-testmark v0.12.1 h1:rMgCpJfwy1sJ50x0M0NgyphxYYPMOODIJHhsXyEHU0s=
281
+
github.com/warpfork/go-testmark v0.12.1/go.mod h1:kHwy7wfvGSPh1rQJYKayD4AbtNaeyZdcGi9tNJTaa5Y=
282
+
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ=
283
+
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
284
+
github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 h1:5HZfQkwe0mIfyDmc1Em5GqlNRzcdtlv4HTNmdpt7XH0=
285
+
github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11/go.mod h1:Wlo/SzPmxVp6vXpGt/zaXhHH0fn4IxgqZc82aKg6bpQ=
286
+
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4=
287
+
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so=
288
+
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
289
+
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
290
+
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
291
+
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
292
+
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
293
+
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b h1:CzigHMRySiX3drau9C6Q5CAbNIApmLdat5jPMqChvDA=
294
+
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8=
295
+
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q=
296
+
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I=
297
+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
298
+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
299
+
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
300
+
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
301
+
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
302
+
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
303
+
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
304
+
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
305
+
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
306
+
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
307
+
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
308
+
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
309
+
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
310
+
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
311
+
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
312
+
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
313
+
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
314
+
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
315
+
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
316
+
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
317
+
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
318
+
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
319
+
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
320
+
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
321
+
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
322
+
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
323
+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
324
+
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
325
+
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
326
+
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
327
+
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
328
+
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
329
+
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o=
330
+
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
331
+
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
332
+
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
333
+
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
334
+
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
335
+
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
336
+
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
337
+
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
338
+
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
339
+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
340
+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
341
+
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
342
+
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
343
+
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
344
+
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
345
+
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
346
+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
347
+
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
348
+
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
349
+
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
350
+
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
351
+
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
352
+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
353
+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
354
+
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
355
+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
356
+
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
357
+
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
358
+
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
359
+
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
360
+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
361
+
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
362
+
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
363
+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
364
+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
365
+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
366
+
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
367
+
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
368
+
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
369
+
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
370
+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
371
+
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
372
+
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
373
+
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
374
+
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
375
+
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
376
+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
377
+
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
378
+
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
379
+
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
380
+
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
381
+
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
382
+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
383
+
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
384
+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
385
+
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
386
+
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
387
+
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
388
+
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
389
+
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
390
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
391
+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
392
+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
393
+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
394
+
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
395
+
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
396
+
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
397
+
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
398
+
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
399
+
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
400
+
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
401
+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
402
+
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
403
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
404
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
405
+
gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM=
406
+
gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA=
407
+
gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E=
408
+
gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE=
409
+
gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8=
410
+
gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
411
+
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
412
+
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
413
+
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=