Signed-off-by: Anirudh Oppiliappan anirudh@tangled.sh
+31
-13
go.mod
+31
-13
go.mod
···
1
1
module tangled.sh/tangled.sh/core
2
2
3
-
go 1.24.0
4
-
5
-
toolchain go1.24.3
3
+
go 1.24.4
6
4
7
5
require (
8
6
github.com/Blank-Xu/sql-adapter v1.1.1
7
+
github.com/alecthomas/assert/v2 v2.11.0
9
8
github.com/alecthomas/chroma/v2 v2.15.0
10
9
github.com/avast/retry-go/v4 v4.6.1
11
10
github.com/bluekeyes/go-gitdiff v0.8.1
···
23
22
github.com/go-git/go-git/v5 v5.14.0
24
23
github.com/google/uuid v1.6.0
25
24
github.com/gorilla/sessions v1.4.0
26
-
github.com/gorilla/websocket v1.5.3
25
+
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
27
26
github.com/hiddeco/sshsig v0.2.0
28
27
github.com/hpcloud/tail v1.0.0
29
28
github.com/ipfs/go-cid v0.5.0
30
29
github.com/lestrrat-go/jwx/v2 v2.1.6
31
30
github.com/mattn/go-sqlite3 v1.14.24
32
31
github.com/microcosm-cc/bluemonday v1.0.27
32
+
github.com/openbao/openbao/api/v2 v2.3.0
33
33
github.com/posthog/posthog-go v1.5.5
34
-
github.com/redis/go-redis/v9 v9.3.0
34
+
github.com/redis/go-redis/v9 v9.7.3
35
35
github.com/resend/resend-go/v2 v2.15.0
36
36
github.com/sethvargo/go-envconfig v1.1.0
37
37
github.com/stretchr/testify v1.10.0
···
39
39
github.com/whyrusleeping/cbor-gen v0.3.1
40
40
github.com/yuin/goldmark v1.4.13
41
41
golang.org/x/crypto v0.40.0
42
-
golang.org/x/net v0.41.0
42
+
golang.org/x/net v0.42.0
43
+
golang.org/x/sync v0.16.0
43
44
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da
44
45
gopkg.in/yaml.v3 v3.0.1
45
46
tangled.sh/icyphox.sh/atproto-oauth v0.0.0-20250724194903-28e660378cb1
···
48
49
require (
49
50
dario.cat/mergo v1.0.1 // indirect
50
51
github.com/Microsoft/go-winio v0.6.2 // indirect
51
-
github.com/ProtonMail/go-crypto v1.2.0 // indirect
52
+
github.com/ProtonMail/go-crypto v1.3.0 // indirect
53
+
github.com/alecthomas/repr v0.4.0 // indirect
52
54
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
53
55
github.com/aymerick/douceur v0.2.0 // indirect
54
56
github.com/beorn7/perks v1.0.1 // indirect
55
57
github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect
56
58
github.com/casbin/govaluate v1.3.0 // indirect
59
+
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
57
60
github.com/cespare/xxhash/v2 v2.3.0 // indirect
58
-
github.com/cloudflare/circl v1.6.0 // indirect
61
+
github.com/cloudflare/circl v1.6.2-0.20250618153321-aa837fd1539d // indirect
59
62
github.com/containerd/errdefs v1.0.0 // indirect
60
63
github.com/containerd/errdefs/pkg v0.3.0 // indirect
61
64
github.com/containerd/log v0.1.0 // indirect
···
68
71
github.com/docker/go-units v0.5.0 // indirect
69
72
github.com/emirpasic/gods v1.18.1 // indirect
70
73
github.com/felixge/httpsnoop v1.0.4 // indirect
74
+
github.com/fsnotify/fsnotify v1.6.0 // indirect
71
75
github.com/go-enry/go-oniguruma v1.2.1 // indirect
72
76
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
73
77
github.com/go-git/go-billy/v5 v5.6.2 // indirect
78
+
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
74
79
github.com/go-logr/logr v1.4.3 // indirect
75
80
github.com/go-logr/stdr v1.2.2 // indirect
76
81
github.com/go-redis/cache/v9 v9.0.0 // indirect
82
+
github.com/go-test/deep v1.1.1 // indirect
77
83
github.com/goccy/go-json v0.10.5 // indirect
78
84
github.com/gogo/protobuf v1.3.2 // indirect
79
85
github.com/golang-jwt/jwt/v5 v5.2.3 // indirect
80
86
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
87
+
github.com/golang/mock v1.6.0 // indirect
81
88
github.com/gorilla/css v1.0.1 // indirect
82
89
github.com/gorilla/securecookie v1.1.2 // indirect
90
+
github.com/hashicorp/errwrap v1.1.0 // indirect
83
91
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
92
+
github.com/hashicorp/go-multierror v1.1.1 // indirect
84
93
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
94
+
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 // indirect
95
+
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
96
+
github.com/hashicorp/go-sockaddr v1.0.7 // indirect
85
97
github.com/hashicorp/golang-lru v1.0.2 // indirect
86
98
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
99
+
github.com/hashicorp/hcl v1.0.1-vault-7 // indirect
100
+
github.com/hexops/gotextdiff v1.0.3 // indirect
87
101
github.com/ipfs/bbloom v0.0.4 // indirect
88
102
github.com/ipfs/boxo v0.33.0 // indirect
89
103
github.com/ipfs/go-block-format v0.2.2 // indirect
···
105
119
github.com/lestrrat-go/option v1.0.1 // indirect
106
120
github.com/mattn/go-isatty v0.0.20 // indirect
107
121
github.com/minio/sha256-simd v1.0.1 // indirect
122
+
github.com/mitchellh/mapstructure v1.5.0 // indirect
108
123
github.com/moby/docker-image-spec v1.3.1 // indirect
109
124
github.com/moby/sys/atomicwriter v0.1.0 // indirect
110
125
github.com/moby/term v0.5.2 // indirect
···
116
131
github.com/multiformats/go-multihash v0.2.3 // indirect
117
132
github.com/multiformats/go-varint v0.0.7 // indirect
118
133
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
134
+
github.com/onsi/gomega v1.37.0 // indirect
119
135
github.com/opencontainers/go-digest v1.0.0 // indirect
120
136
github.com/opencontainers/image-spec v1.1.1 // indirect
121
-
github.com/opentracing/opentracing-go v1.2.0 // indirect
137
+
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
122
138
github.com/pjbgf/sha1cd v0.3.2 // indirect
123
139
github.com/pkg/errors v0.9.1 // indirect
124
140
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
···
127
143
github.com/prometheus/client_model v0.6.2 // indirect
128
144
github.com/prometheus/common v0.64.0 // indirect
129
145
github.com/prometheus/procfs v0.16.1 // indirect
146
+
github.com/ryanuber/go-glob v1.0.0 // indirect
130
147
github.com/segmentio/asm v1.2.0 // indirect
131
148
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
132
149
github.com/spaolacci/murmur3 v1.1.0 // indirect
···
138
155
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
139
156
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
140
157
go.opentelemetry.io/otel v1.37.0 // indirect
158
+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect
141
159
go.opentelemetry.io/otel/metric v1.37.0 // indirect
142
160
go.opentelemetry.io/otel/trace v1.37.0 // indirect
143
161
go.opentelemetry.io/proto/otlp v1.6.0 // indirect
···
145
163
go.uber.org/multierr v1.11.0 // indirect
146
164
go.uber.org/zap v1.27.0 // indirect
147
165
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
148
-
golang.org/x/sync v0.15.0 // indirect
149
166
golang.org/x/sys v0.34.0 // indirect
167
+
golang.org/x/text v0.27.0 // indirect
150
168
golang.org/x/time v0.12.0 // indirect
151
-
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect
152
-
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
153
-
google.golang.org/grpc v1.72.1 // indirect
169
+
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
170
+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
171
+
google.golang.org/grpc v1.73.0 // indirect
154
172
google.golang.org/protobuf v1.36.6 // indirect
155
173
gopkg.in/fsnotify.v1 v1.4.7 // indirect
156
174
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
+80
-96
go.sum
+80
-96
go.sum
···
7
7
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
8
8
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
9
9
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
10
-
github.com/ProtonMail/go-crypto v1.2.0 h1:+PhXXn4SPGd+qk76TlEePBfOfivE0zkWFenhGhFLzWs=
11
-
github.com/ProtonMail/go-crypto v1.2.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
10
+
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
11
+
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
12
12
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
13
13
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
14
14
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
···
23
23
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
24
24
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
25
25
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
26
-
github.com/bluesky-social/indigo v0.0.0-20250520232546-236dd575c91e h1:DVD+HxQsDCVJtAkjfIKZVaBNc3kayHaU+A2TJZkFdp4=
27
-
github.com/bluesky-social/indigo v0.0.0-20250520232546-236dd575c91e/go.mod h1:ovyxp8AMO1Hoe838vMJUbqHTZaAR8ABM3g3TXu+A5Ng=
28
26
github.com/bluesky-social/indigo v0.0.0-20250724221105-5827c8fb61bb h1:BqMNDZMfXwiRTJ6NvQotJ0qInn37JH5U8E+TF01CFHQ=
29
27
github.com/bluesky-social/indigo v0.0.0-20250724221105-5827c8fb61bb/go.mod h1:0XUyOCRtL4/OiyeqMTmr6RlVHQMDgw3LS7CfibuZR5Q=
30
28
github.com/bluesky-social/jetstream v0.0.0-20241210005130-ea96859b93d1 h1:CFvRtYNSnWRAi/98M3O466t9dYuwtesNbu6FVPymRrA=
···
53
51
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
54
52
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
55
53
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
56
-
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
57
-
github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
54
+
github.com/cloudflare/circl v1.6.2-0.20250618153321-aa837fd1539d h1:IiIprFGH6SqstblP0Y9NIo3eaUJGkI/YDOFVSL64Uq4=
55
+
github.com/cloudflare/circl v1.6.2-0.20250618153321-aa837fd1539d/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
58
56
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
59
57
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
60
58
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
···
93
91
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
94
92
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
95
93
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
96
-
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
97
-
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
94
+
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
95
+
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
98
96
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
99
97
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
100
98
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
101
-
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
102
99
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
100
+
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
101
+
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
103
102
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
104
103
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
105
104
github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0=
···
116
115
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
117
116
github.com/go-git/go-git-fixtures/v5 v5.0.0-20241203230421-0753e18f8f03 h1:LumE+tQdnYW24a9RoO08w64LHTzkNkdUqBD/0QPtlEY=
118
117
github.com/go-git/go-git-fixtures/v5 v5.0.0-20241203230421-0753e18f8f03/go.mod h1:hMKrMnUE4W0SJ7bFyM00dyz/HoknZoptGWzrj6M+dEM=
118
+
github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
119
+
github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
119
120
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
120
121
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
121
-
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
122
-
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
123
122
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
124
123
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
125
124
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
···
127
126
github.com/go-redis/cache/v9 v9.0.0 h1:0thdtFo0xJi0/WXbRVu8B066z8OvVymXTJGaXrVWnN0=
128
127
github.com/go-redis/cache/v9 v9.0.0/go.mod h1:cMwi1N8ASBOufbIvk7cdXe2PbPjK/WMRL95FFHWsSgI=
129
128
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
129
+
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
130
+
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
130
131
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
131
132
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
132
133
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
133
134
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
134
135
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
135
-
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
136
-
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
137
-
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
138
136
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
139
137
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
140
138
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
141
139
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
142
-
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
143
140
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
141
+
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
142
+
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
144
143
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
145
144
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
146
145
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
···
173
172
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
174
173
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
175
174
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
176
-
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
177
-
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
175
+
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
176
+
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
178
177
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
179
178
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
179
+
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
180
+
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
181
+
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
180
182
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
181
183
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
182
184
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
183
185
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
184
-
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
185
-
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
186
+
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
187
+
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
186
188
github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
187
189
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
190
+
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM=
191
+
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0=
192
+
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
193
+
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
194
+
github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw=
195
+
github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw=
188
196
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
189
197
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
190
198
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
191
199
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
200
+
github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I=
201
+
github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
192
202
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
193
203
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
194
204
github.com/hiddeco/sshsig v0.2.0 h1:gMWllgKCITXdydVkDL+Zro0PU96QI55LwUwebSwNTSw=
···
198
208
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
199
209
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
200
210
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
201
-
github.com/ipfs/boxo v0.30.0 h1:7afsoxPGGqfoH7Dum/wOTGUB9M5fb8HyKPMlLfBvIEQ=
202
-
github.com/ipfs/boxo v0.30.0/go.mod h1:BPqgGGyHB9rZZcPSzah2Dc9C+5Or3U1aQe7EH1H7370=
203
211
github.com/ipfs/boxo v0.33.0 h1:9ow3chwkDzMj0Deq4AWRUEI7WnIIV7SZhPTzzG2mmfw=
204
212
github.com/ipfs/boxo v0.33.0/go.mod h1:3IPh7YFcCIcKp6o02mCHovrPntoT5Pctj/7j4syh/RM=
205
-
github.com/ipfs/go-block-format v0.2.1 h1:96kW71XGNNa+mZw/MTzJrCpMhBWCrd9kBLoKm9Iip/Q=
206
-
github.com/ipfs/go-block-format v0.2.1/go.mod h1:frtvXHMQhM6zn7HvEQu+Qz5wSTj+04oEH/I+NjDgEjk=
207
213
github.com/ipfs/go-block-format v0.2.2 h1:uecCTgRwDIXyZPgYspaLXoMiMmxQpSx2aq34eNc4YvQ=
208
214
github.com/ipfs/go-block-format v0.2.2/go.mod h1:vmuefuWU6b+9kIU0vZJgpiJt1yicQz9baHXE8qR+KB8=
209
215
github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg=
···
218
224
github.com/ipfs/go-ipfs-ds-help v1.1.1/go.mod h1:75vrVCkSdSFidJscs8n4W+77AtTpCIAdDGAwjitJMIo=
219
225
github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0=
220
226
github.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs=
221
-
github.com/ipfs/go-ipld-cbor v0.2.0 h1:VHIW3HVIjcMd8m4ZLZbrYpwjzqlVUfjLM7oK4T5/YF0=
222
-
github.com/ipfs/go-ipld-cbor v0.2.0/go.mod h1:Cp8T7w1NKcu4AQJLqK0tWpd1nkgTxEVB5C6kVpLW6/0=
223
227
github.com/ipfs/go-ipld-cbor v0.2.1 h1:H05yEJbK/hxg0uf2AJhyerBDbjOuHX4yi+1U/ogRa7E=
224
228
github.com/ipfs/go-ipld-cbor v0.2.1/go.mod h1:x9Zbeq8CoE5R2WicYgBMcr/9mnkQ0lHddYWJP2sMV3A=
225
-
github.com/ipfs/go-ipld-format v0.6.1 h1:lQLmBM/HHbrXvjIkrydRXkn+gc0DE5xO5fqelsCKYOQ=
226
-
github.com/ipfs/go-ipld-format v0.6.1/go.mod h1:8TOH1Hj+LFyqM2PjSqI2/ZnyO0KlfhHbJLkbxFa61hs=
227
229
github.com/ipfs/go-ipld-format v0.6.2 h1:bPZQ+A05ol0b3lsJSl0bLvwbuQ+HQbSsdGTy4xtYUkU=
228
230
github.com/ipfs/go-ipld-format v0.6.2/go.mod h1:nni2xFdHKx5lxvXJ6brt/pndtGxKAE+FPR1rg4jTkyk=
229
231
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
···
233
235
github.com/ipfs/go-log/v2 v2.6.0/go.mod h1:p+Efr3qaY5YXpx9TX7MoLCSEZX5boSWj9wh86P5HJa8=
234
236
github.com/ipfs/go-metrics-interface v0.3.0 h1:YwG7/Cy4R94mYDUuwsBfeziJCVm9pBMJ6q/JR9V40TU=
235
237
github.com/ipfs/go-metrics-interface v0.3.0/go.mod h1:OxxQjZDGocXVdyTPocns6cOLwHieqej/jos7H4POwoY=
236
-
github.com/ipfs/go-test v0.2.1 h1:/D/a8xZ2JzkYqcVcV/7HYlCnc7bv/pKHQiX5TdClkPE=
237
-
github.com/ipfs/go-test v0.2.1/go.mod h1:dzu+KB9cmWjuJnXFDYJwC25T3j1GcN57byN+ixmK39M=
238
-
github.com/ipfs/go-test v0.2.2 h1:1yjYyfbdt1w93lVzde6JZ2einh3DIV40at4rVoyEcE8=
239
238
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
240
239
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
241
240
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
···
247
246
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
248
247
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
249
248
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
250
-
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
251
-
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
252
249
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
253
250
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
254
251
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
···
259
256
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
260
257
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
261
258
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
262
-
github.com/lestrrat-go/blackmagic v1.0.3 h1:94HXkVLxkZO9vJI/w2u1T0DAoprShFd13xtnSINtDWs=
263
-
github.com/lestrrat-go/blackmagic v1.0.3/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
264
259
github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=
265
260
github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
266
261
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
···
273
268
github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU=
274
269
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
275
270
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
276
-
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
277
-
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
278
-
github.com/libp2p/go-libp2p v0.41.1 h1:8ecNQVT5ev/jqALTvisSJeVNvXYJyK4NhQx1nNRXQZE=
279
-
github.com/libp2p/go-libp2p v0.41.1/go.mod h1:DcGTovJzQl/I7HMrby5ZRjeD0kQkGiy+9w6aEkSZpRI=
280
-
github.com/libp2p/go-libp2p v0.42.0 h1:A8foZk+ZEhZTv0Jb++7xUFlrFhBDv4j2Vh/uq4YX+KE=
281
-
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
282
-
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
271
+
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
272
+
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
283
273
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
284
274
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
285
275
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
···
288
278
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
289
279
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
290
280
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
281
+
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
282
+
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
291
283
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
292
284
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
293
285
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
···
304
296
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
305
297
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
306
298
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
307
-
github.com/multiformats/go-multiaddr v0.15.0 h1:zB/HeaI/apcZiTDwhY5YqMvNVl/oQYvs3XySU+qeAVo=
308
-
github.com/multiformats/go-multiaddr v0.15.0/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0=
309
-
github.com/multiformats/go-multiaddr v0.16.0 h1:oGWEVKioVQcdIOBlYM8BH1rZDWOGJSqr9/BKl6zQ4qc=
310
299
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
311
300
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
312
-
github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg=
313
-
github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k=
314
-
github.com/multiformats/go-multicodec v0.9.2 h1:YrlXCuqxjqm3bXl+vBq5LKz5pz4mvAsugdqy78k0pXQ=
315
301
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
316
302
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
317
303
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
···
343
329
github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
344
330
github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
345
331
github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
346
-
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
347
-
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
332
+
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
333
+
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
334
+
github.com/openbao/openbao/api/v2 v2.3.0 h1:61FO3ILtpKoxbD9kTWeGaCq8pz1sdt4dv2cmTXsiaAc=
335
+
github.com/openbao/openbao/api/v2 v2.3.0/go.mod h1:T47WKHb7DqHa3Ms3xicQtl5EiPE+U8diKjb9888okWs=
348
336
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
349
337
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
350
338
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
351
339
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
352
-
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
353
340
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
341
+
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
342
+
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
354
343
github.com/oppiliappan/chroma/v2 v2.19.0 h1:PN7/pb+6JRKCva30NPTtRJMlrOyzgpPpIroNzy4ekHU=
355
344
github.com/oppiliappan/chroma/v2 v2.19.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
356
345
github.com/oppiliappan/go-git/v5 v5.17.0 h1:CuJnpcIDxr0oiNaSHMconovSWnowHznVDG+AhjGuSEo=
···
371
360
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
372
361
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
373
362
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
374
-
github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
375
-
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
376
363
github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4=
377
364
github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
378
365
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
379
366
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
380
367
github.com/redis/go-redis/v9 v9.0.0-rc.4/go.mod h1:Vo3EsyWnicKnSKCA7HhgnvnyA74wOA69Cd2Meli5mmA=
381
-
github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0=
382
-
github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
368
+
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
369
+
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
383
370
github.com/resend/resend-go/v2 v2.15.0 h1:B6oMEPf8IEQwn2Ovx/9yymkESLDSeNfLFaNMw+mzHhE=
384
371
github.com/resend/resend-go/v2 v2.15.0/go.mod h1:3YCb8c8+pLiqhtRFXTyFwlLvfjQtluxOr9HEh2BwCkQ=
385
372
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
···
387
374
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
388
375
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
389
376
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
377
+
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
378
+
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
390
379
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
391
380
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
392
381
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
···
431
420
github.com/whyrusleeping/cbor-gen v0.3.1/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so=
432
421
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
433
422
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
423
+
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
434
424
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
435
425
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
436
426
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
···
440
430
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I=
441
431
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
442
432
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
443
-
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
444
-
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
445
433
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=
446
434
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY=
447
-
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
448
-
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
449
435
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
450
436
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
451
-
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk=
452
-
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME=
437
+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
438
+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
453
439
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU=
454
440
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
455
-
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
456
-
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
457
441
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
458
442
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
459
-
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
460
-
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
461
443
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
462
-
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
463
-
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
444
+
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
464
445
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
465
-
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
466
-
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
446
+
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
467
447
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
468
448
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
469
449
go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI=
···
488
468
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
489
469
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
490
470
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
491
-
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
492
-
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
471
+
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
493
472
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
494
473
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
495
-
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
496
-
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
497
474
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
498
475
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
499
476
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
500
477
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
501
478
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
502
479
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
480
+
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
503
481
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
504
482
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
505
483
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
506
484
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
485
+
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
507
486
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
508
487
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
509
488
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
···
512
491
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
513
492
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
514
493
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
494
+
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
515
495
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
516
496
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
517
497
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
···
521
501
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
522
502
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
523
503
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
524
-
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
525
-
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
526
-
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
527
-
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
504
+
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
505
+
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
506
+
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
507
+
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
528
508
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
529
509
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
530
510
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
···
532
512
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
533
513
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
534
514
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
535
-
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
536
-
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
537
-
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
538
-
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
515
+
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
516
+
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
539
517
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
540
518
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
541
519
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
···
547
525
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
548
526
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
549
527
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
528
+
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
550
529
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
530
+
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
551
531
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
552
532
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
553
533
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
···
555
535
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
556
536
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
557
537
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
538
+
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
558
539
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
559
540
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
560
541
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
561
542
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
543
+
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
562
544
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
563
-
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
564
-
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
545
+
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
546
+
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
565
547
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
566
548
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
567
549
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
···
570
552
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
571
553
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
572
554
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
573
-
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
574
-
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
555
+
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
556
+
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
557
+
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
575
558
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
559
+
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
576
560
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
577
561
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
578
562
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
···
580
564
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
581
565
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
582
566
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
583
-
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
584
-
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
567
+
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
568
+
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
569
+
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
585
570
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
586
-
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
587
-
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
571
+
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
588
572
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
589
573
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
590
574
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
···
598
582
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
599
583
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
600
584
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
585
+
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
601
586
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
602
587
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
603
588
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
604
589
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
590
+
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
605
591
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
606
592
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
607
593
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
608
594
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
609
595
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
610
596
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
611
-
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0=
612
-
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto=
613
-
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34=
614
-
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
615
-
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
616
-
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
597
+
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
598
+
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
599
+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
600
+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
601
+
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
602
+
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
617
603
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
618
604
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
619
605
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
···
650
636
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
651
637
lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=
652
638
lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo=
653
-
tangled.sh/icyphox.sh/atproto-oauth v0.0.0-20250526154904-3906c5336421 h1:ZQNKE1HKWjfQBizxDb2XLhrJcgZqE0MLFIU1iggaF90=
654
-
tangled.sh/icyphox.sh/atproto-oauth v0.0.0-20250526154904-3906c5336421/go.mod h1:Wad0H70uyyY4qZryU/1Ic+QZyw41YxN5QfzNAEBaXkQ=
655
639
tangled.sh/icyphox.sh/atproto-oauth v0.0.0-20250724194903-28e660378cb1 h1:z1os1aRIqeo5e8d0Tx7hk+LH8OdZZeIOY0zw9VB/ZoU=
656
640
tangled.sh/icyphox.sh/atproto-oauth v0.0.0-20250724194903-28e660378cb1/go.mod h1:+oQi9S6IIDll0nxLZVhuzOPX8WKLCYEnE6M5kUKupDg=
657
641
tangled.sh/oppi.li/go-gitdiff v0.8.2 h1:pASJJNWaFn6EmEIUNNjHZQ3stRu6BqTO2YyjKvTcxIc=
+9
-1
spindle/config/config.go
+9
-1
spindle/config/config.go
···
23
23
}
24
24
25
25
type Secrets struct {
26
-
Provider string `env:"PROVIDER, default=sqlite"`
26
+
Provider string `env:"PROVIDER, default=sqlite"`
27
+
OpenBao OpenBaoConfig `env:",prefix=OPENBAO_"`
28
+
}
29
+
30
+
type OpenBaoConfig struct {
31
+
Addr string `env:"ADDR"`
32
+
RoleID string `env:"ROLE_ID"`
33
+
SecretID string `env:"SECRET_ID"`
34
+
Mount string `env:"MOUNT, default=spindle"`
27
35
}
28
36
29
37
type Pipelines struct {
+1
-1
spindle/engine/engine.go
+1
-1
spindle/engine/engine.go
···
74
74
// extract secrets
75
75
var allSecrets []secrets.UnlockedSecret
76
76
if didSlashRepo, err := securejoin.SecureJoin(pipeline.RepoOwner, pipeline.RepoName); err == nil {
77
-
if res, err := e.vault.GetSecretsUnlocked(secrets.DidSlashRepo(didSlashRepo)); err == nil {
77
+
if res, err := e.vault.GetSecretsUnlocked(ctx, secrets.DidSlashRepo(didSlashRepo)); err == nil {
78
78
allSecrets = res
79
79
}
80
80
}
+11
-4
spindle/secrets/manager.go
+11
-4
spindle/secrets/manager.go
···
1
1
package secrets
2
2
3
3
import (
4
+
"context"
4
5
"errors"
5
6
"regexp"
6
7
"time"
···
26
27
type UnlockedSecret = Secret[string]
27
28
28
29
type Manager interface {
29
-
AddSecret(secret UnlockedSecret) error
30
-
RemoveSecret(secret Secret[any]) error
31
-
GetSecretsLocked(repo DidSlashRepo) ([]LockedSecret, error)
32
-
GetSecretsUnlocked(repo DidSlashRepo) ([]UnlockedSecret, error)
30
+
AddSecret(ctx context.Context, secret UnlockedSecret) error
31
+
RemoveSecret(ctx context.Context, secret Secret[any]) error
32
+
GetSecretsLocked(ctx context.Context, repo DidSlashRepo) ([]LockedSecret, error)
33
+
GetSecretsUnlocked(ctx context.Context, repo DidSlashRepo) ([]UnlockedSecret, error)
34
+
}
35
+
36
+
// stopper interface for managers that need cleanup
37
+
type Stopper interface {
38
+
Stop()
33
39
}
34
40
35
41
var ErrKeyAlreadyPresent = errors.New("key already present")
···
40
46
var (
41
47
_ = []Manager{
42
48
&SqliteManager{},
49
+
&OpenBaoManager{},
43
50
}
44
51
)
45
52
+407
spindle/secrets/openbao.go
+407
spindle/secrets/openbao.go
···
1
+
package secrets
2
+
3
+
import (
4
+
"context"
5
+
"fmt"
6
+
"log/slog"
7
+
"path"
8
+
"strings"
9
+
"sync"
10
+
"time"
11
+
12
+
"github.com/bluesky-social/indigo/atproto/syntax"
13
+
vault "github.com/openbao/openbao/api/v2"
14
+
)
15
+
16
+
type OpenBaoManager struct {
17
+
client *vault.Client
18
+
mountPath string
19
+
roleID string
20
+
secretID string
21
+
stopCh chan struct{}
22
+
tokenMu sync.RWMutex
23
+
logger *slog.Logger
24
+
}
25
+
26
+
type OpenBaoManagerOpt func(*OpenBaoManager)
27
+
28
+
func WithMountPath(mountPath string) OpenBaoManagerOpt {
29
+
return func(v *OpenBaoManager) {
30
+
v.mountPath = mountPath
31
+
}
32
+
}
33
+
34
+
func NewOpenBaoManager(address, roleID, secretID string, logger *slog.Logger, opts ...OpenBaoManagerOpt) (*OpenBaoManager, error) {
35
+
if address == "" {
36
+
return nil, fmt.Errorf("address cannot be empty")
37
+
}
38
+
if roleID == "" {
39
+
return nil, fmt.Errorf("role_id cannot be empty")
40
+
}
41
+
if secretID == "" {
42
+
return nil, fmt.Errorf("secret_id cannot be empty")
43
+
}
44
+
45
+
config := vault.DefaultConfig()
46
+
config.Address = address
47
+
48
+
client, err := vault.NewClient(config)
49
+
if err != nil {
50
+
return nil, fmt.Errorf("failed to create openbao client: %w", err)
51
+
}
52
+
53
+
// Authenticate using AppRole
54
+
err = authenticateAppRole(client, roleID, secretID)
55
+
if err != nil {
56
+
return nil, fmt.Errorf("failed to authenticate with AppRole: %w", err)
57
+
}
58
+
59
+
manager := &OpenBaoManager{
60
+
client: client,
61
+
mountPath: "spindle", // default KV v2 mount path
62
+
roleID: roleID,
63
+
secretID: secretID,
64
+
stopCh: make(chan struct{}),
65
+
logger: logger,
66
+
}
67
+
68
+
for _, opt := range opts {
69
+
opt(manager)
70
+
}
71
+
72
+
go manager.tokenRenewalLoop()
73
+
74
+
return manager, nil
75
+
}
76
+
77
+
// authenticateAppRole authenticates the client using AppRole method
78
+
func authenticateAppRole(client *vault.Client, roleID, secretID string) error {
79
+
authData := map[string]interface{}{
80
+
"role_id": roleID,
81
+
"secret_id": secretID,
82
+
}
83
+
84
+
resp, err := client.Logical().Write("auth/approle/login", authData)
85
+
if err != nil {
86
+
return fmt.Errorf("failed to login with AppRole: %w", err)
87
+
}
88
+
89
+
if resp == nil || resp.Auth == nil {
90
+
return fmt.Errorf("no auth info returned from AppRole login")
91
+
}
92
+
93
+
client.SetToken(resp.Auth.ClientToken)
94
+
return nil
95
+
}
96
+
97
+
// stop stops the token renewal goroutine
98
+
func (v *OpenBaoManager) Stop() {
99
+
close(v.stopCh)
100
+
}
101
+
102
+
// tokenRenewalLoop runs in a background goroutine to automatically renew or re-authenticate tokens
103
+
func (v *OpenBaoManager) tokenRenewalLoop() {
104
+
ticker := time.NewTicker(30 * time.Second) // Check every 30 seconds
105
+
defer ticker.Stop()
106
+
107
+
for {
108
+
select {
109
+
case <-v.stopCh:
110
+
return
111
+
case <-ticker.C:
112
+
ctx := context.Background()
113
+
if err := v.ensureValidToken(ctx); err != nil {
114
+
v.logger.Error("openbao token renewal failed", "error", err)
115
+
}
116
+
}
117
+
}
118
+
}
119
+
120
+
// ensureValidToken checks if the current token is valid and renews or re-authenticates if needed
121
+
func (v *OpenBaoManager) ensureValidToken(ctx context.Context) error {
122
+
v.tokenMu.Lock()
123
+
defer v.tokenMu.Unlock()
124
+
125
+
// check current token info
126
+
tokenInfo, err := v.client.Auth().Token().LookupSelf()
127
+
if err != nil {
128
+
// token is invalid, need to re-authenticate
129
+
v.logger.Warn("token lookup failed, re-authenticating", "error", err)
130
+
return v.reAuthenticate()
131
+
}
132
+
133
+
if tokenInfo == nil || tokenInfo.Data == nil {
134
+
return v.reAuthenticate()
135
+
}
136
+
137
+
// check TTL
138
+
ttlRaw, ok := tokenInfo.Data["ttl"]
139
+
if !ok {
140
+
return v.reAuthenticate()
141
+
}
142
+
143
+
var ttl int64
144
+
switch t := ttlRaw.(type) {
145
+
case int64:
146
+
ttl = t
147
+
case float64:
148
+
ttl = int64(t)
149
+
case int:
150
+
ttl = int64(t)
151
+
default:
152
+
return v.reAuthenticate()
153
+
}
154
+
155
+
// if TTL is less than 5 minutes, try to renew
156
+
if ttl < 300 {
157
+
v.logger.Info("token ttl low, attempting renewal", "ttl_seconds", ttl)
158
+
159
+
renewResp, err := v.client.Auth().Token().RenewSelf(3600) // 1h
160
+
if err != nil {
161
+
v.logger.Warn("token renewal failed, re-authenticating", "error", err)
162
+
return v.reAuthenticate()
163
+
}
164
+
165
+
if renewResp == nil || renewResp.Auth == nil {
166
+
v.logger.Warn("token renewal returned no auth info, re-authenticating")
167
+
return v.reAuthenticate()
168
+
}
169
+
170
+
v.logger.Info("token renewed successfully", "new_ttl_seconds", renewResp.Auth.LeaseDuration)
171
+
}
172
+
173
+
return nil
174
+
}
175
+
176
+
// reAuthenticate performs a fresh authentication using AppRole
177
+
func (v *OpenBaoManager) reAuthenticate() error {
178
+
v.logger.Info("re-authenticating with approle")
179
+
180
+
err := authenticateAppRole(v.client, v.roleID, v.secretID)
181
+
if err != nil {
182
+
return fmt.Errorf("re-authentication failed: %w", err)
183
+
}
184
+
185
+
v.logger.Info("re-authentication successful")
186
+
return nil
187
+
}
188
+
189
+
func (v *OpenBaoManager) AddSecret(ctx context.Context, secret UnlockedSecret) error {
190
+
v.tokenMu.RLock()
191
+
defer v.tokenMu.RUnlock()
192
+
if err := ValidateKey(secret.Key); err != nil {
193
+
return err
194
+
}
195
+
196
+
secretPath := v.buildSecretPath(secret.Repo, secret.Key)
197
+
198
+
fmt.Println(v.mountPath, secretPath)
199
+
200
+
existing, err := v.client.KVv2(v.mountPath).Get(ctx, secretPath)
201
+
if err == nil && existing != nil {
202
+
return ErrKeyAlreadyPresent
203
+
}
204
+
205
+
secretData := map[string]interface{}{
206
+
"value": secret.Value,
207
+
"repo": string(secret.Repo),
208
+
"key": secret.Key,
209
+
"created_at": secret.CreatedAt.Format(time.RFC3339),
210
+
"created_by": secret.CreatedBy.String(),
211
+
}
212
+
213
+
_, err = v.client.KVv2(v.mountPath).Put(ctx, secretPath, secretData)
214
+
if err != nil {
215
+
return fmt.Errorf("failed to store secret in openbao: %w", err)
216
+
}
217
+
218
+
return nil
219
+
}
220
+
221
+
func (v *OpenBaoManager) RemoveSecret(ctx context.Context, secret Secret[any]) error {
222
+
v.tokenMu.RLock()
223
+
defer v.tokenMu.RUnlock()
224
+
secretPath := v.buildSecretPath(secret.Repo, secret.Key)
225
+
226
+
existing, err := v.client.KVv2(v.mountPath).Get(ctx, secretPath)
227
+
if err != nil || existing == nil {
228
+
return ErrKeyNotFound
229
+
}
230
+
231
+
err = v.client.KVv2(v.mountPath).Delete(ctx, secretPath)
232
+
if err != nil {
233
+
return fmt.Errorf("failed to delete secret from openbao: %w", err)
234
+
}
235
+
236
+
return nil
237
+
}
238
+
239
+
func (v *OpenBaoManager) GetSecretsLocked(ctx context.Context, repo DidSlashRepo) ([]LockedSecret, error) {
240
+
v.tokenMu.RLock()
241
+
defer v.tokenMu.RUnlock()
242
+
repoPath := v.buildRepoPath(repo)
243
+
244
+
secretsList, err := v.client.Logical().List(fmt.Sprintf("%s/metadata/%s", v.mountPath, repoPath))
245
+
if err != nil {
246
+
if strings.Contains(err.Error(), "no secret found") || strings.Contains(err.Error(), "no handler for route") {
247
+
return []LockedSecret{}, nil
248
+
}
249
+
return nil, fmt.Errorf("failed to list secrets: %w", err)
250
+
}
251
+
252
+
if secretsList == nil || secretsList.Data == nil {
253
+
return []LockedSecret{}, nil
254
+
}
255
+
256
+
keys, ok := secretsList.Data["keys"].([]interface{})
257
+
if !ok {
258
+
return []LockedSecret{}, nil
259
+
}
260
+
261
+
var secrets []LockedSecret
262
+
263
+
for _, keyInterface := range keys {
264
+
key, ok := keyInterface.(string)
265
+
if !ok {
266
+
continue
267
+
}
268
+
269
+
secretPath := path.Join(repoPath, key)
270
+
secretData, err := v.client.KVv2(v.mountPath).Get(ctx, secretPath)
271
+
if err != nil {
272
+
continue // Skip secrets we can't read
273
+
}
274
+
275
+
if secretData == nil || secretData.Data == nil {
276
+
continue
277
+
}
278
+
279
+
data := secretData.Data
280
+
281
+
createdAtStr, ok := data["created_at"].(string)
282
+
if !ok {
283
+
createdAtStr = time.Now().Format(time.RFC3339)
284
+
}
285
+
286
+
createdAt, err := time.Parse(time.RFC3339, createdAtStr)
287
+
if err != nil {
288
+
createdAt = time.Now()
289
+
}
290
+
291
+
createdByStr, ok := data["created_by"].(string)
292
+
if !ok {
293
+
createdByStr = ""
294
+
}
295
+
296
+
keyStr, ok := data["key"].(string)
297
+
if !ok {
298
+
keyStr = key
299
+
}
300
+
301
+
secret := LockedSecret{
302
+
Key: keyStr,
303
+
Repo: repo,
304
+
CreatedAt: createdAt,
305
+
CreatedBy: syntax.DID(createdByStr),
306
+
}
307
+
308
+
secrets = append(secrets, secret)
309
+
}
310
+
311
+
return secrets, nil
312
+
}
313
+
314
+
func (v *OpenBaoManager) GetSecretsUnlocked(ctx context.Context, repo DidSlashRepo) ([]UnlockedSecret, error) {
315
+
v.tokenMu.RLock()
316
+
defer v.tokenMu.RUnlock()
317
+
repoPath := v.buildRepoPath(repo)
318
+
319
+
secretsList, err := v.client.Logical().List(fmt.Sprintf("%s/metadata/%s", v.mountPath, repoPath))
320
+
if err != nil {
321
+
if strings.Contains(err.Error(), "no secret found") || strings.Contains(err.Error(), "no handler for route") {
322
+
return []UnlockedSecret{}, nil
323
+
}
324
+
return nil, fmt.Errorf("failed to list secrets: %w", err)
325
+
}
326
+
327
+
if secretsList == nil || secretsList.Data == nil {
328
+
return []UnlockedSecret{}, nil
329
+
}
330
+
331
+
keys, ok := secretsList.Data["keys"].([]interface{})
332
+
if !ok {
333
+
return []UnlockedSecret{}, nil
334
+
}
335
+
336
+
var secrets []UnlockedSecret
337
+
338
+
for _, keyInterface := range keys {
339
+
key, ok := keyInterface.(string)
340
+
if !ok {
341
+
continue
342
+
}
343
+
344
+
secretPath := path.Join(repoPath, key)
345
+
secretData, err := v.client.KVv2(v.mountPath).Get(ctx, secretPath)
346
+
if err != nil {
347
+
continue
348
+
}
349
+
350
+
if secretData == nil || secretData.Data == nil {
351
+
continue
352
+
}
353
+
354
+
data := secretData.Data
355
+
356
+
valueStr, ok := data["value"].(string)
357
+
if !ok {
358
+
continue // skip secrets without values
359
+
}
360
+
361
+
createdAtStr, ok := data["created_at"].(string)
362
+
if !ok {
363
+
createdAtStr = time.Now().Format(time.RFC3339)
364
+
}
365
+
366
+
createdAt, err := time.Parse(time.RFC3339, createdAtStr)
367
+
if err != nil {
368
+
createdAt = time.Now()
369
+
}
370
+
371
+
createdByStr, ok := data["created_by"].(string)
372
+
if !ok {
373
+
createdByStr = ""
374
+
}
375
+
376
+
keyStr, ok := data["key"].(string)
377
+
if !ok {
378
+
keyStr = key
379
+
}
380
+
381
+
secret := UnlockedSecret{
382
+
Key: keyStr,
383
+
Value: valueStr,
384
+
Repo: repo,
385
+
CreatedAt: createdAt,
386
+
CreatedBy: syntax.DID(createdByStr),
387
+
}
388
+
389
+
secrets = append(secrets, secret)
390
+
}
391
+
392
+
return secrets, nil
393
+
}
394
+
395
+
// buildRepoPath creates an OpenBao path for a repository
396
+
func (v *OpenBaoManager) buildRepoPath(repo DidSlashRepo) string {
397
+
// convert DidSlashRepo to a safe path by replacing special characters
398
+
repoPath := strings.ReplaceAll(string(repo), "/", "_")
399
+
repoPath = strings.ReplaceAll(repoPath, ":", "_")
400
+
repoPath = strings.ReplaceAll(repoPath, ".", "_")
401
+
return fmt.Sprintf("repos/%s", repoPath)
402
+
}
403
+
404
+
// buildSecretPath creates an OpenBao path for a specific secret
405
+
func (v *OpenBaoManager) buildSecretPath(repo DidSlashRepo, key string) string {
406
+
return path.Join(v.buildRepoPath(repo), key)
407
+
}
+189
spindle/secrets/openbao_test.go
+189
spindle/secrets/openbao_test.go
···
1
+
package secrets
2
+
3
+
import (
4
+
"log/slog"
5
+
"os"
6
+
"testing"
7
+
"time"
8
+
9
+
"github.com/bluesky-social/indigo/atproto/syntax"
10
+
"github.com/stretchr/testify/assert"
11
+
)
12
+
13
+
func createTestSecretForOpenBao(repo, key, value, createdBy string) UnlockedSecret {
14
+
return UnlockedSecret{
15
+
Key: key,
16
+
Value: value,
17
+
Repo: DidSlashRepo(repo),
18
+
CreatedAt: time.Now(),
19
+
CreatedBy: syntax.DID(createdBy),
20
+
}
21
+
}
22
+
23
+
func TestOpenBaoManagerInterface(t *testing.T) {
24
+
var _ Manager = (*OpenBaoManager)(nil)
25
+
}
26
+
27
+
func TestNewOpenBaoManager(t *testing.T) {
28
+
tests := []struct {
29
+
name string
30
+
address string
31
+
roleID string
32
+
secretID string
33
+
opts []OpenBaoManagerOpt
34
+
expectError bool
35
+
errorContains string
36
+
}{
37
+
{
38
+
name: "empty address",
39
+
address: "",
40
+
roleID: "test-role-id",
41
+
secretID: "test-secret-id",
42
+
opts: nil,
43
+
expectError: true,
44
+
errorContains: "address cannot be empty",
45
+
},
46
+
{
47
+
name: "empty role_id",
48
+
address: "http://localhost:8200",
49
+
roleID: "",
50
+
secretID: "test-secret-id",
51
+
opts: nil,
52
+
expectError: true,
53
+
errorContains: "role_id cannot be empty",
54
+
},
55
+
{
56
+
name: "empty secret_id",
57
+
address: "http://localhost:8200",
58
+
roleID: "test-role-id",
59
+
secretID: "",
60
+
opts: nil,
61
+
expectError: true,
62
+
errorContains: "secret_id cannot be empty",
63
+
},
64
+
}
65
+
66
+
for _, tt := range tests {
67
+
t.Run(tt.name, func(t *testing.T) {
68
+
logger := slog.New(slog.NewTextHandler(os.Stderr, nil))
69
+
manager, err := NewOpenBaoManager(tt.address, tt.roleID, tt.secretID, logger, tt.opts...)
70
+
71
+
if tt.expectError {
72
+
assert.Error(t, err)
73
+
assert.Nil(t, manager)
74
+
assert.Contains(t, err.Error(), tt.errorContains)
75
+
} else {
76
+
// For valid configurations, we expect an error during authentication
77
+
// since we're not connecting to a real OpenBao server
78
+
assert.Error(t, err)
79
+
assert.Nil(t, manager)
80
+
}
81
+
})
82
+
}
83
+
}
84
+
85
+
func TestOpenBaoManager_PathBuilding(t *testing.T) {
86
+
manager := &OpenBaoManager{mountPath: "secret"}
87
+
88
+
tests := []struct {
89
+
name string
90
+
repo DidSlashRepo
91
+
key string
92
+
expected string
93
+
}{
94
+
{
95
+
name: "simple repo path",
96
+
repo: DidSlashRepo("did:plc:foo/repo"),
97
+
key: "api_key",
98
+
expected: "repos/did_plc_foo_repo/api_key",
99
+
},
100
+
{
101
+
name: "complex repo path with dots",
102
+
repo: DidSlashRepo("did:web:example.com/my-repo"),
103
+
key: "secret_key",
104
+
expected: "repos/did_web_example_com_my-repo/secret_key",
105
+
},
106
+
}
107
+
108
+
for _, tt := range tests {
109
+
t.Run(tt.name, func(t *testing.T) {
110
+
result := manager.buildSecretPath(tt.repo, tt.key)
111
+
assert.Equal(t, tt.expected, result)
112
+
})
113
+
}
114
+
}
115
+
116
+
func TestOpenBaoManager_buildRepoPath(t *testing.T) {
117
+
manager := &OpenBaoManager{mountPath: "test"}
118
+
119
+
tests := []struct {
120
+
name string
121
+
repo DidSlashRepo
122
+
expected string
123
+
}{
124
+
{
125
+
name: "simple repo",
126
+
repo: "did:plc:test/myrepo",
127
+
expected: "repos/did_plc_test_myrepo",
128
+
},
129
+
{
130
+
name: "repo with dots",
131
+
repo: "did:plc:example.com/my.repo",
132
+
expected: "repos/did_plc_example_com_my_repo",
133
+
},
134
+
{
135
+
name: "complex repo",
136
+
repo: "did:web:example.com:8080/path/to/repo",
137
+
expected: "repos/did_web_example_com_8080_path_to_repo",
138
+
},
139
+
}
140
+
141
+
for _, tt := range tests {
142
+
t.Run(tt.name, func(t *testing.T) {
143
+
result := manager.buildRepoPath(tt.repo)
144
+
assert.Equal(t, tt.expected, result)
145
+
})
146
+
}
147
+
}
148
+
149
+
func TestWithMountPath(t *testing.T) {
150
+
manager := &OpenBaoManager{mountPath: "default"}
151
+
152
+
opt := WithMountPath("custom-mount")
153
+
opt(manager)
154
+
155
+
assert.Equal(t, "custom-mount", manager.mountPath)
156
+
}
157
+
158
+
func TestOpenBaoManager_Stop(t *testing.T) {
159
+
// Create a manager with minimal setup
160
+
manager := &OpenBaoManager{
161
+
mountPath: "test",
162
+
stopCh: make(chan struct{}),
163
+
}
164
+
165
+
// Verify the manager implements Stopper interface
166
+
var stopper Stopper = manager
167
+
assert.NotNil(t, stopper)
168
+
169
+
// Call Stop and verify it doesn't panic
170
+
assert.NotPanics(t, func() {
171
+
manager.Stop()
172
+
})
173
+
174
+
// Verify the channel was closed
175
+
select {
176
+
case <-manager.stopCh:
177
+
// Channel was closed as expected
178
+
default:
179
+
t.Error("Expected stop channel to be closed after Stop()")
180
+
}
181
+
}
182
+
183
+
func TestOpenBaoManager_StopperInterface(t *testing.T) {
184
+
manager := &OpenBaoManager{}
185
+
186
+
// Verify that OpenBaoManager implements the Stopper interface
187
+
_, ok := interface{}(manager).(Stopper)
188
+
assert.True(t, ok, "OpenBaoManager should implement Stopper interface")
189
+
}
+15
spindle/secrets/policy.hcl
+15
spindle/secrets/policy.hcl
···
1
+
# KV v2 data operations
2
+
path "spindle/data/*" {
3
+
capabilities = ["create", "read", "update", "delete", "list"]
4
+
}
5
+
6
+
# KV v2 metadata operations (needed for listing)
7
+
path "spindle/metadata/*" {
8
+
capabilities = ["list", "read", "delete"]
9
+
}
10
+
11
+
# Root path access (needed for mount-level operations)
12
+
path "spindle/*" {
13
+
capabilities = ["list"]
14
+
}
15
+
+9
-8
spindle/secrets/sqlite.go
+9
-8
spindle/secrets/sqlite.go
···
2
2
package secrets
3
3
4
4
import (
5
+
"context"
5
6
"database/sql"
6
7
"fmt"
7
8
"time"
···
61
62
return err
62
63
}
63
64
64
-
func (s *SqliteManager) AddSecret(secret UnlockedSecret) error {
65
+
func (s *SqliteManager) AddSecret(ctx context.Context, secret UnlockedSecret) error {
65
66
query := fmt.Sprintf(`
66
67
insert or ignore into %s (repo, key, value, created_by)
67
68
values (?, ?, ?, ?);
68
69
`, s.tableName)
69
70
70
-
res, err := s.db.Exec(query, secret.Repo, secret.Key, secret.Value, secret.CreatedBy)
71
+
res, err := s.db.ExecContext(ctx, query, secret.Repo, secret.Key, secret.Value, secret.CreatedBy)
71
72
if err != nil {
72
73
return err
73
74
}
···
84
85
return nil
85
86
}
86
87
87
-
func (s *SqliteManager) RemoveSecret(secret Secret[any]) error {
88
+
func (s *SqliteManager) RemoveSecret(ctx context.Context, secret Secret[any]) error {
88
89
query := fmt.Sprintf(`
89
90
delete from %s where repo = ? and key = ?;
90
91
`, s.tableName)
91
92
92
-
res, err := s.db.Exec(query, secret.Repo, secret.Key)
93
+
res, err := s.db.ExecContext(ctx, query, secret.Repo, secret.Key)
93
94
if err != nil {
94
95
return err
95
96
}
···
106
107
return nil
107
108
}
108
109
109
-
func (s *SqliteManager) GetSecretsLocked(didSlashRepo DidSlashRepo) ([]LockedSecret, error) {
110
+
func (s *SqliteManager) GetSecretsLocked(ctx context.Context, didSlashRepo DidSlashRepo) ([]LockedSecret, error) {
110
111
query := fmt.Sprintf(`
111
112
select repo, key, created_at, created_by from %s where repo = ?;
112
113
`, s.tableName)
113
114
114
-
rows, err := s.db.Query(query, didSlashRepo)
115
+
rows, err := s.db.QueryContext(ctx, query, didSlashRepo)
115
116
if err != nil {
116
117
return nil, err
117
118
}
···
138
139
return ls, nil
139
140
}
140
141
141
-
func (s *SqliteManager) GetSecretsUnlocked(didSlashRepo DidSlashRepo) ([]UnlockedSecret, error) {
142
+
func (s *SqliteManager) GetSecretsUnlocked(ctx context.Context, didSlashRepo DidSlashRepo) ([]UnlockedSecret, error) {
142
143
query := fmt.Sprintf(`
143
144
select repo, key, value, created_at, created_by from %s where repo = ?;
144
145
`, s.tableName)
145
146
146
-
rows, err := s.db.Query(query, didSlashRepo)
147
+
rows, err := s.db.QueryContext(ctx, query, didSlashRepo)
147
148
if err != nil {
148
149
return nil, err
149
150
}
+31
-21
spindle/secrets/sqlite_test.go
+31
-21
spindle/secrets/sqlite_test.go
···
1
1
package secrets
2
2
3
3
import (
4
+
"context"
4
5
"testing"
5
6
"time"
6
7
8
+
"github.com/alecthomas/assert/v2"
7
9
"github.com/bluesky-social/indigo/atproto/syntax"
8
10
)
9
11
···
122
124
defer manager.db.Close()
123
125
124
126
for i, secret := range tt.secrets {
125
-
err := manager.AddSecret(secret)
127
+
err := manager.AddSecret(context.Background(), secret)
126
128
if err != tt.expectError[i] {
127
129
t.Errorf("Secret %d: expected error %v, got %v", i, tt.expectError[i], err)
128
130
}
···
189
191
190
192
// Setup secrets
191
193
for _, secret := range tt.setupSecrets {
192
-
if err := manager.AddSecret(secret); err != nil {
194
+
if err := manager.AddSecret(context.Background(), secret); err != nil {
193
195
t.Fatalf("Failed to setup secret: %v", err)
194
196
}
195
197
}
196
198
197
199
// Test removal
198
-
err := manager.RemoveSecret(tt.removeSecret)
200
+
err := manager.RemoveSecret(context.Background(), tt.removeSecret)
199
201
if err != tt.expectError {
200
202
t.Errorf("Expected error %v, got %v", tt.expectError, err)
201
203
}
···
262
264
263
265
// Setup secrets
264
266
for _, secret := range tt.setupSecrets {
265
-
if err := manager.AddSecret(secret); err != nil {
267
+
if err := manager.AddSecret(context.Background(), secret); err != nil {
266
268
t.Fatalf("Failed to setup secret: %v", err)
267
269
}
268
270
}
269
271
270
272
// Test getting locked secrets
271
-
lockedSecrets, err := manager.GetSecretsLocked(tt.queryRepo)
273
+
lockedSecrets, err := manager.GetSecretsLocked(context.Background(), tt.queryRepo)
272
274
if tt.expectError && err == nil {
273
275
t.Error("Expected error but got none")
274
276
return
···
369
371
370
372
// Setup secrets
371
373
for _, secret := range tt.setupSecrets {
372
-
if err := manager.AddSecret(secret); err != nil {
374
+
if err := manager.AddSecret(context.Background(), secret); err != nil {
373
375
t.Fatalf("Failed to setup secret: %v", err)
374
376
}
375
377
}
376
378
377
379
// Test getting unlocked secrets
378
-
unlockedSecrets, err := manager.GetSecretsUnlocked(tt.queryRepo)
380
+
unlockedSecrets, err := manager.GetSecretsUnlocked(context.Background(), tt.queryRepo)
379
381
if tt.expectError && err == nil {
380
382
t.Error("Expected error but got none")
381
383
return
···
424
426
operations: []func(Manager) error{
425
427
func(m Manager) error {
426
428
secret := createTestSecret("interface.test/repo", "test_key", "test_value", "did:plc:user")
427
-
return m.AddSecret(secret)
429
+
return m.AddSecret(context.Background(), secret)
428
430
},
429
431
func(m Manager) error {
430
-
_, err := m.GetSecretsLocked(DidSlashRepo("interface.test/repo"))
432
+
_, err := m.GetSecretsLocked(context.Background(), DidSlashRepo("interface.test/repo"))
431
433
return err
432
434
},
433
435
func(m Manager) error {
434
-
_, err := m.GetSecretsUnlocked(DidSlashRepo("interface.test/repo"))
436
+
_, err := m.GetSecretsUnlocked(context.Background(), DidSlashRepo("interface.test/repo"))
435
437
return err
436
438
},
437
439
func(m Manager) error {
···
439
441
Key: "test_key",
440
442
Repo: DidSlashRepo("interface.test/repo"),
441
443
}
442
-
return m.RemoveSecret(secret)
444
+
return m.RemoveSecret(context.Background(), secret)
443
445
},
444
446
},
445
447
expectError: false,
···
449
451
operations: []func(Manager) error{
450
452
func(m Manager) error {
451
453
secret := createTestSecret("interface.test/repo", "dup_key", "value1", "did:plc:user")
452
-
return m.AddSecret(secret)
454
+
return m.AddSecret(context.Background(), secret)
453
455
},
454
456
func(m Manager) error {
455
457
secret := createTestSecret("interface.test/repo", "dup_key", "value2", "did:plc:user")
456
-
return m.AddSecret(secret) // Should return ErrKeyAlreadyPresent
458
+
return m.AddSecret(context.Background(), secret) // Should return ErrKeyAlreadyPresent
457
459
},
458
460
},
459
461
expectError: true,
···
507
509
508
510
// Add all secrets
509
511
for _, secret := range secrets {
510
-
if err := manager.AddSecret(secret); err != nil {
512
+
if err := manager.AddSecret(context.Background(), secret); err != nil {
511
513
t.Fatalf("Failed to add secret %s: %v", secret.Key, err)
512
514
}
513
515
}
514
516
515
517
// Verify counts
516
-
locked1, _ := manager.GetSecretsLocked(repo1)
517
-
locked2, _ := manager.GetSecretsLocked(repo2)
518
+
locked1, _ := manager.GetSecretsLocked(context.Background(), repo1)
519
+
locked2, _ := manager.GetSecretsLocked(context.Background(), repo2)
518
520
519
521
if len(locked1) != 2 {
520
522
t.Errorf("Expected 2 secrets for repo1, got %d", len(locked1))
···
525
527
526
528
// Remove and verify
527
529
secretToRemove := Secret[any]{Key: "db_password", Repo: repo1}
528
-
if err := manager.RemoveSecret(secretToRemove); err != nil {
530
+
if err := manager.RemoveSecret(context.Background(), secretToRemove); err != nil {
529
531
t.Fatalf("Failed to remove secret: %v", err)
530
532
}
531
533
532
-
locked1After, _ := manager.GetSecretsLocked(repo1)
534
+
locked1After, _ := manager.GetSecretsLocked(context.Background(), repo1)
533
535
if len(locked1After) != 1 {
534
536
t.Errorf("Expected 1 secret for repo1 after removal, got %d", len(locked1After))
535
537
}
···
544
546
repo := DidSlashRepo("empty.test/repo")
545
547
546
548
// Operations on empty database should not error
547
-
locked, err := manager.GetSecretsLocked(repo)
549
+
locked, err := manager.GetSecretsLocked(context.Background(), repo)
548
550
if err != nil {
549
551
t.Errorf("GetSecretsLocked on empty DB failed: %v", err)
550
552
}
···
552
554
t.Errorf("Expected 0 secrets, got %d", len(locked))
553
555
}
554
556
555
-
unlocked, err := manager.GetSecretsUnlocked(repo)
557
+
unlocked, err := manager.GetSecretsUnlocked(context.Background(), repo)
556
558
if err != nil {
557
559
t.Errorf("GetSecretsUnlocked on empty DB failed: %v", err)
558
560
}
···
562
564
563
565
// Remove from empty should return ErrKeyNotFound
564
566
nonExistent := Secret[any]{Key: "none", Repo: repo}
565
-
err = manager.RemoveSecret(nonExistent)
567
+
err = manager.RemoveSecret(context.Background(), nonExistent)
566
568
if err != ErrKeyNotFound {
567
569
t.Errorf("Expected ErrKeyNotFound, got %v", err)
568
570
}
···
578
580
})
579
581
}
580
582
}
583
+
584
+
func TestSqliteManager_StopperInterface(t *testing.T) {
585
+
manager := &SqliteManager{}
586
+
587
+
// Verify that SqliteManager does NOT implement the Stopper interface
588
+
_, ok := interface{}(manager).(Stopper)
589
+
assert.False(t, ok, "SqliteManager should NOT implement Stopper interface")
590
+
}
+36
-4
spindle/server.go
+36
-4
spindle/server.go
···
68
68
69
69
n := notifier.New()
70
70
71
-
// TODO: add hashicorp vault provider and choose here
72
-
vault, err := secrets.NewSQLiteManager(cfg.Server.DBPath, secrets.WithTableName("secrets"))
73
-
if err != nil {
74
-
return fmt.Errorf("failed to setup secrets provider: %w", err)
71
+
var vault secrets.Manager
72
+
switch cfg.Server.Secrets.Provider {
73
+
case "openbao":
74
+
if cfg.Server.Secrets.OpenBao.Addr == "" {
75
+
return fmt.Errorf("openbao address is required when using openbao secrets provider")
76
+
}
77
+
if cfg.Server.Secrets.OpenBao.RoleID == "" {
78
+
return fmt.Errorf("openbao role_id is required when using openbao secrets provider")
79
+
}
80
+
if cfg.Server.Secrets.OpenBao.SecretID == "" {
81
+
return fmt.Errorf("openbao secret_id is required when using openbao secrets provider")
82
+
}
83
+
vault, err = secrets.NewOpenBaoManager(
84
+
cfg.Server.Secrets.OpenBao.Addr,
85
+
cfg.Server.Secrets.OpenBao.RoleID,
86
+
cfg.Server.Secrets.OpenBao.SecretID,
87
+
logger,
88
+
secrets.WithMountPath(cfg.Server.Secrets.OpenBao.Mount),
89
+
)
90
+
if err != nil {
91
+
return fmt.Errorf("failed to setup openbao secrets provider: %w", err)
92
+
}
93
+
logger.Info("using openbao secrets provider", "address", cfg.Server.Secrets.OpenBao.Addr, "mount", cfg.Server.Secrets.OpenBao.Mount)
94
+
case "sqlite", "":
95
+
vault, err = secrets.NewSQLiteManager(cfg.Server.DBPath, secrets.WithTableName("secrets"))
96
+
if err != nil {
97
+
return fmt.Errorf("failed to setup sqlite secrets provider: %w", err)
98
+
}
99
+
logger.Info("using sqlite secrets provider", "path", cfg.Server.DBPath)
100
+
default:
101
+
return fmt.Errorf("unknown secrets provider: %s", cfg.Server.Secrets.Provider)
75
102
}
76
103
77
104
eng, err := engine.New(ctx, cfg, d, &n, vault)
···
120
147
jq.Start()
121
148
defer jq.Stop()
122
149
150
+
// Stop vault token renewal if it implements Stopper
151
+
if stopper, ok := vault.(secrets.Stopper); ok {
152
+
defer stopper.Stop()
153
+
}
154
+
123
155
cursorStore, err := cursor.NewSQLiteStore(cfg.Server.DBPath)
124
156
if err != nil {
125
157
return fmt.Errorf("failed to setup sqlite3 cursor store: %w", err)