~bigbes/sr-ht-dolt

ca9357a0d245b7f8ce3256f9f4524f83925a031e — Eugene Blikh 30 days ago
foundation: module, core domain, schema, spike-verified dolt interop

Phase 0 foundation for dolt.sr.ht (pure-Go Dolt hosting for SourceHut).

Dependency pins:
- core-go v0.0.0-20260520082310-fdb3662452dc: the production instance's
  submodule commit (fdb3662); must never be upgraded so token/config/crypto
  behavior matches the rest of the instance.
- dolthub/dolt/go v0.40.5-0.20260626152440-45335d44ad79: pseudo-version at the
  commit tagged v2.1.10 (45335d44), matching the installed dolt CLI. The /go
  submodule's latest tag (v0.40.4, 2021) does not interop with a modern CLI;
  matching the CLI commit guarantees a common NBS format (Format_DOLT) and
  remotesapi proto.
- gopkg.in/go-jose/go-jose.v2 v2.6.3: same JOSE version dolt's creds package
  signs EdDSA keypair JWTs with, avoiding a duplicate JOSE lib.
- grpc v1.79.3, logrus v1.8.3, lib/pq v1.10.9, chi/v5 v5.3.1, brant v0.5.1.

Contents:
- core/ pure domain: models, ValidateName/ParseRepoPath, the access matrix
  (Allowed + NotFoundForPrivate), full table-driven tests.
- schema.sql + migrations/0001_initial.sql (brant format).
- config.example.ini, contrib/dolt.sr.ht.conf (nginx), Makefile, scss/main.scss,
  static/logo.svg, README, .gitignore.
- internal/smoke: throwaway import-and-build check (deleted in Phase 3).

Spike (storage/spike_test.go, tag `spike`): bare NBS store via WriteEmptyRepo,
served by remotesrv on an ephemeral single port, round-tripped through the real
dolt CLI v2.1.10 (clone -> insert -> commit -> push -> re-clone -> verify rows).
Green, no version juggling needed beyond selecting the v2.1.10 commit. FS must
be rooted at the repos dir (LocalFilesysWithWorkingDir) so sealed chunk-URL
prefixes are clean relatives.

Build note: dolt pulls go-icu-regex (CGO), which needs ICU4C headers; on this
Mac set CGO_CPPFLAGS/CGO_LDFLAGS to icu4c@78 (documented in README).
A  => .gitignore +11 -0
@@ 1,11 @@
# Built binaries
/doltsrht
/doltsrht-migrate

# Built CSS (generated by `make css`)
/static/main.css
/static/main.min.css
/static/main.min.*.css

# macOS
.DS_Store

A  => Makefile +85 -0
@@ 1,85 @@
PREFIX?=/usr/local
BINDIR?=$(PREFIX)/bin
LIBDIR?=$(PREFIX)/lib
SHAREDIR?=$(PREFIX)/share

ASSETS?=/usr/share/sourcehut

SERVICE=dolt.sr.ht
STATICDIR=$(SHAREDIR)/sourcehut/static/$(SERVICE)
MIGRATIONDIR=$(SHAREDIR)/sourcehut/migrations/$(SERVICE)

SASSC?=sassc
SASSC_INCLUDE=-I$(ASSETS)/scss/
MINIFY?=minify

BINARIES=\
	doltsrht \
	doltsrht-migrate

# Default target builds the Go binaries only. CSS (all-share) is deliberately
# kept off the default path because sassc/minify are not always installed on
# dev machines; run `make css` explicitly to build stylesheets.
all: all-bin

all-bin: $(BINARIES)

all-share: static/main.min.css

css: all-share

# Build each binary if its cmd package exists yet. The cmd/ packages land in
# Phase 3; until then these targets are no-ops rather than hard failures.
doltsrht:
	@if [ -d ./cmd/doltsrht ]; then \
		echo "go build -o $@ ./cmd/doltsrht"; \
		go build -o $@ ./cmd/doltsrht; \
	else \
		echo "skip $@: ./cmd/doltsrht not present yet"; \
	fi

doltsrht-migrate:
	@if [ -d ./cmd/doltsrht-migrate ]; then \
		echo "go build -o $@ ./cmd/doltsrht-migrate"; \
		go build -o $@ ./cmd/doltsrht-migrate; \
	else \
		echo "skip $@: ./cmd/doltsrht-migrate not present yet"; \
	fi

# Compile every buildable package; used as the CI build gate.
build:
	go build ./...

install: install-bin install-share

install-bin: all-bin
	mkdir -p $(BINDIR)
	for bin in $(BINARIES); do \
		if [ -x $$bin ]; then install -Dm755 $$bin $(BINDIR)/; fi; \
	done

install-share:
	mkdir -p $(STATICDIR)
	mkdir -p $(MIGRATIONDIR)
	install -Dm644 schema.sql $(SHAREDIR)/sourcehut/$(SERVICE).sql
	install -Dm644 migrations/*.sql $(MIGRATIONDIR)
	if [ -d static ]; then install -Dm644 static/*.css static/*.svg $(STATICDIR) 2>/dev/null || true; fi

clean: clean-bin clean-share

clean-bin:
	rm -f $(BINARIES)

clean-share:
	rm -f static/main.min.css static/main.css static/main.min.*.css

.PHONY: all all-bin all-share css build install install-bin install-share
.PHONY: clean clean-bin clean-share $(BINARIES)

static/main.css: scss/main.scss
	mkdir -p $(@D)
	$(SASSC) $(SASSC_INCLUDE) $< $@

static/main.min.css: static/main.css
	$(MINIFY) -o $@ $<
	cp $@ $(@D)/main.min.$$(sha256sum $@ | cut -c1-8).css

A  => README.md +117 -0
@@ 1,117 @@
# dolt.sr.ht

A self-hosted [Dolt](https://www.dolthub.com/) database hosting service for a
SourceHut instance — "DoltLab for SourceHut". It hosts Dolt databases the way
git.sr.ht hosts git repos: `dolt clone`/`push`/`pull` over HTTPS plus an
integrated web UI that shares the SourceHut nav, unified login, and Bootstrap
theme.

Pure Go, one module (`go.bigb.es/sourcehut-dolt`), no upstream SourceHut
modification — integration is config-driven: a `[dolt.sr.ht]` section in the
shared instance `config.ini` puts the service into every other service's nav.

Storage is bare NBS chunk-store directories (no `.dolt/`, no working set) at
`<repos>/~<user>/<name>` — exactly what `remotesrv` serves and `file://`
remotes use. PostgreSQL holds metadata (a mirror of meta's users, plus
repositories, ACLs, and dolt keys). Clone URL:
`dolt clone https://dolt.srht.bigb.es/~user/db`.

Two auth flows are supported: username + meta personal access token
(`--user` + `DOLT_REMOTE_PASSWORD`, HTTP Basic) and dolt's Ed25519 keypair flow
(`dolt creds` / `dolt login`, Bearer EdDSA JWT — the git-SSH-key-like UX).

## Status

Phase 0 (foundation) only: the pure `core/` domain (name/path validation and
the access matrix), the SQL schema and brant migration, config/nginx/Makefile
scaffolding, and the de-risking storage spike. The service binaries, database
layer, auth, remotesapi, and web UI land in later phases.

## Build prerequisites

- **Go 1.26+**
- **A C toolchain** — `github.com/dolthub/dolt/go` uses CGO for
  [`gozstd`](https://github.com/valyala/gozstd) and
  [`go-icu-regex`](https://github.com/dolthub/go-icu-regex).
- **ICU4C development headers** — required by `go-icu-regex`.
  - Debian/Ubuntu: `apt install libicu-dev` (headers on the default path).
  - macOS (Homebrew): `brew install icu4c` installs a keg-only, versioned
    formula. Point CGO at it, e.g. for `icu4c@78`:

    ```sh
    export CGO_CPPFLAGS="-I/opt/homebrew/opt/icu4c@78/include"
    export CGO_LDFLAGS="-L/opt/homebrew/opt/icu4c@78/lib"
    ```

- **sassc + minify** — only for building CSS (`make css`); not needed for the
  default build:

  ```sh
  brew install sassc                                   # or apt install sassc
  go install github.com/tdewolff/minify/v2/cmd/minify@latest
  ```

  CSS is compiled against the shared sourcehut SCSS: `make css ASSETS=/path/to/sourcehut/scss/parent`
  (`ASSETS` defaults to `/usr/share/sourcehut`; `sassc` is invoked with
  `-I $(ASSETS)/scss`).

## Pinned dependencies (and why)

- **`git.sr.ht/~sircmpwn/core-go` v0.0.0-20260520082310-fdb3662452dc** — this
  exact pseudo-version matches the production instance's `core-go` submodule
  commit (`fdb3662`). **Never upgrade it** (no bare `go get -u`, no `@latest`);
  token validation, config, and crypto must behave identically to the rest of
  the instance.
- **`github.com/dolthub/dolt/go` v0.40.5-0.20260626152440-45335d44ad79** — a
  pseudo-version pinned to the commit tagged **v2.1.10** (`45335d44`), the dolt
  CLI version installed on the target host (`/opt/homebrew/bin/dolt`, v2.1.10).
  The dolt `/go` submodule's latest *tag* is the stale `v0.40.4` (2021), which
  does **not** interop with a modern CLI; matching the CLI's commit guarantees a
  common NBS storage format (`types.Format_DOLT` / `__DOLT__`) and remotesapi
  proto. Verified end-to-end by the Phase-0 spike (see below). If the CLI is
  upgraded, re-pin `dolt/go` to the new CLI's commit and re-run the spike.
- **`gopkg.in/go-jose/go-jose.v2` v2.6.3** — the same JOSE major/version that
  `dolt/go`'s `creds` package uses to sign the EdDSA keypair JWTs, so the Bearer
  verify path stays byte-compatible and no duplicate JOSE lib is pulled in.
- grpc v1.79.3, logrus v1.8.3, lib/pq v1.10.9, chi/v5, and brant round out the
  transport, logging, Postgres driver, HTTP router, and migration tooling.

## The spike

`storage/spike_test.go` (build tag `spike`) is the Phase-0 de-risk gate. It
inits a bare NBS store via `doltdb.LoadDoltDB` + `WriteEmptyRepo`, serves it
with `remotesrv.NewServer` on an ephemeral localhost port (single-port
http+gRPC multiplex, no auth), then drives the real `dolt` CLI through a full
round-trip: clone → create table + insert → commit → push → fresh re-clone →
verify the rows. It skips (does not fail) when the CLI is absent, and uses an
isolated `$HOME` so your real dolt config is untouched.

```sh
export CGO_CPPFLAGS="-I/opt/homebrew/opt/icu4c@78/include"
export CGO_LDFLAGS="-L/opt/homebrew/opt/icu4c@78/lib"
go test -tags spike ./storage/ -run TestSpike -v
```

## Dev commands

```sh
go build ./...                 # build every package (needs the CGO env above)
go test ./core/...             # pure-domain unit tests
go test -tags spike ./storage/ -v   # the interop spike
make                           # build the service binaries (no-ops until Phase 3)
make css                       # build stylesheets (needs sassc + minify)
```

## Deployment sketch

1. Add a `[dolt.sr.ht]` section to the shared instance `config.ini` (see
   `config.example.ini`) plus an `origin=` line so the service appears in every
   other service's nav. This host **must** be inside meta's `internal-ipnet`
   (token validation uses internal auth against meta).
2. Create the database and run migrations: `doltsrht-migrate` applies
   `migrations/*.sql` (brant format). `schema.sql` is the full init DDL.
3. Install the nginx site config (`contrib/dolt.sr.ht.conf`) and add a
   `dolt.srht.bigb.es` DNS record. nginx path-routes the remotesapi gRPC, the
   sealed-URL chunk data plane, the CredentialsService, and the web UI to
   separate listeners.
4. Start `doltsrht`.

A  => config.example.ini +53 -0
@@ 1,53 @@
; dolt.sr.ht configuration.
;
; In production this section is merged into the single shared instance
; config.ini (the same file every *.sr.ht service reads). Only the keys in the
; [dolt.sr.ht] section below are ours; the rest are shared keys owned by other
; services and referenced (NOT duplicated) here — they must already be present
; and consistent across the instance.
;
; Deployment prerequisite: this host must be inside meta's [meta.sr.ht::api]
; internal-ipnet, because token validation (FetchMetaProfile +
; LookupTokenRevocation) uses internal authentication against meta.

[dolt.sr.ht]
;
; The URL dolt.sr.ht is served at (protocol://domain). This is also the JWT
; audience the Bearer (dolt creds) auth flow checks against, so it must match
; the host clients pass to `dolt login --auth-endpoint`.
origin=https://dolt.srht.bigb.es
;
; PostgreSQL connection string for the dolt.sr.ht metadata database (users
; mirror, repositories, ACLs, dolt keys).
connection-string=postgresql://doltsrht@localhost/dolt.sr.ht?sslmode=disable
;
; Root directory holding the bare NBS chunk-store dirs, one per database at
; <repos>/~<user>/<name>. This is what remotesrv serves and file:// remotes use.
repos=/var/lib/dolt
;
; Address for the remotesapi listener (gRPC ChunkStoreService + HTTP chunk data
; plane, h2c-multiplexed on one port). nginx grpc_pass/proxy_pass targets this.
remotesapi-listen=127.0.0.1:5306
;
; Address for the small CredentialsService.WhoAmI gRPC listener used by the
; `dolt login` keypair flow. nginx path-routes the CredentialsService here.
credsapi-listen=127.0.0.1:5308
;
; Directory containing the built static assets (main.min.<sha>.css, logo.svg).
static-dir=/usr/share/sourcehut/dolt.sr.ht/static
;
; Set to "yes" to run brant migrations automatically on package upgrade.
migrate-on-upgrade=yes

; ---------------------------------------------------------------------------
; Shared keys reused in place (owned by other services, listed for reference;
; do not duplicate their values here — they live in the shared config.ini):
;
;   [sr.ht] network-key      shared secret for internal service auth
;   [sr.ht] site-name        used to render the shared nav brand
;   [sr.ht] environment      non-"production" adds a banner to every page
;   [sr.ht] owner-name / owner-email   author of the initial empty commit
;   [webhooks] private-key    derives the offline Bearer-token HMAC key
;   [meta.sr.ht] origin       meta's URL (login redirects, profile fetch)
;   [meta.sr.ht::api] internal-ipnet   subnets allowed to use internal auth
; ---------------------------------------------------------------------------

A  => contrib/dolt.sr.ht.conf +64 -0
@@ 1,64 @@
# nginx site config for dolt.sr.ht, in the sr.ht-nginx style. Drop into the
# nginx sites dir alongside the other *.sr.ht.conf files; TLS/http2 and the
# shared proxy headers come from the included snippets (sourcehut.conf,
# port443.conf, port80.conf).
#
# Three back-end listeners are multiplexed by path:
#   - the remotesapi gRPC ChunkStoreService and its sealed-URL chunk data plane
#     -> 127.0.0.1:5306
#   - the CredentialsService (dolt login / keypair WhoAmI) -> 127.0.0.1:5308
#   - everything else (web UI + /static) -> 127.0.0.1:5307

server {
	include sourcehut.conf;
	include port80.conf;
	server_name dolt.srht.bigb.es;
}

server {
	include sourcehut.conf;
	include port443.conf;
	server_name dolt.srht.bigb.es;

	# dolt push can stream arbitrarily large table files.
	client_max_body_size 0;

	# remotesapi control plane: gRPC ChunkStoreService (clone/pull/push RPCs).
	# X-Forwarded-Proto must be set so the server hands back https:// sealed
	# chunk URLs (getScheme honors this header).
	location /dolt.services.remotesapi.v1alpha1.ChunkStoreService/ {
		grpc_pass grpc://127.0.0.1:5306;
		grpc_set_header X-Forwarded-Proto https;
		grpc_read_timeout 600s;
		grpc_send_timeout 600s;
		client_max_body_size 0;
	}

	# CredentialsService.WhoAmI: the `dolt login` keypair association flow.
	location /dolt.services.remotesapi.v1alpha1.CredentialsService/ {
		grpc_pass grpc://127.0.0.1:5308;
		grpc_set_header X-Forwarded-Proto https;
	}

	# Chunk data plane: AES-GCM sealed URLs (possession == authorization),
	# 15-minute expiry. GET on pull, PUT on push. No extra auth needed.
	location /single_symmetric_key_sealed_request/ {
		proxy_pass http://127.0.0.1:5306;
		client_max_body_size 0;
		proxy_request_buffering off;
		proxy_read_timeout 600s;
		proxy_send_timeout 600s;
	}

	# Web UI and /static.
	location / {
		proxy_pass http://127.0.0.1:5307;
		include headers.conf;
		include web.conf;
	}

	location /static {
		root /usr/share/sourcehut;
		expires 30d;
	}
}

A  => core/access.go +85 -0
@@ 1,85 @@
package core

// Allowed decides whether caller may perform op on repo, given the caller's
// resolved per-repo ACL grant (aclMode, nil if the caller has no ACL entry).
// It implements the dolt.sr.ht access matrix exactly:
//
//	Caller \ Visibility | PUBLIC        | UNLISTED      | PRIVATE
//	--------------------|---------------|---------------|----------------------
//	anon / any user     | browse, clone | browse, clone | —
//	ACL RO              | browse, clone | browse, clone | browse, clone
//	ACL RW              | + push        | + push        | browse, clone, push
//	owner               | all           | all           | all
//
// Rules:
//   - The owner may do anything (subject to the suspension rule below).
//   - An ACL RO grant permits browse and clone on any visibility, including
//     PRIVATE.
//   - An ACL RW grant additionally permits push (but never admin).
//   - With no ACL and non-owner: PUBLIC and UNLISTED permit browse and clone
//     for everyone (including anonymous); PRIVATE permits nothing.
//   - A suspended caller may read (browse, clone) but never push or admin,
//     regardless of ownership or ACL.
//   - Any operation outside the four known Ops is denied.
//
// Access control fails closed: a nil repo denies everything.
func Allowed(caller *Caller, repo *Repo, aclMode *AccessMode, op Op) bool {
	switch op {
	case OpBrowse, OpCloneRead, OpPush, OpAdmin:
		// known op
	default:
		return false
	}
	if repo == nil {
		return false
	}

	suspended := caller != nil && caller.Suspended
	isWrite := op == OpPush || op == OpAdmin
	if suspended && isWrite {
		return false
	}

	// Owner may do anything (write already gated by the suspension check).
	if caller != nil && caller.UserID == repo.OwnerID {
		return true
	}

	// Explicit ACL grants (only meaningful for authenticated callers).
	if caller != nil && aclMode != nil {
		switch *aclMode {
		case AccessRO:
			return op == OpBrowse || op == OpCloneRead
		case AccessRW:
			// RO reads plus push; never admin.
			return op == OpBrowse || op == OpCloneRead || op == OpPush
		}
	}

	// No ownership, no ACL: fall back to visibility. Only reads are ever
	// granted this way.
	switch repo.Visibility {
	case VisibilityPublic, VisibilityUnlisted:
		return op == OpBrowse || op == OpCloneRead
	default: // VisibilityPrivate and anything unrecognized
		return false
	}
}

// NotFoundForPrivate reports whether a denied request for repo should be
// surfaced as "not found" (404) rather than "forbidden" (403), so that the
// existence of PRIVATE repositories is not leaked. Consult it only after
// Allowed has already returned false for the request.
//
// A PRIVATE repo that the caller cannot even browse is reported as not found;
// PUBLIC/UNLISTED repos, and PRIVATE repos the caller may browse, reveal their
// existence normally (a plain 403). A nil repo is always "not found".
func NotFoundForPrivate(caller *Caller, repo *Repo, aclMode *AccessMode) bool {
	if repo == nil {
		return true
	}
	if repo.Visibility != VisibilityPrivate {
		return false
	}
	return !Allowed(caller, repo, aclMode, OpBrowse)
}

A  => core/access_test.go +142 -0
@@ 1,142 @@
package core

import "testing"

const (
	ownerID = 1
	otherID = 2
)

func repoWith(v Visibility) *Repo {
	return &Repo{ID: 10, Name: "db", OwnerID: ownerID, OwnerName: "owner", Path: "/x", Visibility: v}
}

func ptr(m AccessMode) *AccessMode { return &m }

// grant bundles the four operation outcomes for one matrix cell.
type grant struct{ browse, clone, push, admin bool }

func (g grant) want(op Op) bool {
	switch op {
	case OpBrowse:
		return g.browse
	case OpCloneRead:
		return g.clone
	case OpPush:
		return g.push
	case OpAdmin:
		return g.admin
	}
	return false
}

func TestAllowedMatrix(t *testing.T) {
	anon := (*Caller)(nil)
	user := &Caller{UserID: otherID, Username: "user", UserType: UserTypeUser}
	owner := &Caller{UserID: ownerID, Username: "owner", UserType: UserTypeUser}
	suspendedOwner := &Caller{UserID: ownerID, Username: "owner", UserType: UserTypeSuspended, Suspended: true}
	suspendedUser := &Caller{UserID: otherID, Username: "user", UserType: UserTypeSuspended, Suspended: true}

	readOnly := grant{browse: true, clone: true}
	readWrite := grant{browse: true, clone: true, push: true}
	all := grant{browse: true, clone: true, push: true, admin: true}
	none := grant{}

	tests := []struct {
		name   string
		caller *Caller
		repo   *Repo
		acl    *AccessMode
		want   grant
	}{
		// anonymous, no ACL
		{"anon/public", anon, repoWith(VisibilityPublic), nil, readOnly},
		{"anon/unlisted", anon, repoWith(VisibilityUnlisted), nil, readOnly},
		{"anon/private", anon, repoWith(VisibilityPrivate), nil, none},

		// authenticated non-owner, no ACL (same as anonymous)
		{"user/public", user, repoWith(VisibilityPublic), nil, readOnly},
		{"user/unlisted", user, repoWith(VisibilityUnlisted), nil, readOnly},
		{"user/private", user, repoWith(VisibilityPrivate), nil, none},

		// ACL RO grant: browse+clone on every visibility, incl. PRIVATE
		{"acl-ro/public", user, repoWith(VisibilityPublic), ptr(AccessRO), readOnly},
		{"acl-ro/unlisted", user, repoWith(VisibilityUnlisted), ptr(AccessRO), readOnly},
		{"acl-ro/private", user, repoWith(VisibilityPrivate), ptr(AccessRO), readOnly},

		// ACL RW grant: + push, never admin, on every visibility
		{"acl-rw/public", user, repoWith(VisibilityPublic), ptr(AccessRW), readWrite},
		{"acl-rw/unlisted", user, repoWith(VisibilityUnlisted), ptr(AccessRW), readWrite},
		{"acl-rw/private", user, repoWith(VisibilityPrivate), ptr(AccessRW), readWrite},

		// owner: everything on every visibility
		{"owner/public", owner, repoWith(VisibilityPublic), nil, all},
		{"owner/unlisted", owner, repoWith(VisibilityUnlisted), nil, all},
		{"owner/private", owner, repoWith(VisibilityPrivate), nil, all},

		// suspended: reads only, never push/admin
		{"suspended-owner/private", suspendedOwner, repoWith(VisibilityPrivate), nil, readOnly},
		{"suspended-owner/public", suspendedOwner, repoWith(VisibilityPublic), nil, readOnly},
		{"suspended-user-rw/private", suspendedUser, repoWith(VisibilityPrivate), ptr(AccessRW), readOnly},
		{"suspended-user-rw/public", suspendedUser, repoWith(VisibilityPublic), ptr(AccessRW), readOnly},
		{"suspended-user-ro/private", suspendedUser, repoWith(VisibilityPrivate), ptr(AccessRO), readOnly},
	}

	ops := []Op{OpBrowse, OpCloneRead, OpPush, OpAdmin}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			for _, op := range ops {
				got := Allowed(tt.caller, tt.repo, tt.acl, op)
				want := tt.want.want(op)
				if got != want {
					t.Errorf("Allowed(%s, %s) = %v, want %v", tt.name, op, got, want)
				}
			}
		})
	}
}

func TestAllowedUnknownOpDenied(t *testing.T) {
	owner := &Caller{UserID: ownerID}
	if Allowed(owner, repoWith(VisibilityPublic), nil, Op(99)) {
		t.Fatal("unknown op must be denied even for the owner")
	}
}

func TestAllowedNilRepoDenied(t *testing.T) {
	owner := &Caller{UserID: ownerID}
	for _, op := range []Op{OpBrowse, OpCloneRead, OpPush, OpAdmin} {
		if Allowed(owner, nil, nil, op) {
			t.Fatalf("nil repo must deny %s", op)
		}
	}
}

func TestNotFoundForPrivate(t *testing.T) {
	anon := (*Caller)(nil)
	user := &Caller{UserID: otherID}
	owner := &Caller{UserID: ownerID}

	tests := []struct {
		name   string
		caller *Caller
		repo   *Repo
		acl    *AccessMode
		want   bool
	}{
		{"nil-repo", anon, nil, nil, true},
		{"public-anon", anon, repoWith(VisibilityPublic), nil, false},
		{"unlisted-anon", anon, repoWith(VisibilityUnlisted), nil, false},
		{"private-anon-hidden", anon, repoWith(VisibilityPrivate), nil, true},
		{"private-unauthorized-user-hidden", user, repoWith(VisibilityPrivate), nil, true},
		{"private-owner-visible", owner, repoWith(VisibilityPrivate), nil, false},
		{"private-acl-ro-visible", user, repoWith(VisibilityPrivate), ptr(AccessRO), false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := NotFoundForPrivate(tt.caller, tt.repo, tt.acl); got != tt.want {
				t.Fatalf("NotFoundForPrivate(%s) = %v, want %v", tt.name, got, tt.want)
			}
		})
	}
}

A  => core/models.go +85 -0
@@ 1,85 @@
// Package core holds the pure domain model for dolt.sr.ht: value types,
// name/path validation, and the access-control matrix. It performs no I/O and
// imports nothing outside the standard library, so every other package can
// depend on it freely.
package core

// Visibility mirrors the Postgres `visibility` enum.
type Visibility string

const (
	VisibilityPublic   Visibility = "PUBLIC"
	VisibilityUnlisted Visibility = "UNLISTED"
	VisibilityPrivate  Visibility = "PRIVATE"
)

// AccessMode mirrors the Postgres `access_mode` enum: an ACL grant is either
// read-only (browse + clone) or read-write (+ push).
type AccessMode string

const (
	AccessRO AccessMode = "RO"
	AccessRW AccessMode = "RW"
)

// UserType mirrors the Postgres `user_type` enum, itself a mirror of meta's
// user type. SUSPENDED users may read but never write.
type UserType string

const (
	UserTypePending   UserType = "PENDING"
	UserTypeUser      UserType = "USER"
	UserTypeAdmin     UserType = "ADMIN"
	UserTypeSuspended UserType = "SUSPENDED"
)

// Op is an operation whose authorization is decided by Allowed.
type Op int

const (
	// OpBrowse is a read of repository metadata/contents through the web UI.
	OpBrowse Op = iota
	// OpCloneRead is a read over the remotesapi (clone/pull/fetch).
	OpCloneRead
	// OpPush is a write over the remotesapi (push).
	OpPush
	// OpAdmin is an owner-only administrative action (settings, ACLs, delete).
	OpAdmin
)

func (o Op) String() string {
	switch o {
	case OpBrowse:
		return "browse"
	case OpCloneRead:
		return "clone-read"
	case OpPush:
		return "push"
	case OpAdmin:
		return "admin"
	default:
		return "unknown"
	}
}

// Repo is a hosted Dolt database. Path is the absolute on-disk NBS store dir.
type Repo struct {
	ID          int
	Name        string
	Description string
	OwnerID     int
	OwnerName   string
	Path        string
	Visibility  Visibility
}

// Caller is the authenticated principal for a request. A nil *Caller is an
// anonymous request. Suspended is carried explicitly (rather than derived from
// UserType) because the token/cookie resolver decides it, and it gates all
// write operations regardless of ACL.
type Caller struct {
	UserID    int
	Username  string
	UserType  UserType
	Suspended bool
}

A  => core/names.go +68 -0
@@ 1,68 @@
package core

import (
	"fmt"
	"regexp"
	"strings"
)

// MaxNameLen is the maximum length of a database name. Names double as SQL
// database identifiers, so they are kept short and conservative.
const MaxNameLen = 64

// nameRe matches a valid database (or owner) name: an alphanumeric first and
// last character, with alphanumerics, hyphens and underscores in between. It
// structurally forbids "." and ".." (no dots at all) and leading/trailing
// separators.
var nameRe = regexp.MustCompile(`^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$`)

// ValidateName reports whether name is an acceptable database name. It rejects
// the empty string, names longer than MaxNameLen, the traversal names "." and
// ".." explicitly, and anything not matching nameRe.
func ValidateName(name string) error {
	if name == "" {
		return fmt.Errorf("name must not be empty")
	}
	if name == "." || name == ".." {
		return fmt.Errorf("name %q is not allowed", name)
	}
	if len(name) > MaxNameLen {
		return fmt.Errorf("name is too long (%d > %d)", len(name), MaxNameLen)
	}
	if !nameRe.MatchString(name) {
		return fmt.Errorf("name %q must match %s", name, nameRe.String())
	}
	return nil
}

// ParseRepoPath splits a repository path into its owner and database segments.
// It accepts "~user/db", "user/db", and leading/trailing slashes. It rejects
// empty segments, ".."/"." traversal segments, and any path that does not have
// exactly two segments. The returned owner never carries a leading "~".
//
// ParseRepoPath validates structure only; owner and db are not name-validated
// here (callers that touch disk or SQL must additionally run ValidateName).
func ParseRepoPath(path string) (owner, db string, err error) {
	trimmed := strings.Trim(path, "/")
	if trimmed == "" {
		return "", "", fmt.Errorf("empty repo path")
	}
	segs := strings.Split(trimmed, "/")
	if len(segs) != 2 {
		return "", "", fmt.Errorf("repo path %q must have exactly 2 segments, got %d", path, len(segs))
	}
	for _, s := range segs {
		if s == "" {
			return "", "", fmt.Errorf("repo path %q has an empty segment", path)
		}
		if s == "." || s == ".." {
			return "", "", fmt.Errorf("repo path %q contains a traversal segment %q", path, s)
		}
	}
	owner = strings.TrimPrefix(segs[0], "~")
	db = segs[1]
	if owner == "" {
		return "", "", fmt.Errorf("repo path %q has an empty owner", path)
	}
	return owner, db, nil
}

A  => core/names_test.go +93 -0
@@ 1,93 @@
package core

import (
	"strings"
	"testing"
)

func TestValidateName(t *testing.T) {
	tests := []struct {
		name  string
		input string
		ok    bool
	}{
		{"simple", "demo", true},
		{"alnum", "db123", true},
		{"with-hyphen", "my-db", true},
		{"with-underscore", "my_db", true},
		{"mixed", "a-b_c-1", true},
		{"single-char", "a", true},
		{"single-digit", "7", true},
		{"max-length", strings.Repeat("a", MaxNameLen), true},

		{"empty", "", false},
		{"dot", ".", false},
		{"dotdot", "..", false},
		{"embedded-dot", "a.b", false},
		{"leading-hyphen", "-db", false},
		{"trailing-hyphen", "db-", false},
		{"leading-underscore", "_db", false},
		{"trailing-underscore", "db_", false},
		{"slash", "a/b", false},
		{"space", "a b", false},
		{"tilde", "~db", false},
		{"too-long", strings.Repeat("a", MaxNameLen+1), false},
		{"unicode", "café", false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateName(tt.input)
			if tt.ok && err != nil {
				t.Fatalf("ValidateName(%q) = %v, want nil", tt.input, err)
			}
			if !tt.ok && err == nil {
				t.Fatalf("ValidateName(%q) = nil, want error", tt.input)
			}
		})
	}
}

func TestParseRepoPath(t *testing.T) {
	tests := []struct {
		name      string
		input     string
		wantOwner string
		wantDB    string
		wantErr   bool
	}{
		{"tilde", "~user/db", "user", "db", false},
		{"no-tilde", "user/db", "user", "db", false},
		{"leading-slash", "/~user/db", "user", "db", false},
		{"trailing-slash", "~user/db/", "user", "db", false},
		{"both-slashes", "/~user/db/", "user", "db", false},
		{"names-with-symbols", "~my-user/my_db-1", "my-user", "my_db-1", false},

		{"empty", "", "", "", true},
		{"only-slashes", "///", "", "", true},
		{"one-segment", "~user", "", "", true},
		{"three-segments", "~user/db/extra", "", "", true},
		{"empty-inner-segment", "~user//db", "", "", true},
		{"traversal-dotdot", "~user/..", "", "", true},
		{"traversal-owner-dotdot", "../db", "", "", true},
		{"traversal-dot", "~user/.", "", "", true},
		{"tilde-only-owner", "~/db", "", "", true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			owner, db, err := ParseRepoPath(tt.input)
			if tt.wantErr {
				if err == nil {
					t.Fatalf("ParseRepoPath(%q) = (%q,%q,nil), want error", tt.input, owner, db)
				}
				return
			}
			if err != nil {
				t.Fatalf("ParseRepoPath(%q) unexpected error: %v", tt.input, err)
			}
			if owner != tt.wantOwner || db != tt.wantDB {
				t.Fatalf("ParseRepoPath(%q) = (%q,%q), want (%q,%q)",
					tt.input, owner, db, tt.wantOwner, tt.wantDB)
			}
		})
	}
}

A  => go.mod +160 -0
@@ 1,160 @@
module go.bigb.es/sourcehut-dolt

go 1.26.4

require (
	git.sr.ht/~bitfehler/brant v0.5.1
	git.sr.ht/~sircmpwn/core-go v0.0.0-20260520082310-fdb3662452dc
	github.com/dolthub/dolt/go v0.40.5-0.20260626152440-45335d44ad79
	github.com/go-chi/chi/v5 v5.3.1
	github.com/lib/pq v1.10.9
	github.com/sirupsen/logrus v1.8.3
	google.golang.org/grpc v1.79.3
	gopkg.in/go-jose/go-jose.v2 v2.6.3
)

require (
	cel.dev/expr v0.25.1 // indirect
	cloud.google.com/go v0.120.0 // indirect
	cloud.google.com/go/auth v0.16.2 // indirect
	cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
	cloud.google.com/go/compute/metadata v0.9.0 // indirect
	cloud.google.com/go/iam v1.5.2 // indirect
	cloud.google.com/go/monitoring v1.24.2 // indirect
	cloud.google.com/go/storage v1.50.0 // indirect
	git.sr.ht/~sircmpwn/dowork v0.0.0-20241216125407-2b00aa42322c // indirect
	git.sr.ht/~sircmpwn/getopt v1.0.0 // indirect
	git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9 // indirect
	github.com/99designs/gqlgen v0.17.36 // indirect
	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 // indirect
	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect
	github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
	github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 // indirect
	github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
	github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect
	github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 // indirect
	github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 // indirect
	github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
	github.com/Masterminds/squirrel v1.5.4 // indirect
	github.com/ProtonMail/go-crypto v1.3.0 // indirect
	github.com/agnivade/levenshtein v1.1.1 // indirect
	github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible // indirect
	github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
	github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
	github.com/aws/aws-sdk-go-v2/config v1.29.8 // indirect
	github.com/aws/aws-sdk-go-v2/credentials v1.18.2 // indirect
	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.1 // indirect
	github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.64 // indirect
	github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
	github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
	github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect
	github.com/aws/aws-sdk-go-v2/service/dynamodb v1.41.0 // indirect
	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
	github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect
	github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.15 // indirect
	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect
	github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect
	github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 // indirect
	github.com/aws/aws-sdk-go-v2/service/sso v1.26.1 // indirect
	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.31.1 // indirect
	github.com/aws/aws-sdk-go-v2/service/sts v1.35.1 // indirect
	github.com/aws/smithy-go v1.24.2 // indirect
	github.com/beorn7/perks v1.0.1 // indirect
	github.com/cenkalti/backoff/v4 v4.1.3 // indirect
	github.com/cespare/xxhash/v2 v2.3.0 // indirect
	github.com/cloudflare/circl v1.6.0 // indirect
	github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
	github.com/cockroachdb/apd/v3 v3.2.3 // indirect
	github.com/denisbrodbeck/machineid v1.0.1 // indirect
	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
	github.com/dolthub/aws-sdk-go-ini-parser v0.0.0-20250305001723-2821c37f6c12 // indirect
	github.com/dolthub/eventsapi_schema v0.0.0-20260310172945-37a9265ade69 // indirect
	github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 // indirect
	github.com/dolthub/fslock v0.0.5 // indirect
	github.com/dolthub/go-icu-regex v0.0.0-20260610153742-72563bc7ca83 // indirect
	github.com/dolthub/go-mysql-server v0.20.1-0.20260625171506-68aec8237480 // indirect
	github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63 // indirect
	github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 // indirect
	github.com/dolthub/vitess v0.0.0-20260624214226-81d034e0fde8 // indirect
	github.com/dustin/go-humanize v1.0.1 // indirect
	github.com/edsrzf/mmap-go v1.2.0 // indirect
	github.com/emersion/go-message v0.18.2 // indirect
	github.com/emersion/go-pgpmail v0.2.2 // indirect
	github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
	github.com/emersion/go-smtp v0.21.3 // indirect
	github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
	github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
	github.com/esote/minmaxheap v1.0.0 // indirect
	github.com/fatih/color v1.13.0 // indirect
	github.com/felixge/httpsnoop v1.0.4 // indirect
	github.com/fernet/fernet-go v0.0.0-20211208181803-9f70042a33ee // indirect
	github.com/go-chi/cors v1.2.2 // indirect
	github.com/go-jose/go-jose/v4 v4.1.4 // indirect
	github.com/go-logr/logr v1.4.3 // indirect
	github.com/go-logr/stdr v1.2.2 // indirect
	github.com/go-redis/redis/v8 v8.11.5 // indirect
	github.com/goccy/go-json v0.10.2 // indirect
	github.com/gofrs/flock v0.8.1 // indirect
	github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
	github.com/golang/snappy v0.0.4 // indirect
	github.com/google/btree v1.1.2 // indirect
	github.com/google/s2a-go v0.1.9 // indirect
	github.com/google/uuid v1.6.0 // indirect
	github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
	github.com/googleapis/gax-go/v2 v2.14.2 // indirect
	github.com/gorilla/websocket v1.5.0 // indirect
	github.com/hashicorp/golang-lru v0.5.4 // indirect
	github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
	github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d // indirect
	github.com/kavu/go_reuseport v1.5.0 // indirect
	github.com/kch42/buzhash v0.0.0-20160816060738-9bdec3dec7c6 // indirect
	github.com/kylelemons/godebug v1.1.0 // indirect
	github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
	github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
	github.com/lestrrat-go/strftime v1.2.0 // indirect
	github.com/mattn/go-colorable v0.1.13 // indirect
	github.com/mattn/go-isatty v0.0.20 // indirect
	github.com/mfridman/interpolate v0.0.2 // indirect
	github.com/mitchellh/mapstructure v1.5.0 // indirect
	github.com/mohae/uvarint v0.0.0-20160208145430-c3f9e62bf2b0 // indirect
	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
	github.com/oracle/oci-go-sdk/v65 v65.55.0 // indirect
	github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
	github.com/pkg/errors v0.9.1 // indirect
	github.com/prometheus/client_golang v1.23.2 // indirect
	github.com/prometheus/client_model v0.6.2 // indirect
	github.com/prometheus/common v0.66.1 // indirect
	github.com/sony/gobreaker v0.5.0 // indirect
	github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
	github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec // indirect
	github.com/vektah/gqlparser/v2 v2.5.8 // indirect
	github.com/xtaci/smux v1.5.56 // indirect
	github.com/zeebo/xxh3 v1.0.2 // indirect
	go.opentelemetry.io/auto/sdk v1.2.1 // indirect
	go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect
	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
	go.opentelemetry.io/otel v1.43.0 // indirect
	go.opentelemetry.io/otel/metric v1.43.0 // indirect
	go.opentelemetry.io/otel/sdk v1.43.0 // indirect
	go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect
	go.opentelemetry.io/otel/trace v1.43.0 // indirect
	go.uber.org/multierr v1.10.0 // indirect
	go.uber.org/zap v1.27.0 // indirect
	go.yaml.in/yaml/v2 v2.4.2 // indirect
	golang.org/x/crypto v0.51.0 // indirect
	golang.org/x/net v0.54.0 // indirect
	golang.org/x/oauth2 v0.34.0 // indirect
	golang.org/x/sync v0.20.0 // indirect
	golang.org/x/sys v0.45.0 // indirect
	golang.org/x/text v0.37.0 // indirect
	golang.org/x/time v0.12.0 // indirect
	golang.org/x/tools v0.45.0 // indirect
	google.golang.org/api v0.241.0 // indirect
	google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect
	google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
	google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
	google.golang.org/protobuf v1.36.11 // indirect
	gopkg.in/src-d/go-errors.v1 v1.0.0 // indirect
)

A  => go.sum +515 -0
@@ 1,515 @@
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=
cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q=
cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM=
cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U=
cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6QJs=
cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
git.sr.ht/~bitfehler/brant v0.5.1 h1:emUbT5r1P0p2mgC/52NMAzGa2zw2To/Bim5ySeyGrks=
git.sr.ht/~bitfehler/brant v0.5.1/go.mod h1:XD6RmtKTOT/RL+2iQ+KBcPhLYAjeJWdLj3yfM5+7k/g=
git.sr.ht/~sircmpwn/core-go v0.0.0-20260520082310-fdb3662452dc h1:txQeGOaDKWVsAbnMd8CziM9GxbFn+6ztolA+sNE22T4=
git.sr.ht/~sircmpwn/core-go v0.0.0-20260520082310-fdb3662452dc/go.mod h1:JmathMemB+hBk1IgrMn6RuBGusd+fTg1s/X6rNVKXXU=
git.sr.ht/~sircmpwn/core-go v0.0.0-20260708091830-71b27871dc30 h1:Bh43WLyYzPHKKEsPeTNZTGGZP27TCmuuDUa+K7qVbp4=
git.sr.ht/~sircmpwn/core-go v0.0.0-20260708091830-71b27871dc30/go.mod h1:JmathMemB+hBk1IgrMn6RuBGusd+fTg1s/X6rNVKXXU=
git.sr.ht/~sircmpwn/dowork v0.0.0-20241216125407-2b00aa42322c h1:v2opuaN0C5ZpuCifRNR9ZQ8V9IG+Ja80otK1MFj5RnI=
git.sr.ht/~sircmpwn/dowork v0.0.0-20241216125407-2b00aa42322c/go.mod h1:8neHEO3503w/rNtttnR0JFpQgM/GFhaafVwvkPsFIDw=
git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw=
git.sr.ht/~sircmpwn/getopt v1.0.0 h1:/pRHjO6/OCbBF4puqD98n6xtPEgE//oq5U8NXjP7ROc=
git.sr.ht/~sircmpwn/getopt v1.0.0/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw=
git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9 h1:Ahny8Ud1LjVMMAlt8utUFKhhxJtwBAualvsbc/Sk7cE=
git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9/go.mod h1:BVJwbDfVjCjoFiKrhkei6NdGcZYpkDkdyCdg1ukytRA=
github.com/99designs/gqlgen v0.17.36 h1:u/o/rv2SZ9s5280dyUOOrkpIIkr/7kITMXYD3rkJ9go=
github.com/99designs/gqlgen v0.17.36/go.mod h1:6RdyY8puhCoWAQVr2qzF2OMVfudQzc8ACxzpzluoQm4=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 h1:jWQK1GI+LeGGUKBADtcH2rRqPxYB1Ljwms5gFA2LqrM=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4/go.mod h1:8mwH4klAm9DUgR2EEHyEEAQlRDvLPyg5fQry3y+cDew=
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 h1:5IT7xOdq17MtcdtL/vtl6mGfzhaq4m4vpollPRmlsBQ=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0/go.mod h1:ZV4VOm0/eHR06JLrXWe09068dHpr3TRpY9Uo7T+anuA=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 h1:ig/FpDD2JofP/NExKQUbn7uOSZzJAQqogfqluZK4ed4=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0=
github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible h1:QoRMR0TCctLDqBCMyOu1eXdZyMw3F7uGA9qPn2J4+R8=
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY=
github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
github.com/aws/aws-sdk-go-v2/config v1.29.8 h1:RpwAfYcV2lr/yRc4lWhUM9JRPQqKgKWmou3LV7UfWP4=
github.com/aws/aws-sdk-go-v2/config v1.29.8/go.mod h1:t+G7Fq1OcO8cXTPPXzxQSnj/5Xzdc9jAAD3Xrn9/Mgo=
github.com/aws/aws-sdk-go-v2/credentials v1.18.2 h1:mfm0GKY/PHLhs7KO0sUaOtFnIQ15Qqxt+wXbO/5fIfs=
github.com/aws/aws-sdk-go-v2/credentials v1.18.2/go.mod h1:v0SdJX6ayPeZFQxgXUKw5RhLpAoZUuynxWDfh8+Eknc=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.1 h1:owmNBboeA0kHKDcdF8KiSXmrIuXZustfMGGytv6OMkM=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.1/go.mod h1:Bg1miN59SGxrZqlP8vJZSmXW+1N8Y1MjQDq1OfuNod8=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.64 h1:RTko0AQ0i1vWXDM97DkuW6zskgOxFxm4RqC0kmBJFkE=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.64/go.mod h1:ty968MpOa5CoQ/ALWNB8Gmfoehof2nRHDR/DZDPfimE=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22/go.mod h1:zd/JsJ4P7oGfUhXn1VyLqaRZwPmZwg44Jf2dS84Dm3Y=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.41.0 h1:kSMAk72LZ5eIdY/W+tVV6VdokciajcDdVClEBVNWNP0=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.41.0/go.mod h1:yYaWRnVSPyAmexW5t7G3TcuYoalYfT+xQwzWsvtUQ7M=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13/go.mod h1:CEuVn5WqOMilYl+tbccq8+N2ieCy0gVn3OtRb0vBNNM=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.15 h1:M1R1rud7HzDrfCdlBQ7NjnRsDNEhXO/vGhuD189Ggmk=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.15/go.mod h1:uvFKBSq9yMPV4LGAi7N4awn4tLY+hKE35f8THes2mzQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 h1:ZlvrNcHSFFWURB8avufQq9gFsheUgjVD9536obIknfM=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21/go.mod h1:cv3TNhVrssKR0O/xxLJVRfd2oazSnZnkUeTf6ctUwfQ=
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 h1:HwxWTbTrIHm5qY+CAEur0s/figc3qwvLWsNkF4RPToo=
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3/go.mod h1:uoA43SdFwacedBfSgfFSjjCvYe8aYBS7EnU5GZ/YKMM=
github.com/aws/aws-sdk-go-v2/service/sso v1.26.1 h1:uWaz3DoNK9MNhm7i6UGxqufwu3BEuJZm72WlpGwyVtY=
github.com/aws/aws-sdk-go-v2/service/sso v1.26.1/go.mod h1:ILpVNjL0BO+Z3Mm0SbEeUoYS9e0eJWV1BxNppp0fcb8=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.31.1 h1:XdG6/o1/ZDmn3wJU5SRAejHaWgKS4zHv0jBamuKuS2k=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.31.1/go.mod h1:oiotGTKadCOCl3vg/tYh4k45JlDF81Ka8rdumNhEnIQ=
github.com/aws/aws-sdk-go-v2/service/sts v1.35.1 h1:iF4Xxkc0H9c/K2dS0zZw3SCkj0Z7n6AMnUiiyoJND+I=
github.com/aws/aws-sdk-go-v2/service/sts v1.35.1/go.mod h1:0bxIatfN0aLq4mjoLDeBpOjOke68OsFlXPDFJ7V0MYw=
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
github.com/cockroachdb/apd/v3 v3.2.3 h1:4Zx+I3R35bFXMnltzmjP79i2cravE4jTRL6ps9Aux80=
github.com/cockroachdb/apd/v3 v3.2.3/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/dolthub/aws-sdk-go-ini-parser v0.0.0-20250305001723-2821c37f6c12 h1:IdqX7J8vi/Kn3T3Ee0VzqnLqwFmgA2hr8WZETPcQjfM=
github.com/dolthub/aws-sdk-go-ini-parser v0.0.0-20250305001723-2821c37f6c12/go.mod h1:rN7X8BHwkjPcfMQQ2QTAq/xM3leUSGLfb+1Js7Y6TVo=
github.com/dolthub/dolt/go v0.40.4 h1:9C6pjDN3G9YYSRcwG+Do9EiyiFIZVJMrnF4XWFy1EN4=
github.com/dolthub/dolt/go v0.40.4/go.mod h1:Tw9XAsW9VgEO+8h0qqXli6GAbxFvDoO3uDl61m0ldso=
github.com/dolthub/dolt/go v0.40.5-0.20260626152440-45335d44ad79 h1:jglYHtNhgm90ePaU8JQGREbI8oZXuMuZnocUF8047LI=
github.com/dolthub/dolt/go v0.40.5-0.20260626152440-45335d44ad79/go.mod h1:Pls391pPY0J/AojRBhmZchPRdQ9Ut4QU9x6p0bZ9/X4=
github.com/dolthub/eventsapi_schema v0.0.0-20260310172945-37a9265ade69 h1:JShhbqMw26nKx3pqqu/cFxOpzBkN+4elVhzuUfgDw2k=
github.com/dolthub/eventsapi_schema v0.0.0-20260310172945-37a9265ade69/go.mod h1:SSLraQS/jGLYFgff3vuZ+JbVUct6vyEeMzjLBqWqoyM=
github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 h1:u3PMzfF8RkKd3lB9pZ2bfn0qEG+1Gms9599cr0REMww=
github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2/go.mod h1:mIEZOHnFx4ZMQeawhw9rhsj+0zwQj7adVsnBX7t+eKY=
github.com/dolthub/fslock v0.0.5 h1:QoXhBhgY1oumHE26qyE7tgmXUT8qjJwxsIzo54O/B/k=
github.com/dolthub/fslock v0.0.5/go.mod h1:sdofYYqE0D79zNZyB4/kmlnsQOVap1C2yByjGKSirEM=
github.com/dolthub/go-icu-regex v0.0.0-20260610153742-72563bc7ca83 h1:FEMjCGEroDnY/BXyAffVZxUpXhP2GpoUJyyq5KaLn8c=
github.com/dolthub/go-icu-regex v0.0.0-20260610153742-72563bc7ca83/go.mod h1:F3cnm+vMRK1HaU6+rNqQrOCyR03HHhR1GWG2gnPOqaE=
github.com/dolthub/go-mysql-server v0.20.1-0.20260625171506-68aec8237480 h1:LTH0FHVgS2Utgi/98xzfunuXl3dJckMAgM0RLUJAzjM=
github.com/dolthub/go-mysql-server v0.20.1-0.20260625171506-68aec8237480/go.mod h1:mj5/QX3V8i92REbA1w6CzyknJAFdKtdE7l931405C/E=
github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63 h1:OAsXLAPL4du6tfbBgK0xXHZkOlos63RdKYS3Sgw/dfI=
github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63/go.mod h1:lV7lUeuDhH5thVGDCKXbatwKy2KW80L4rMT46n+Y2/Q=
github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 h1:bMGS25NWAGTEtT5tOBsCuCrlYnLRKpbJVJkDbrTRhwQ=
github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71/go.mod h1:2/2zjLQ/JOOSbbSboojeg+cAwcRV0fDLzIiWch/lhqI=
github.com/dolthub/vitess v0.0.0-20260624214226-81d034e0fde8 h1:zmKLyRCTiNZnizO++sNXP1QW2bxLE2Y+WJAdu/hcu6s=
github.com/dolthub/vitess v0.0.0-20260624214226-81d034e0fde8/go.mod h1:5SVEJgAhw5nnQUFnGgKI1Svqes1Mw+zKUsalyxmOuG0=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84=
github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
github.com/emersion/go-message v0.17.0/go.mod h1:/9Bazlb1jwUNB0npYYBsdJ2EMOiiyN3m5UVHbY7GoNw=
github.com/emersion/go-message v0.18.2 h1:rl55SQdjd9oJcIoQNhubD2Acs1E6IzlZISRTK7x/Lpg=
github.com/emersion/go-message v0.18.2/go.mod h1:XpJyL70LwRvq2a8rVbHXikPgKj8+aI0kGdHlg16ibYA=
github.com/emersion/go-pgpmail v0.2.2 h1:cO2jwsE0gb8aDdCcVH5Dfe1XV3Rhhw2GVWsmQd3CbaI=
github.com/emersion/go-pgpmail v0.2.2/go.mod h1:mRB5P7QKiAuOvcT36tdRZvm7nSt7V+f6jbzzup3HuvU=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 h1:hH4PQfOndHDlpzYfLAAfl63E8Le6F2+EL/cdhlkyRJY=
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-smtp v0.21.3 h1:7uVwagE8iPYE48WhNsng3RRpCUpFvNl39JGNSIyGVMY=
github.com/emersion/go-smtp v0.21.3/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g=
github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=
github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4=
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
github.com/esote/minmaxheap v1.0.0 h1:rgA7StnXXpZG6qlM0S7pUmEv1KpWe32rYT4x8J8ntaA=
github.com/esote/minmaxheap v1.0.0/go.mod h1:Ln8+i7fS1k3PLgZI2JAo0iA1as95QnIYiGCrqSJ5FZk=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fernet/fernet-go v0.0.0-20211208181803-9f70042a33ee h1:v6Eju/FhxsACGNipFEPBZZAzGr1F/jlRQr1qiBw2nEE=
github.com/fernet/fernet-go v0.0.0-20211208181803-9f70042a33ee/go.mod h1:2H9hjfbpSMHwY503FclkV/lZTBh2YlOmLLSda12uL8c=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/go-chi/chi/v5 v5.3.1 h1:3j4HZLGZQ3JpMCrPJF/Jl3mYJfWLKBfNJ6quurUGCf8=
github.com/go-chi/chi/v5 v5.3.1/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto=
github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE=
github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0=
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d h1:c93kUJDtVAXFEhsCh5jSxyOJmFHuzcihnslQiX8Urwo=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/kavu/go_reuseport v1.5.0 h1:UNuiY2OblcqAtVDE8Gsg1kZz8zbBWg907sP1ceBV+bk=
github.com/kavu/go_reuseport v1.5.0/go.mod h1:CG8Ee7ceMFSMnx/xr25Vm0qXaj2Z4i5PWoUx+JZ5/CU=
github.com/kch42/buzhash v0.0.0-20160816060738-9bdec3dec7c6 h1:l6Y3mFnF46A+CeZsTrT8kVIuhayq1266oxWpDKE7hnQ=
github.com/kch42/buzhash v0.0.0-20160816060738-9bdec3dec7c6/go.mod h1:UtDV9qK925GVmbdjR+e1unqoo+wGWNHHC6XB1Eu6wpE=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/lestrrat-go/strftime v1.2.0 h1:8fAUYOeaJKCuLzNvUWBAo8t6I6hkFfodDTndEzJIun0=
github.com/lestrrat-go/strftime v1.2.0/go.mod h1:GtsIA/7ddIGJjEdfadUafEb1sbutvlvpMdPCMglykYo=
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mohae/uvarint v0.0.0-20160208145430-c3f9e62bf2b0 h1:fXRYk7YXVIBMGAHT+GmAcbiXrudXMPtqdLfbkVfUhkI=
github.com/mohae/uvarint v0.0.0-20160208145430-c3f9e62bf2b0/go.mod h1:+6ZKJfAk1B0oKLOwdzYuRVJn3upG1c7uOm5Ih7Rrkvc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oracle/oci-go-sdk/v65 v65.55.0 h1:enKyHVLdJYDJrc9232w33u5F6t2p8Din4593kn3nh/w=
github.com/oracle/oci-go-sdk/v65 v65.55.0/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.8.3 h1:DBBfY8eMYazKEJHb3JKpSPfpgd2mBCoNFlQx6C5fftU=
github.com/sirupsen/logrus v1.8.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec h1:DGmKwyZwEB8dI7tbLt/I/gQuP559o/0FrAkHKlQM/Ks=
github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec/go.mod h1:owBmyHYMLkxyrugmfwE/DLJyW8Ro9mkphwuVErQ0iUw=
github.com/vektah/gqlparser/v2 v2.5.8 h1:pm6WOnGdzFOCfcQo9L3+xzW51mKrlwTEg4Wr7AH1JW4=
github.com/vektah/gqlparser/v2 v2.5.8/go.mod h1:z8xXUff237NntSuH8mLFijZ+1tjV1swDbpDqjJmk6ME=
github.com/xtaci/smux v1.5.56 h1:Eyv/dUULmkGZZNucLUisnkzJ/4UQ5YZTschhugFBM0U=
github.com/xtaci/smux v1.5.56/go.mod h1:IGQ9QYrBphmb/4aTnLEcJby0TNr3NV+OslIOMrX825Q=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE=
go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
google.golang.org/api v0.241.0 h1:QKwqWQlkc6O895LchPEDUSYr22Xp3NCxpQRiWTB6avE=
google.golang.org/api v0.241.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/go-jose/go-jose.v2 v2.6.3 h1:nt80fvSDlhKWQgSWyHyy5CfmlQr+asih51R8PTWNKKs=
gopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI=
gopkg.in/src-d/go-errors.v1 v1.0.0 h1:cooGdZnCjYbeS1zb1s6pVAAimTdKceRrpn7aKOnNIfc=
gopkg.in/src-d/go-errors.v1 v1.0.0/go.mod h1:q1cBlomlw2FnDBDNGlnh6X0jPihy+QxZfMMNxPCbdYg=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

A  => internal/smoke/smoke.go +59 -0
@@ 1,59 @@
// Package smoke is a throwaway build-and-resolve smoke test for Phase 0. It
// imports every third-party package the project will depend on and references
// one exported symbol from each, so that `go build ./...` populates go.sum and
// proves the whole dependency set (core-go, dolthub/dolt, grpc, chi, pq, brant,
// logrus, go-jose) resolves and compiles together — including the CGO (gozstd)
// pieces of dolt.
//
// DELETE THIS PACKAGE in the final phase (Phase 3); it exists only to pin and
// verify the dependency graph and has no runtime purpose.
package smoke

import (
	"git.sr.ht/~bitfehler/brant"
	"git.sr.ht/~sircmpwn/core-go/auth"
	"git.sr.ht/~sircmpwn/core-go/config"
	"git.sr.ht/~sircmpwn/core-go/crypto"
	"git.sr.ht/~sircmpwn/core-go/database"
	"git.sr.ht/~sircmpwn/core-go/server"
	"github.com/dolthub/dolt/go/libraries/doltcore/creds"
	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
	"github.com/dolthub/dolt/go/libraries/doltcore/remotesrv"
	"github.com/dolthub/dolt/go/libraries/utils/earl"
	"github.com/dolthub/dolt/go/libraries/utils/filesys"
	"github.com/dolthub/dolt/go/store/nbs"
	"github.com/dolthub/dolt/go/store/types"
	"github.com/go-chi/chi/v5"
	_ "github.com/lib/pq" // registers the "postgres" database/sql driver
	"github.com/sirupsen/logrus"
	"google.golang.org/grpc"
	jose "gopkg.in/go-jose/go-jose.v2"

	remotesapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/remotesapi/v1alpha1"
)

// refs pins one exported symbol from each key package. The values are never
// used; the point is that the compiler resolves the identifiers.
var refs = []any{
	// core-go
	server.New,
	auth.DecodeBearerToken,
	config.LoadConfig,
	crypto.InitCrypto,
	database.Middleware,
	// dolthub/dolt
	remotesrv.NewServer,
	doltdb.LoadDoltDB,
	nbs.NewUnlimitedMemQuotaProvider,
	earl.FileUrlFromPath,
	creds.PubKeyToKIDStr,
	filesys.LocalFS,
	types.Format_DOLT,
	remotesapi.PushConcurrencyControl_PUSH_CONCURRENCY_CONTROL_IGNORE_WORKING_SET,
	// transport / web / db / migrations / logging / jose
	grpc.NewServer,
	chi.NewRouter,
	brant.NewProvider,
	logrus.New,
	jose.NewSigner,
}

A  => migrations/0001_initial.sql +73 -0
@@ 1,73 @@
-- +brant Up
CREATE TYPE visibility AS ENUM (
	'PUBLIC',
	'PRIVATE',
	'UNLISTED'
);

CREATE TYPE user_type AS ENUM (
	'PENDING',
	'USER',
	'ADMIN',
	'SUSPENDED'
);

CREATE TYPE access_mode AS ENUM (
	'RO',
	'RW'
);

CREATE TABLE "user" (
	id integer PRIMARY KEY,
	username varchar(256) UNIQUE,
	created timestamp NOT NULL,
	updated timestamp NOT NULL,
	email varchar(256) NOT NULL UNIQUE,
	user_type user_type NOT NULL,
	url varchar(256),
	location varchar(256),
	bio varchar(4096),
	suspension_notice varchar(4096)
);
CREATE INDEX ix_user_username ON "user"(username);

CREATE TABLE repository (
	id serial PRIMARY KEY,
	created timestamp NOT NULL,
	updated timestamp NOT NULL,
	name varchar(64) NOT NULL,
	description varchar(1024),
	owner_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
	path varchar(1024) NOT NULL UNIQUE,
	visibility visibility NOT NULL,
	CONSTRAINT uq_repo_owner_id_name UNIQUE (owner_id, name)
);

CREATE TABLE access (
	id serial PRIMARY KEY,
	created timestamp NOT NULL,
	updated timestamp NOT NULL,
	repo_id integer NOT NULL REFERENCES repository(id) ON DELETE CASCADE,
	user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
	mode access_mode NOT NULL,
	CONSTRAINT uq_access_user_id_repo_id UNIQUE (user_id, repo_id)
);

CREATE TABLE dolt_key (
	id serial PRIMARY KEY,
	created timestamp NOT NULL,
	user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
	kid varchar(64) NOT NULL UNIQUE,
	pubkey bytea NOT NULL,
	comment varchar(256),
	last_used timestamp
);

-- +brant Down
DROP TABLE dolt_key;
DROP TABLE access;
DROP TABLE repository;
DROP TABLE "user";
DROP TYPE access_mode;
DROP TYPE user_type;
DROP TYPE visibility;

A  => schema.sql +70 -0
@@ 1,70 @@
-- dolt.sr.ht full initial schema.
--
-- This is the authoritative DDL for a fresh install. The brant migration in
-- migrations/0001_initial.sql applies the same objects incrementally; keep the
-- two in sync. The "user" table mirrors meta's users (core-go's
-- FetchMetaProfile lazily inserts rows keyed by meta's user id).

CREATE TYPE visibility AS ENUM (
	'PUBLIC',
	'PRIVATE',
	'UNLISTED'
);

CREATE TYPE user_type AS ENUM (
	'PENDING',
	'USER',
	'ADMIN',
	'SUSPENDED'
);

CREATE TYPE access_mode AS ENUM (
	'RO',
	'RW'
);

CREATE TABLE "user" (
	id integer PRIMARY KEY,               -- meta's user id, inserted explicitly (not serial)
	username varchar(256) UNIQUE,
	created timestamp NOT NULL,
	updated timestamp NOT NULL,
	email varchar(256) NOT NULL UNIQUE,
	user_type user_type NOT NULL,
	url varchar(256),
	location varchar(256),
	bio varchar(4096),
	suspension_notice varchar(4096)
);
CREATE INDEX ix_user_username ON "user"(username);

CREATE TABLE repository (
	id serial PRIMARY KEY,
	created timestamp NOT NULL,
	updated timestamp NOT NULL,
	name varchar(64) NOT NULL,
	description varchar(1024),
	owner_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
	path varchar(1024) NOT NULL UNIQUE,   -- absolute on-disk NBS store dir
	visibility visibility NOT NULL,
	CONSTRAINT uq_repo_owner_id_name UNIQUE (owner_id, name)
);

CREATE TABLE access (
	id serial PRIMARY KEY,
	created timestamp NOT NULL,
	updated timestamp NOT NULL,
	repo_id integer NOT NULL REFERENCES repository(id) ON DELETE CASCADE,
	user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
	mode access_mode NOT NULL,
	CONSTRAINT uq_access_user_id_repo_id UNIQUE (user_id, repo_id)
);

CREATE TABLE dolt_key (
	id serial PRIMARY KEY,
	created timestamp NOT NULL,
	user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
	kid varchar(64) NOT NULL UNIQUE,      -- base32(SHA-512/224(pubkey)), dolt alphabet
	pubkey bytea NOT NULL,                -- 32-byte ed25519 public key
	comment varchar(256),
	last_used timestamp
);

A  => scss/main.scss +26 -0
@@ 1,26 @@
@import "base";

// Minimal dolt.sr.ht service styles layered on the shared core.sr.ht theme.
// The bulk of the look comes from @import "base" (Bootstrap + sourcehut vars),
// resolved via `sassc -I $(ASSETS)/scss`.

.header-extension {
  margin-top: -1rem;
  margin-bottom: 1rem;
}

// Clone box on the database overview page: two variants (token / keypair).
.clone-url {
  font-family: $font-family-monospace;
  input {
    font-family: $font-family-monospace;
  }
}

// Compact grid for the database list on user and dashboard pages.
.repo-list {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-gap: 0.25rem 1rem;
  align-items: baseline;
}

A  => static/logo.svg +14 -0
@@ 1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128">
	<style>
		@media (prefers-color-scheme: light) {
			#outline { display: none }
			#logo { stroke: black }
		}
		@media (prefers-color-scheme: dark) {
			#outline { display: none }
			#logo { stroke: white }
		}
	</style>
	<circle id="outline" cx="64" cy="64" r="50" fill="none" stroke-width="14" stroke="#888" />
	<circle id="logo" cx="64" cy="64" r="50" fill="none" stroke-width="10" stroke="white" />
</svg>

A  => storage/spike_test.go +220 -0
@@ 1,220 @@
//go:build spike

// Phase-0 de-risk spike. This is the whole point of Phase 0: prove that the
// pinned github.com/dolthub/dolt/go module can init a bare NBS store, serve it
// over remotesrv, and round-trip a real `dolt` CLI (v2.1.10) clone -> insert ->
// commit -> push -> re-clone. If this passes, the storage format and remotesapi
// RPCs are compatible between the module and the CLI, and the parallel waves
// can proceed.
//
// Run with:
//
//	CGO_CPPFLAGS=-I/opt/homebrew/opt/icu4c@78/include \
//	CGO_LDFLAGS=-L/opt/homebrew/opt/icu4c@78/lib \
//	go test -tags spike ./storage/ -run TestSpike -v
//
// The test skips (does not fail) when the dolt CLI is absent.
package storage

import (
	"context"
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"testing"
	"time"

	remotesapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/remotesapi/v1alpha1"
	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
	"github.com/dolthub/dolt/go/libraries/doltcore/remotesrv"
	"github.com/dolthub/dolt/go/libraries/utils/earl"
	"github.com/dolthub/dolt/go/libraries/utils/filesys"
	"github.com/dolthub/dolt/go/store/nbs"
	"github.com/dolthub/dolt/go/store/types"
	"github.com/sirupsen/logrus"
)

const doltBin = "/opt/homebrew/bin/dolt"

// spikeCache is a minimal remotesrv.DBCache modeled on the upstream
// utils/remotesrv LocalCSCache: it memoizes one nbs.NewLocalStore per repo
// path, resolved under a fixed root directory. Real storage/ (wave A) will
// grow repo-row validation and eviction on top of this shape.
type spikeCache struct {
	mu   sync.Mutex
	root string
	dbs  map[string]remotesrv.RemoteSrvStore
}

func (c *spikeCache) Get(ctx context.Context, path, nbfVerStr string) (remotesrv.RemoteSrvStore, error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	id := filepath.FromSlash(strings.Trim(path, "/"))
	if cs, ok := c.dbs[id]; ok {
		return cs, nil
	}
	abs := filepath.Join(c.root, id)
	if err := os.MkdirAll(abs, 0o755); err != nil {
		return nil, err
	}
	cs, err := nbs.NewLocalStore(ctx, nbfVerStr, abs, 128*1024*1024, nbs.NewUnlimitedMemQuotaProvider(), false)
	if err != nil {
		return nil, err
	}
	c.dbs[id] = cs
	return cs, nil
}

func TestSpike(t *testing.T) {
	if _, err := os.Stat(doltBin); err != nil {
		t.Skipf("dolt CLI not found at %s (%v); skipping interop spike", doltBin, err)
	}

	ctx := context.Background()
	root := t.TempDir()
	reposRoot := filepath.Join(root, "repos")

	// 1. Create a bare NBS store at repos/test/db and write an empty repo.
	storeDir := filepath.Join(reposRoot, "test", "db")
	if err := os.MkdirAll(storeDir, 0o755); err != nil {
		t.Fatal(err)
	}
	fileURL := earl.FileUrlFromPath(storeDir, os.PathSeparator)
	ddb, err := doltdb.LoadDoltDB(ctx, types.Format_DOLT, fileURL, filesys.LocalFS)
	if err != nil {
		t.Fatalf("LoadDoltDB(%s): %v", fileURL, err)
	}
	if err := ddb.WriteEmptyRepo(ctx, "main", "spike", "spike@test.local"); err != nil {
		t.Fatalf("WriteEmptyRepo: %v", err)
	}
	// Release the init handle before the server opens its own store over the
	// same directory.
	if err := ddb.Close(); err != nil {
		t.Fatalf("close init DoltDB: %v", err)
	}

	// 2. Serve repos/ over remotesrv on a single ephemeral localhost port
	// (http + gRPC multiplexed), no auth interceptors, plain http.
	addr := freeAddr(t)
	cache := &spikeCache{root: reposRoot, dbs: map[string]remotesrv.RemoteSrvStore{}}
	logger := logrus.New()
	logger.SetLevel(logrus.ErrorLevel)
	// The server FS must be rooted at reposRoot so chunk-download URL prefixes
	// are clean relatives (e.g. "test/db") rather than "../../.." paths, which
	// the sealed-URL file handler rejects. Upstream achieves this by chdir-ing
	// to the repos root; we set the FS working dir instead to avoid mutating
	// global process state.
	fs, err := filesys.LocalFilesysWithWorkingDir(reposRoot)
	if err != nil {
		t.Fatalf("LocalFilesysWithWorkingDir(%s): %v", reposRoot, err)
	}
	server, err := remotesrv.NewServer(remotesrv.ServerArgs{
		Logger:             logrus.NewEntry(logger),
		HttpHost:           "", // echo request :authority into chunk URLs
		HttpListenAddr:     addr,
		GrpcListenAddr:     addr, // == HttpListenAddr => single-port multiplex
		FS:                 fs,
		DBCache:            cache,
		ConcurrencyControl: remotesapi.PushConcurrencyControl_PUSH_CONCURRENCY_CONTROL_IGNORE_WORKING_SET,
	})
	if err != nil {
		t.Fatalf("remotesrv.NewServer: %v", err)
	}
	listeners, err := server.Listeners()
	if err != nil {
		t.Fatalf("server.Listeners: %v", err)
	}
	go server.Serve(listeners)
	defer server.GracefulStop()
	waitTCP(t, addr)

	// 3. Drive the real dolt CLI against the server in an isolated HOME so the
	// developer's real dolt config is untouched.
	home := filepath.Join(root, "home")
	if err := os.MkdirAll(home, 0o755); err != nil {
		t.Fatal(err)
	}
	env := append(os.Environ(), "HOME="+home)
	runDolt(t, env, root, "config", "--global", "--add", "user.email", "spike@test.local")
	runDolt(t, env, root, "config", "--global", "--add", "user.name", "spike")

	url := fmt.Sprintf("http://%s/test/db", addr)

	clone1 := filepath.Join(root, "clone1")
	if err := os.MkdirAll(clone1, 0o755); err != nil {
		t.Fatal(err)
	}
	runDolt(t, env, clone1, "clone", url)

	work := filepath.Join(clone1, "db")
	runDolt(t, env, work, "sql", "-q",
		"create table t(i int primary key); insert into t values (1),(2),(3);")
	runDolt(t, env, work, "commit", "-Am", "spike commit")
	runDolt(t, env, work, "push", "origin", "main")

	// 4. Fresh re-clone and verify the pushed rows are present.
	clone2 := filepath.Join(root, "clone2")
	if err := os.MkdirAll(clone2, 0o755); err != nil {
		t.Fatal(err)
	}
	runDolt(t, env, clone2, "clone", url)
	out := runDolt(t, env, filepath.Join(clone2, "db"), "sql", "-q",
		"select i from t order by i", "-r", "csv")

	for _, want := range []string{"1", "2", "3"} {
		if !strings.Contains(out, want) {
			t.Fatalf("re-cloned db missing row %q; got csv:\n%s", want, out)
		}
	}
	t.Logf("spike round-trip OK; re-clone csv:\n%s", out)
}

// freeAddr returns a currently-free 127.0.0.1 address. There is an inherent
// race between closing the probe listener and the server binding, but it is
// acceptable for a single local test.
func freeAddr(t *testing.T) string {
	t.Helper()
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("probe listen: %v", err)
	}
	addr := l.Addr().String()
	if err := l.Close(); err != nil {
		t.Fatalf("probe close: %v", err)
	}
	return addr
}

// waitTCP blocks until addr accepts a TCP connection or the deadline elapses.
func waitTCP(t *testing.T, addr string) {
	t.Helper()
	deadline := time.Now().Add(10 * time.Second)
	for time.Now().Before(deadline) {
		conn, err := net.DialTimeout("tcp", addr, 200*time.Millisecond)
		if err == nil {
			conn.Close()
			return
		}
		time.Sleep(50 * time.Millisecond)
	}
	t.Fatalf("server at %s never became reachable", addr)
}

// runDolt runs the dolt CLI in dir with env, fails the test on non-zero exit,
// and returns combined stdout+stderr.
func runDolt(t *testing.T, env []string, dir string, args ...string) string {
	t.Helper()
	cmd := exec.Command(doltBin, args...)
	cmd.Dir = dir
	cmd.Env = env
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("dolt %s failed: %v\n%s", strings.Join(args, " "), err, out)
	}
	return string(out)
}