diff --git a/.drone-manifest.yml b/.drone-manifest.yml new file mode 100644 index 0000000..5ca6313 --- /dev/null +++ b/.drone-manifest.yml @@ -0,0 +1,22 @@ +image: happydomain/checker-srv:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} +{{#if build.tags}} +tags: +{{#each build.tags}} + - {{this}} +{{/each}} +{{/if}} +manifests: + - image: happydomain/checker-srv:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 + platform: + architecture: amd64 + os: linux + - image: happydomain/checker-srv:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 + platform: + architecture: arm64 + os: linux + variant: v8 + - image: happydomain/checker-srv:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm + platform: + architecture: arm + os: linux + variant: v7 diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..9689361 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,187 @@ +--- +kind: pipeline +type: docker +name: build-amd64 + +platform: + os: linux + arch: amd64 + +steps: + - name: checker build + image: golang:1-alpine + commands: + - apk add --no-cache git make + - make + environment: + CHECKER_VERSION: "${DRONE_BRANCH}-${DRONE_COMMIT}" + CGO_ENABLED: 0 + when: + event: + exclude: + - tag + + - name: checker build tag + image: golang:1-alpine + commands: + - apk add --no-cache git make + - make + environment: + CHECKER_VERSION: "${DRONE_SEMVER}" + CGO_ENABLED: 0 + when: + event: + - tag + + - name: publish on Docker Hub + image: plugins/docker + settings: + repo: happydomain/checker-srv + auto_tag: true + auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} + dockerfile: Dockerfile + build_args: + - CHECKER_VERSION=${DRONE_BRANCH}-${DRONE_COMMIT} + username: + from_secret: docker_username + password: + from_secret: docker_password + when: + event: + exclude: + - tag + + - name: publish on Docker Hub (tag) + image: plugins/docker + settings: + repo: happydomain/checker-srv + auto_tag: true + auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} + dockerfile: Dockerfile + build_args: + - CHECKER_VERSION=${DRONE_SEMVER} + username: + from_secret: docker_username + password: + from_secret: docker_password + when: + event: + - tag + +trigger: + branch: + exclude: + - renovate/* + event: + - cron + - push + - tag + +--- +kind: pipeline +type: docker +name: build-arm64 + +platform: + os: linux + arch: arm64 + +steps: + - name: checker build + image: golang:1-alpine + commands: + - apk add --no-cache git make + - make + environment: + CHECKER_VERSION: "${DRONE_BRANCH}-${DRONE_COMMIT}" + CGO_ENABLED: 0 + when: + event: + exclude: + - tag + + - name: checker build tag + image: golang:1-alpine + commands: + - apk add --no-cache git make + - make + environment: + CHECKER_VERSION: "${DRONE_SEMVER}" + CGO_ENABLED: 0 + when: + event: + - tag + + - name: publish on Docker Hub + image: plugins/docker + settings: + repo: happydomain/checker-srv + auto_tag: true + auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} + dockerfile: Dockerfile + build_args: + - CHECKER_VERSION=${DRONE_BRANCH}-${DRONE_COMMIT} + username: + from_secret: docker_username + password: + from_secret: docker_password + when: + event: + exclude: + - tag + + - name: publish on Docker Hub (tag) + image: plugins/docker + settings: + repo: happydomain/checker-srv + auto_tag: true + auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH} + dockerfile: Dockerfile + build_args: + - CHECKER_VERSION=${DRONE_SEMVER} + username: + from_secret: docker_username + password: + from_secret: docker_password + when: + event: + - tag + +trigger: + event: + - cron + - push + - tag + +--- +kind: pipeline +name: docker-manifest + +platform: + os: linux + arch: arm64 + +steps: + - name: publish on Docker Hub + image: plugins/manifest + settings: + auto_tag: true + ignore_missing: true + spec: .drone-manifest.yml + username: + from_secret: docker_username + password: + from_secret: docker_password + +trigger: + branch: + exclude: + - renovate/* + event: + - cron + - push + - tag + +depends_on: + - build-amd64 + - build-arm64 diff --git a/README.md b/README.md index 38e3ede..c5b791c 100644 --- a/README.md +++ b/README.md @@ -64,53 +64,19 @@ make docker CHECKER_VERSION=1.2.3 Set the `endpoint` admin option for the SRV checker to the URL of the running checker-srv server (e.g., `http://checker-srv:8080`). happyDomain will delegate observation collection to this endpoint. -## Protocol +## Rules -### POST /collect - -Request: -```json -{ - "key": "srv_records", - "target": {"userId": "...", "domainId": "...", "serviceId": "..."}, - "options": { - "service": {"_svctype": "svcs.UnknownSRV", "Service": {"srv": [ /* dns.SRV records */ ]}}, - "subdomain": "_sip._tcp", - "domain": "example.com", - "tcpTimeout": 3000, - "udpTimeout": 2000 - } -} -``` - -Response: -```json -{ - "data": { - "serviceDomain": "_sip._tcp.example.com", - "records": [ - { - "service": "sip", - "proto": "tcp", - "owner": "_sip._tcp.example.com", - "target": "sip1.example.com", - "port": 5061, - "priority": 10, - "weight": 20, - "addresses": ["203.0.113.5"], - "probes": [ - { - "address": "203.0.113.5:5061", - "proto": "tcp", - "connected": true, - "latencyMs": 12.4 - } - ] - } - ] - } -} -``` +| Code | Description | Severity | +|-------------------------------|---------------------------------------------------------------------------------------------------|---------------------| +| `srv_records_present` | At least one SRV record is published for this service. | CRITICAL | +| `srv_null_target` | Detects SRV records with target `.`, signaling the service is intentionally not available. | WARNING | +| `srv_target_not_cname` | RFC 2782: SRV targets must resolve directly to A/AAAA, not through a CNAME. | WARNING | +| `srv_targets_resolve` | Every SRV target resolves to at least one A/AAAA address. | CRITICAL | +| `srv_port_valid` | SRV records advertise a non-zero port that clients can actually connect to. | CRITICAL | +| `srv_priority_weight_sanity` | Priority/weight values follow RFC 2782 conventions (failover present, weights meaningful). | WARNING | +| `srv_tcp_reachable` | Every TCP SRV `target:port` accepts a TCP connection. | CRITICAL | +| `srv_udp_reachable` | UDP SRV targets do not return ICMP port-unreachable. | WARNING | +| `srv_redundancy` | At least two distinct SRV targets exist (avoids single point of failure). | INFO | ## Discovered TLS endpoints @@ -132,18 +98,6 @@ so that the TLS checker can probe the targets without re-parsing SRV data: Null targets (`.`) and unknown service names are skipped. SNI is set to the SRV target hostname. -## Rules - -| Name | Description | -|---|---| -| `srv_records_present` | At least one SRV record is published. | -| `srv_null_target` | Detects `"."` targets (RFC 2782, service intentionally unavailable). | -| `srv_target_not_cname` | Warns when an SRV target is a CNAME (RFC 2782 violation). | -| `srv_targets_resolve` | Every target resolves to at least one A/AAAA. | -| `srv_tcp_reachable` | Every `_tcp` SRV `target:port` accepts a TCP connection. | -| `srv_udp_reachable` | UDP targets do not return ICMP port-unreachable. | -| `srv_redundancy` | At least two distinct targets exist (no single point of failure). | - ## License & licensing roadmap This project is currently licensed under the **GNU Affero General Public diff --git a/checker/collect.go b/checker/collect.go index 4a410fb..83f37df 100644 --- a/checker/collect.go +++ b/checker/collect.go @@ -86,7 +86,10 @@ func (p *srvProvider) Collect(ctx context.Context, opts sdk.CheckerOptions) (any } for _, r := range payload.Records { - owner := strings.TrimSuffix(r.Hdr.Name, ".") + // Hdr.Name is relative to the service location (serviceDomain = + // subdomain.domain), so we join it with serviceDomain before + // treating as FQDN. + owner := sdk.JoinRelative(r.Hdr.Name, serviceDomain) svc, proto := parseOwner(owner, serviceDomain) rec := SRVRecord{ diff --git a/checker/report.go b/checker/report.go index 8cdb3c2..5d44efb 100644 --- a/checker/report.go +++ b/checker/report.go @@ -256,52 +256,79 @@ func (p *srvProvider) GetHTMLReport(ctx sdk.ReportContext) (string, error) { rd.Records = append(rd.Records, rec) } - if len(resolveFails) > 0 { - rd.Alerts = append(rd.Alerts, reportAlert{ - Severity: "crit", - Title: fmt.Sprintf("DNS resolution failed for %d SRV target(s)", len(resolveFails)), - Body: template.HTML(fmt.Sprintf( - "%s
Clients will not be able to reach the service. Fix: either publish A/AAAA records for the target(s), or remove the broken SRV record.", - strings.Join(resolveFails, "
"))), - }) - } - if len(cnames) > 0 { - rd.Alerts = append(rd.Alerts, reportAlert{ - Severity: "warn", - Title: "SRV target is a CNAME (RFC 2782 violation)", - Body: template.HTML(fmt.Sprintf( - "Target(s): %s
RFC 2782 requires SRV targets to resolve directly to A/AAAA. "+ - "Some clients will refuse to follow the CNAME. Fix: point the SRV record to a hostname with A/AAAA records, "+ - "or replace the CNAME with an ALIAS/ANAME at the DNS provider.", - ""+strings.Join(cnames, ", ")+"")), - }) - } - if len(tcpDown) > 0 { - var items []string - for _, f := range tcpDown { - items = append(items, fmt.Sprintf("%s (%s): %s", - template.HTMLEscapeString(f.address), - template.HTMLEscapeString(f.owner), - template.HTMLEscapeString(f.err))) + // Build alerts from rule states when available; fall back to raw-data + // analysis when the host hasn't threaded rule output through yet. + states := ctx.States() + if len(states) > 0 { + for _, st := range states { + sev := "" + switch st.Status { + case sdk.StatusCrit, sdk.StatusError: + sev = "crit" + case sdk.StatusWarn: + sev = "warn" + case sdk.StatusInfo: + sev = "info" + default: + continue + } + alert := reportAlert{ + Severity: sev, + Title: st.Message, + } + if fix, ok := st.Meta["fix"].(string); ok && fix != "" { + alert.Body = template.HTML(template.HTMLEscapeString(fix)) + } + rd.Alerts = append(rd.Alerts, alert) + } + } else { + if len(resolveFails) > 0 { + rd.Alerts = append(rd.Alerts, reportAlert{ + Severity: "crit", + Title: fmt.Sprintf("DNS resolution failed for %d SRV target(s)", len(resolveFails)), + Body: template.HTML(fmt.Sprintf( + "%s
Clients will not be able to reach the service. Fix: either publish A/AAAA records for the target(s), or remove the broken SRV record.", + strings.Join(resolveFails, "
"))), + }) + } + if len(cnames) > 0 { + rd.Alerts = append(rd.Alerts, reportAlert{ + Severity: "warn", + Title: "SRV target is a CNAME (RFC 2782 violation)", + Body: template.HTML(fmt.Sprintf( + "Target(s): %s
RFC 2782 requires SRV targets to resolve directly to A/AAAA. "+ + "Some clients will refuse to follow the CNAME. Fix: point the SRV record to a hostname with A/AAAA records, "+ + "or replace the CNAME with an ALIAS/ANAME at the DNS provider.", + ""+strings.Join(cnames, ", ")+"")), + }) + } + if len(tcpDown) > 0 { + var items []string + for _, f := range tcpDown { + items = append(items, fmt.Sprintf("%s (%s): %s", + template.HTMLEscapeString(f.address), + template.HTMLEscapeString(f.owner), + template.HTMLEscapeString(f.err))) + } + rd.Alerts = append(rd.Alerts, reportAlert{ + Severity: "crit", + Title: fmt.Sprintf("%d target(s) unreachable on their advertised TCP port", len(tcpDown)), + Body: template.HTML(strings.Join(items, "
") + + "
Check: (1) the server is running and bound to the right port; " + + "(2) firewall/security-group allows inbound TCP to that port; " + + "(3) the SRV record is not pointing at an old IP."), + }) + } + if len(nulls) > 0 && len(nulls) == len(d.Records) { + rd.Alerts = append(rd.Alerts, reportAlert{ + Severity: "warn", + Title: "All SRV records use the null target (\".\"): service is explicitly disabled", + Body: template.HTML( + "RFC 2782 defines a single SRV record with target \".\" to signal that the service is " + + "intentionally not available. If this is what you want, the configuration is correct. " + + "If you expected clients to reach this service, replace the null target with a real hostname."), + }) } - rd.Alerts = append(rd.Alerts, reportAlert{ - Severity: "crit", - Title: fmt.Sprintf("%d target(s) unreachable on their advertised TCP port", len(tcpDown)), - Body: template.HTML(strings.Join(items, "
") + - "
Check: (1) the server is running and bound to the right port; " + - "(2) firewall/security-group allows inbound TCP to that port; " + - "(3) the SRV record is not pointing at an old IP."), - }) - } - if len(nulls) > 0 && len(nulls) == len(d.Records) { - rd.Alerts = append(rd.Alerts, reportAlert{ - Severity: "warn", - Title: "All SRV records use the null target (\".\"): service is explicitly disabled", - Body: template.HTML( - "RFC 2782 defines a single SRV record with target \".\" to signal that the service is " + - "intentionally not available. If this is what you want, the configuration is correct. " + - "If you expected clients to reach this service, replace the null target with a real hostname."), - }) } var buf strings.Builder diff --git a/go.mod b/go.mod index 851c290..c53916b 100644 --- a/go.mod +++ b/go.mod @@ -3,44 +3,44 @@ module git.happydns.org/checker-srv go 1.25.0 require ( - git.happydns.org/checker-sdk-go v1.5.0 - git.happydns.org/checker-tls v0.6.2 + git.happydns.org/checker-sdk-go v1.7.0 + git.happydns.org/checker-tls v0.7.0 git.happydns.org/happyDomain v0.7.0 ) require ( - github.com/bytedance/gopkg v0.1.3 // indirect - github.com/bytedance/sonic v1.15.0 // indirect - github.com/bytedance/sonic/loader v0.5.0 // indirect - github.com/cloudwego/base64x v0.1.6 // indirect + github.com/bytedance/gopkg v0.1.4 // indirect + github.com/bytedance/sonic v1.15.1 // indirect + github.com/bytedance/sonic/loader v0.5.1 // indirect + github.com/cloudwego/base64x v0.1.7 // indirect github.com/gabriel-vasile/mimetype v1.4.13 // indirect - github.com/gin-contrib/sse v1.1.0 // indirect + github.com/gin-contrib/sse v1.1.1 // indirect github.com/gin-gonic/gin v1.12.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.30.1 // indirect - github.com/goccy/go-json v0.10.5 // indirect + github.com/go-playground/validator/v10 v10.30.2 // indirect + github.com/goccy/go-json v0.10.6 // indirect github.com/goccy/go-yaml v1.19.2 // indirect github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-isatty v0.0.22 // indirect github.com/miekg/dns v1.1.72 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pelletier/go-toml/v2 v2.3.1 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.1 // indirect - go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect - golang.org/x/arch v0.24.0 // indirect - golang.org/x/crypto v0.49.0 // indirect - golang.org/x/mod v0.33.0 // indirect - golang.org/x/net v0.51.0 // indirect + go.mongodb.org/mongo-driver/v2 v2.6.0 // indirect + golang.org/x/arch v0.27.0 // indirect + golang.org/x/crypto v0.51.0 // indirect + golang.org/x/mod v0.36.0 // indirect + golang.org/x/net v0.54.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/text v0.35.0 // indirect - golang.org/x/tools v0.42.0 // indirect + golang.org/x/sys v0.44.0 // indirect + golang.org/x/text v0.37.0 // indirect + golang.org/x/tools v0.45.0 // indirect google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/go.sum b/go.sum index 4bf62a6..3664c74 100644 --- a/go.sum +++ b/go.sum @@ -1,25 +1,25 @@ -git.happydns.org/checker-sdk-go v1.5.0 h1:5uD5Cm6xJ+lwnhbJ09iCXGHbYS9zRh+Yh0NeBHkAPBY= -git.happydns.org/checker-sdk-go v1.5.0/go.mod h1:aNAcfYFfbhvH9kJhE0Njp5GX0dQbxdRB0rJ0KvSC5nI= -git.happydns.org/checker-tls v0.6.2 h1:8oKia1XlD+tklyqrwzmUgFH1Kw8VLSLLF9suZ7Qr14E= -git.happydns.org/checker-tls v0.6.2/go.mod h1:9tpnxg0iOwS+7If64DRG1jqYonUAgxOBuxwfF5mVkL4= +git.happydns.org/checker-sdk-go v1.7.0 h1:dSgo2js5mfXluLc6x0WWZ0MQULd9XV2GI9z0Usk+Qgw= +git.happydns.org/checker-sdk-go v1.7.0/go.mod h1:aNAcfYFfbhvH9kJhE0Njp5GX0dQbxdRB0rJ0KvSC5nI= +git.happydns.org/checker-tls v0.7.0 h1:mfNHYbHMGS40y+N2rudC2svT/xLK7KCiSa7V8/RhcTM= +git.happydns.org/checker-tls v0.7.0/go.mod h1:wlY4UI3owvqMAtOcXLmskpTpZ7xPjuiV6M42+rFZDQo= git.happydns.org/happyDomain v0.7.0 h1:NV82/NbcSeRm0+IUZqaK3Vu9Ovl5+vv4AigUJZMdwws= git.happydns.org/happyDomain v0.7.0/go.mod h1:5tgkmqFE65kK359rY49V++49wgZ0gco+Gh9X6tbL+bY= -github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= -github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= -github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= -github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= -github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= -github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= -github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= -github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM= +github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4= +github.com/bytedance/sonic v1.15.1 h1:nJD5PmM0vY7J8CT6MxoqbVAAMhkSmV2HgRAUrrpLoOw= +github.com/bytedance/sonic v1.15.1/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA= +github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI= +github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI= +github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg= 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= -github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= -github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-contrib/sse v1.1.1 h1:uGYpNwTacv5R68bSGMapo62iLTRa9l5zxGCps4hK6ko= +github.com/gin-contrib/sse v1.1.1/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s= github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -28,10 +28,10 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= -github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/go-playground/validator/v10 v10.30.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ= +github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc= +github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= +github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -43,8 +43,8 @@ github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzh github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -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/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= +github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -52,8 +52,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= +github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -75,27 +75,26 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= -go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE= -go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= +go.mongodb.org/mongo-driver/v2 v2.6.0 h1:b9sJOYrkmt4l8bY43ZenFBcPlhYIjaOfYHLtbB/5qi8= +go.mongodb.org/mongo-driver/v2 v2.6.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= -golang.org/x/arch v0.24.0 h1:qlJ3M9upxvFfwRM51tTg3Yl+8CP9vCC1E7vlFpgv99Y= -golang.org/x/arch v0.24.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= -golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= -golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= +golang.org/x/arch v0.27.0 h1:0WNVcR8u9yFz8j5FvdHpgwNp3FS5U4guYdzHwEiGjoU= +golang.org/x/arch v0.27.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8= +golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= +golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= +golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= +golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= +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/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= +golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= +golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=