Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c64ef628e | ||
|
|
e7b64bfad8 | ||
|
|
53a4a5bb5d | ||
|
|
c5d633bf3a | ||
|
|
f021830ebf | ||
|
|
90c5f81271 | ||
|
|
963a9a2be5 | ||
|
|
fc69bb81f6 | ||
|
|
006dda86c7 | ||
|
|
23be765f2f | ||
|
|
7ce41309a8 | ||
|
|
342e0126a4 | ||
|
|
ae84372e6c | ||
|
|
4a32bd9802 | ||
|
|
828da1b916 | ||
|
|
d378702c23 | ||
|
|
2e8fc55a9b | ||
|
|
65af686c34 | ||
|
|
ea90a0ebf0 | ||
|
|
15e1cf9175 | ||
|
|
0de5ca4cbd | ||
|
|
e71712e1dd | ||
|
|
5f3b1bd6cc | ||
|
|
30eb88a821 |
@@ -12,7 +12,7 @@ on:
|
||||
jobs:
|
||||
check-fe-ts-lint:
|
||||
name: Check Frontend Type and Lint check
|
||||
runs-on: [self-hosted, linux, amd64]
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: [self-hosted, linux, amd64]
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -71,7 +71,19 @@ jobs:
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
GOEXPERIMENT: jsonv2
|
||||
VERSION: ${{ steps.version.outputs.tag }}
|
||||
# luxfi (+ hanzoai) modules are first-party/private: resolve them
|
||||
# directly via git and skip the public checksum DB — force-moved
|
||||
# upstream tags never match sum.golang.org. GH_PAT carries luxfi read
|
||||
# access; the repo GITHUB_TOKEN is the fallback for public modules.
|
||||
GOPRIVATE: github.com/luxfi/*,github.com/hanzoai/*
|
||||
GONOSUMDB: github.com/luxfi/*,github.com/hanzoai/*
|
||||
GH_PAT: ${{ secrets.GH_PAT }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
token="${GH_PAT:-$GITHUB_TOKEN}"
|
||||
if [ -n "$token" ]; then
|
||||
git config --global url."https://x-access-token:${token}@github.com/".insteadOf "https://github.com/"
|
||||
fi
|
||||
mkdir -p dist
|
||||
go build -trimpath \
|
||||
-ldflags="-s -w -X main.version=${VERSION}" \
|
||||
@@ -177,7 +189,7 @@ jobs:
|
||||
ghcr.io/hanzoai/kms:latest
|
||||
build-args: |
|
||||
GO_EXPERIMENT=jsonv2
|
||||
GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN=${{ secrets.GH_PAT || secrets.GITHUB_TOKEN }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
|
||||
+9
-3
@@ -4,7 +4,7 @@
|
||||
# required). The TS dashboard ships as a static asset built in a separate
|
||||
# stage and copied verbatim.
|
||||
|
||||
FROM node:22-alpine AS frontend
|
||||
FROM ghcr.io/hanzoai/nodejs:v24.18.0 AS frontend
|
||||
WORKDIR /src/frontend
|
||||
COPY frontend/package.json frontend/pnpm-lock.yaml ./
|
||||
RUN corepack enable pnpm && pnpm install --frozen-lockfile
|
||||
@@ -18,10 +18,16 @@ ARG TARGETARCH
|
||||
|
||||
WORKDIR /src
|
||||
COPY go.mod go.sum ./
|
||||
# kmsclient is an in-repo module built via a local replace (see go.mod).
|
||||
# Stage its go.mod/go.sum so `go mod download` can read the replaced module's
|
||||
# graph before the full source tree is copied.
|
||||
COPY sdk/go/go.mod sdk/go/go.sum ./sdk/go/
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
if [ -n "${GITHUB_TOKEN}" ]; then \
|
||||
git config --global url."https://x-access-token:${GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/"; \
|
||||
fi && \
|
||||
GOPRIVATE="github.com/luxfi/*,github.com/hanzoai/*" \
|
||||
GONOSUMCHECK="github.com/luxfi/*,github.com/hanzoai/*" \
|
||||
git config --global url."https://${GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/" && \
|
||||
GONOSUMDB="github.com/luxfi/*,github.com/hanzoai/*" \
|
||||
go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
@@ -28,6 +28,10 @@ ARG TARGETARCH
|
||||
|
||||
WORKDIR /src
|
||||
COPY go.mod go.sum ./
|
||||
# kmsclient is an in-repo module built via a local replace (see go.mod).
|
||||
# Stage its go.mod/go.sum so `go mod download` can read the replaced module's
|
||||
# graph before the full source tree is copied.
|
||||
COPY sdk/go/go.mod sdk/go/go.sum ./sdk/go/
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
GOPRIVATE="github.com/luxfi/*,github.com/hanzoai/*" \
|
||||
GONOSUMCHECK="github.com/luxfi/*,github.com/hanzoai/*" \
|
||||
|
||||
@@ -1,267 +1,101 @@
|
||||
# Hanzo KMS
|
||||
# Hanzo KMS — agent guide
|
||||
|
||||
**Last Updated**: 2026-04-23
|
||||
**Repo**: github.com/hanzoai/kms
|
||||
**Module**: github.com/hanzoai/kms (Go 1.26.1)
|
||||
|
||||
**Upstream lineage**: see `LICENSE` for the required upstream MIT attribution. The legacy fork's code path was deleted; the current request path is a thin wrapper over `github.com/luxfi/kms`. The copyright header is retained for legal accuracy, but none of the original upstream code remains on the wire.
|
||||
**Repo**: `github.com/hanzoai/kms` · **Module**: `github.com/hanzoai/kms` (Go 1.26)
|
||||
|
||||
## What this is
|
||||
|
||||
A thin Hanzo-branded wrapper over `github.com/luxfi/kms`. The Go server,
|
||||
secret store, MPC client, ZAP secrets server, and S3 replicator all come
|
||||
from luxfi/kms. This module owns three things:
|
||||
The canonical secret store + threshold-signing service for every Hanzo deployment.
|
||||
A **thin Go wrapper over `github.com/luxfi/kms`** (v1.11.x) + `luxfi/mpc` — all server
|
||||
logic lives upstream; this module wires those primitives with Hanzo defaults and adds
|
||||
JWT verification, the audit ledger, version CAS, and header hygiene. The root package
|
||||
`kms` mounts into the unified cloud binary via `kms.Mount(app, deps)` (HIP-0106) and
|
||||
also ships as the standalone `cmd/kmsd` daemon. There is **no** Node fork, **no**
|
||||
PostgreSQL, **no** Base — the legacy `internal/{handler,store,server}` tree is gone.
|
||||
|
||||
1. `cmd/kmsd` — the daemon. Wires luxfi/kms primitives with Hanzo defaults
|
||||
(port 8443, `/data/hanzo-kms`, IAM at `https://hanzo.id`) and adds
|
||||
Hanzo-specific JWT verification, audit log, version CAS, header hygiene.
|
||||
2. `cmd/kms` — the admin CLI (`kms put|get|list|rotate|status`).
|
||||
3. `pkg/kmsclient` — the Go client used by every other Hanzo service to
|
||||
fetch secrets at runtime (HTTP + ZAP fallback).
|
||||
## Canonical role (Hanzo SDK model)
|
||||
|
||||
There is **no legacy Node.js fork** in this repo. There is **no Base/SQLite**. There
|
||||
is **no PostgreSQL** in the canonical Go module. Everything that used to
|
||||
live in `internal/handler/`, `internal/store/`, `internal/server/` has been
|
||||
deleted in favour of the upstream `github.com/luxfi/kms/pkg/{store,keys,
|
||||
mpc,zapserver}` packages. The `frontend/` and `ui/` trees are legacy React
|
||||
assets shipped as static files; they do not affect the API surface.
|
||||
This is a **product/service repo** (`hanzoai/<product>`) — the canonical impl of KMS.
|
||||
Its Go client lives in-repo at `sdk/go` (module `github.com/hanzoai/kms/sdk/go`) and is
|
||||
what every other Hanzo service imports to fetch secrets. Full model:
|
||||
`~/work/hanzo/SDK-ARCHITECTURE.md` (one impl one place; discovery repos link out).
|
||||
|
||||
## Architecture
|
||||
## Brand rules (hard)
|
||||
|
||||
```
|
||||
┌──────────────────────────────┐
|
||||
│ Hanzo IAM (hanzo.id) │
|
||||
│ RFC 6749 + OIDC, JWKS │
|
||||
└──────────────┬───────────────┘
|
||||
OAuth2 / JWT (RS256)
|
||||
│
|
||||
┌───────────────────────────▼──────────────────────────┐
|
||||
│ kmsd │
|
||||
│ HTTP :8443 (mux: /v1/kms/* only) │
|
||||
│ ZAP :9999 (binary, opcodes 0x0040..0x0043) │
|
||||
│ │
|
||||
│ cmd/kmsd/main.go ← wiring + routes │
|
||||
│ cmd/kmsd/auth.go ← JWT verify (RFC 7519) │
|
||||
│ cmd/kmsd/jwks.go ← JWKS cache + RSA resolve │
|
||||
│ cmd/kmsd/audit.go ← per-request audit ledger │
|
||||
│ cmd/kmsd/versioning.go← per-secret version CAS │
|
||||
│ │
|
||||
│ github.com/luxfi/kms/pkg/store ← secret CRUD │
|
||||
│ github.com/luxfi/kms/pkg/keys ← MPC key mgr │
|
||||
│ github.com/luxfi/kms/pkg/mpc ← MPC client │
|
||||
│ github.com/luxfi/kms/pkg/zapserver← ZAP transport │
|
||||
└──────────────┬───────────────────────────────────────┘
|
||||
│
|
||||
┌──────────────▼─────────────┐ ┌─────────────────────┐
|
||||
│ ZapDB (encrypted, LSM) │───▶│ S3 (age-encrypted)│
|
||||
│ $KMS_DATA_DIR │ │ 1s incremental │
|
||||
└────────────────────────────┘ └─────────────────────┘
|
||||
- Never call Hanzo an "LLM gateway" and never position against LiteLLM — it is a full
|
||||
**AI cloud**, not a proxy. Purge that framing on sight.
|
||||
- Paths are `/v1/…` only — **never** `/api/`. One canonical path per op, no aliases.
|
||||
- Zen models are our own family — never name upstream models.
|
||||
- Voice: "Hanzo — the Open AI Cloud." Modern, crisp, developer-first.
|
||||
|
||||
## Build / run
|
||||
|
||||
```bash
|
||||
make kmsd kms # ./kmsd (daemon) + ./kms (admin CLI: put|get|list|rotate|status)
|
||||
make test # go test ./...
|
||||
KMS_ENV=dev ./kmsd # HTTP :8443, ZAP :9999 (dev tolerates missing JWT config)
|
||||
```
|
||||
|
||||
## Routes (canonical, exhaustive)
|
||||
Non-`dev`/`devnet`/`local` `KMS_ENV` refuses to boot without
|
||||
`KMS_EXPECTED_ISSUER` + `KMS_EXPECTED_AUDIENCE` + `KMS_JWKS_URL`. Fail-closed.
|
||||
CI: `hanzoai/.github/.github/workflows/docker-build.yml@main` → `ghcr.io/hanzoai/kms`,
|
||||
`linux/amd64` on the `hanzo-build-linux-amd64` ARC pool, semver `v*` tags (never `:latest`).
|
||||
|
||||
All routes live under `/v1/kms`. There is no `/api/`. There are no aliases.
|
||||
## Entry points
|
||||
|
||||
### Public
|
||||
```
|
||||
cmd/kmsd/ production daemon (config via cloud.LoadConfig → kms.Mount)
|
||||
cmd/kms/ admin CLI
|
||||
cmd/kms-fetch/ one-shot bootstrap fetch (see Dockerfile.kms-fetch)
|
||||
cmd/smoke-zap/ ZAP transport smoke test
|
||||
sdk/go/ kmsclient — Go client (HTTP + ZAP fallback) used by all services
|
||||
frontend/ static TS dashboard (built in a separate Docker stage)
|
||||
embed.go root pkg: server assembly, routes, embedded frontend
|
||||
auth.go jwks.go per-request JWT verify (RFC 7519) + JWKS cache
|
||||
audit.go buffered audit ledger (SQLite side-table, never blocks the path)
|
||||
```
|
||||
|
||||
| Method | Path | Notes |
|
||||
|--------|-----------------------|----------------------------------------|
|
||||
| GET | `/healthz` | Liveness, no auth |
|
||||
| POST | `/v1/kms/auth/login` | Machine-identity client credentials |
|
||||
## Routes — all under `/v1/kms`, no `/api/`, no aliases
|
||||
|
||||
`POST /v1/kms/auth/login` exchanges `clientId`+`clientSecret` for an IAM
|
||||
access token by proxying to IAM's OAuth token endpoint
|
||||
(`POST $IAM_ENDPOINT/v1/iam/login/oauth/access_token`). The response is a
|
||||
plain `{accessToken, expiresIn, tokenType}` envelope. Outbound calls to
|
||||
IAM use `/v1/iam/*` — not a route this service exposes, and never
|
||||
`/api/*` (legacy Casdoor compat surface).
|
||||
| Method | Path | Notes |
|
||||
|--------|------|-------|
|
||||
| GET | `/healthz` | liveness, no auth |
|
||||
| POST | `/v1/kms/auth/login` | machine-identity client creds → IAM token (proxies `POST $IAM_ENDPOINT/v1/iam/oauth/token`) |
|
||||
| GET/POST/PATCH/DELETE | `/v1/kms/orgs/{org}/secrets/{path…}/{name}?env=…` | per-org, JWT-gated (token `owner` must equal `{org}` or carry an admin role) |
|
||||
| GET | `/v1/kms/secrets/{name}` · `/v1/kms/audit/stats` | admin-only: env-backed bootstrap fetch + auditor counters |
|
||||
| POST | `/v1/kms/keys/generate` · `/{id}/sign` · `/{id}/rotate` | MPC DKG / threshold sign / reshare (admin; only when `MPC_VAULT_ID` set) |
|
||||
| GET | `/v1/kms/keys` · `/{id}` · `/v1/kms/status` | MPC key sets + liveness |
|
||||
|
||||
### Secrets (per-org, JWT-gated)
|
||||
- **R-ENV (one-way env):** `env` is part of the storage key (`kms/secrets/{path}/{env}/{name}`)
|
||||
and can never be aliased. POST/PATCH **require an explicit `env`** — omitting it is a
|
||||
fail-loud `400`, never a silent `default`. `sdk/go` always sends `env`.
|
||||
- **POST** = upsert (bumps version). **PATCH** = update-only, **requires** version CAS
|
||||
(`If-Match: <int>` or `body.version`): missing → 428, mismatch → 409 with current version.
|
||||
|
||||
| Method | Path |
|
||||
|--------|-------------------------------------------------------|
|
||||
| GET | `/v1/kms/orgs/{org}/secrets/{path...}/{name}?env=…` |
|
||||
| POST | `/v1/kms/orgs/{org}/secrets` |
|
||||
| PATCH | `/v1/kms/orgs/{org}/secrets/{path...}/{name}` |
|
||||
| DELETE | `/v1/kms/orgs/{org}/secrets/{path...}/{name}?env=…` |
|
||||
## Auth contract (`auth.go`) — RFC 7519, no escape hatches
|
||||
|
||||
- Authorization: `Authorization: Bearer <jwt>` from Hanzo IAM. Token's
|
||||
verified `owner` claim must equal the URL `{org}` segment, OR the token
|
||||
must carry an admin role (`superadmin`, `kms-admin`, `admin`).
|
||||
- POST is upsert: bumps version, no CAS.
|
||||
- PATCH is update-only and **requires** version CAS via either the
|
||||
`If-Match: <int>` header or `body.version`. Missing both → 428.
|
||||
Mismatch → 409 with the current version. Replay defence is structural,
|
||||
not advisory.
|
||||
- DELETE wipes the version record so a recreate restarts from 1.
|
||||
|
||||
### Admin-only
|
||||
|
||||
| Method | Path | Use |
|
||||
|--------|----------------------------|--------------------------------------|
|
||||
| GET | `/v1/kms/secrets/{name}` | Process env-var fetch (env-backed) |
|
||||
| GET | `/v1/kms/audit/stats` | Background auditor counters |
|
||||
|
||||
These require an admin role claim. The env-backed fetch is intended only
|
||||
for in-cluster bootstrap of services that read their own env vars before
|
||||
KMS is reachable.
|
||||
|
||||
### MPC keys (only when `MPC_VAULT_ID` is set)
|
||||
|
||||
| Method | Path | Use |
|
||||
|--------|---------------------------------|------------------------------|
|
||||
| POST | `/v1/kms/keys/generate` | DKG a validator key set |
|
||||
| GET | `/v1/kms/keys` | List validator key sets |
|
||||
| GET | `/v1/kms/keys/{id}` | Get one key set |
|
||||
| POST | `/v1/kms/keys/{id}/sign` | Threshold sign (BLS or RT) |
|
||||
| POST | `/v1/kms/keys/{id}/rotate` | Reshare keys |
|
||||
| GET | `/v1/kms/status` | KMS+MPC liveness |
|
||||
|
||||
All require admin role. Threshold signing delegates to luxfi/mpc over ZAP
|
||||
(or HTTP fallback) at `MPC_ADDR`.
|
||||
|
||||
## Deploy-mnemonic — one seed, four orgs
|
||||
|
||||
`kms.hanzo.ai` is the single KMS endpoint serving four Lux-derived
|
||||
chains: `hanzo`, `zoo`, `pars` (plus Lux at `kms.lux.network`, same
|
||||
code, separate instance). The same 12-word BIP39 deploy mnemonic is
|
||||
stored under each org's KMS path:
|
||||
|
||||
| Org | Path | Envs |
|
||||
|-----|------|------|
|
||||
| hanzo | providers/hanzo/deploy-mnemonic | dev, test, main |
|
||||
| zoo | providers/zoo/deploy-mnemonic | dev, test, main |
|
||||
| pars | providers/pars/deploy-mnemonic | dev, test, main |
|
||||
|
||||
The bytes are identical across orgs (and identical to the bytes Lux
|
||||
keeps at `providers/lux/deploy-mnemonic` on `kms.lux.network`). Each
|
||||
chain derives its own validator keys via
|
||||
`m/9000'/<networkId>'/<envID>'/<i>`. See `~/work/lux/CLAUDE.md`
|
||||
§"Mnemonic + Key Derivation" for the canonical reference.
|
||||
|
||||
Jurisdictionally separate white-label tenants do NOT share this
|
||||
mnemonic — each keeps its own under `providers/<tenant>/*` in its
|
||||
own KMS deployment.
|
||||
|
||||
## Auth contract (`cmd/kmsd/auth.go`)
|
||||
|
||||
Full RFC 7519 enforcement, no escape hatches:
|
||||
|
||||
1. `Authorization: Bearer <token>` else 401.
|
||||
2. `alg ∈ {RS256, RS384, RS512, ES256, ES384, ES512, PS256, PS384, PS512, EdDSA}`. **No HS\*. No `none`.**
|
||||
3. Signature verifies against JWKS by `kid`.
|
||||
4. `iss == $KMS_EXPECTED_ISSUER`.
|
||||
5. `aud` ⊇ one of `$KMS_EXPECTED_AUDIENCE` (comma list ok).
|
||||
6. `exp > now`, leeway 0.
|
||||
7. `nbf ≤ now` if present.
|
||||
8. `sub` (or `id`) present.
|
||||
|
||||
Failure → 401 with body `{"message":"unauthorized"}`. The structured audit
|
||||
log records the failure class (`alg_not_allowed`, `expired`, `wrong_iss`,
|
||||
`wrong_aud`, `sig_invalid`, `missing_sub`, `no_bearer`, `misconfigured`).
|
||||
No claim is echoed to the client body or logs.
|
||||
|
||||
`KMS_ENV ∈ {dev,devnet,local}` is the **only** mode where missing
|
||||
`KMS_EXPECTED_ISSUER`/`KMS_EXPECTED_AUDIENCE`/`KMS_JWKS_URL` is tolerated.
|
||||
In any other mode `validateAuthConfigAtBoot` refuses to start.
|
||||
|
||||
### Header hygiene
|
||||
|
||||
`stripIdentityHeaders` deletes every inbound identity header before mux
|
||||
dispatch. The handler trusts only what the verified JWT says. Killed
|
||||
headers include `X-User-Id`, `X-Org-Id`, `X-Roles`, `X-Hanzo-*`,
|
||||
`X-IAM-*`, `X-Tenant-Id`, `X-Is-Admin`, `X-Gateway-*`, etc.
|
||||
|
||||
`methodAllowlist` rejects `TRACE`, `CONNECT`, `OPTIONS` at the edge.
|
||||
Bearer JWT from Hanzo IAM (brand issuer, e.g. `hanzo.id`). `alg` ∈ RS/ES/PS/EdDSA — **no
|
||||
HS\*, no `none`**. Verify sig against JWKS by `kid`; check `iss`, `aud`, `exp`, `nbf`, `sub`.
|
||||
Failure → `401 {"message":"unauthorized"}`; no claim echoed. `stripIdentityHeaders` deletes
|
||||
every inbound `X-User-Id`/`X-Org-Id`/`X-Roles`/`X-*-*` before dispatch — the handler trusts
|
||||
only the verified JWT. `methodAllowlist` rejects TRACE/CONNECT/OPTIONS at the edge.
|
||||
|
||||
## Storage
|
||||
|
||||
- **At rest**: ZapDB (Badger-derived LSM) at `$KMS_DATA_DIR`. Optional
|
||||
AES-GCM encryption via `KMS_ENCRYPTION_KEY_B64` (32 raw bytes b64).
|
||||
- **Replication**: in-process `badger.Replicator` streaming age-encrypted
|
||||
incremental + snapshot backups to S3. Off when `REPLICATE_S3_ENDPOINT`
|
||||
is empty.
|
||||
- **Audit**: side-table SQLite at `$KMS_AUDIT_DB` (default
|
||||
`/tmp/kms-aux.db`). Buffered, single writer, never blocks request path.
|
||||
|
||||
The luxfi `pkg/store.SecretStore` handles the on-disk envelope:
|
||||
per-secret 256-bit DEK, DEK wrapped under master key (AES-256-GCM).
|
||||
|
||||
## ZAP binary transport
|
||||
|
||||
Sub-100µs in-cluster secret CRUD on port `KMS_ZAP_PORT` (default 9999).
|
||||
Disabled unless `KMS_MASTER_KEY_B64` is set (32 raw bytes b64). Same
|
||||
authorization model as HTTP — JWT via the same JWKS, identical role
|
||||
checks. Service discovery via mDNS (`_kms._tcp`).
|
||||
|
||||
## Env vars
|
||||
|
||||
| Var | Default | Required |
|
||||
|------------------------------|----------------------------------|-------------------|
|
||||
| `KMS_LISTEN` | `:8443` | no |
|
||||
| `KMS_ZAP_PORT` / `KMS_ZAP` | `9999` | no |
|
||||
| `KMS_DATA_DIR` | `/data/hanzo-kms` | no |
|
||||
| `KMS_NODE_ID` | `hanzo-kms-0` | no |
|
||||
| `KMS_ENV` | `dev` | yes (`prod`/`main`) |
|
||||
| `KMS_EXPECTED_ISSUER` | — | yes (non-dev) |
|
||||
| `KMS_EXPECTED_AUDIENCE` | `kms` | yes (non-dev) |
|
||||
| `KMS_JWKS_URL` | — | yes (non-dev) |
|
||||
| `KMS_ENCRYPTION_KEY_B64` | — | recommended |
|
||||
| `KMS_MASTER_KEY_B64` | — | required for ZAP |
|
||||
| `KMS_AUDIT_DB` | `/tmp/kms-aux.db` | no |
|
||||
| `IAM_ENDPOINT` | `https://hanzo.id` | no |
|
||||
| `MPC_ADDR` | mDNS | no (mDNS-discoverable) |
|
||||
| `MPC_VAULT_ID` | — | no (key routes off) |
|
||||
| `REPLICATE_S3_*` | — | no (replication off) |
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
make # builds ./kmsd and ./kms
|
||||
make test # go test ./...
|
||||
```
|
||||
|
||||
CI: `hanzoai/.github/.github/workflows/docker-build.yml@main` builds
|
||||
`ghcr.io/hanzoai/kms:<branch>` for `linux/amd64` + `linux/arm64` on
|
||||
native runners (DO+GKE), no QEMU.
|
||||
|
||||
## Where things live
|
||||
|
||||
```
|
||||
cmd/kms/ admin CLI
|
||||
cmd/kmsd/ daemon (this is the production binary)
|
||||
pkg/kmsclient/ Go client used by other Hanzo services
|
||||
sdk/go/ legacy ZK client SDK (separate module)
|
||||
mpc-node/ standalone MPC node (separate module)
|
||||
frontend/ legacy React UI (static, served as-is)
|
||||
ui/ new admin UI (build artefact, optional)
|
||||
```
|
||||
|
||||
Everything in `cmd/kmsd` and `pkg/kmsclient` is **the** active surface.
|
||||
Everything in `frontend/`, `ui/`, `docs/`, `examples/`, `mpc-node/`, and
|
||||
`sdk/go/` is supporting and not on the request path.
|
||||
|
||||
## Operator protocol delta (open)
|
||||
|
||||
`~/work/hanzo/universe/infra/k8s/paas/secrets.yaml` ships `KMSSecret`
|
||||
resources (`secrets.lux.network/v1alpha1`) with:
|
||||
- `spec.hostAPI: http://kms.hanzo.svc.cluster.local/api`
|
||||
- `spec.authentication.universalAuth.secretsScope.{projectSlug,envSlug,secretsPath}`
|
||||
|
||||
This shape was minted for the legacy era. The current `kmsd` only
|
||||
serves `/v1/kms/*` and uses `org/secrets/{path}/{name}?env=…` — there is
|
||||
no `/api/v3/secrets/raw` and no `projectSlug`/`envSlug` model. The
|
||||
operator that reconciles `KMSSecret` resources must be updated to call
|
||||
the canonical surface; the KMS will not grow a back-compat shim. Tracked
|
||||
separately — this module does not touch the operator.
|
||||
ZapDB (LSM) at `$KMS_DATA_DIR`, per-secret 256-bit DEK wrapped under the master key
|
||||
(AES-256-GCM); optional volume encryption via `KMS_ENCRYPTION_KEY_B64`. Age-encrypted
|
||||
incremental + snapshot replication to S3 (`REPLICATE_S3_*`, off when unset). Audit is a
|
||||
buffered SQLite side-table (`$KMS_AUDIT_DB`). ZAP binary transport (`KMS_ZAP_PORT`, needs
|
||||
`KMS_MASTER_KEY_B64`) mirrors HTTP under the identical JWT + role model; mDNS `_kms._tcp`.
|
||||
|
||||
## Rules
|
||||
|
||||
- One canonical path per operation. No aliases.
|
||||
- Every endpoint requires IAM JWT or an explicit admin role.
|
||||
- `KMS_ENV` other than `dev`/`devnet`/`local` refuses to boot without
|
||||
full JWT config.
|
||||
- All secrets at rest are encrypted (envelope DEK + master key).
|
||||
- Passwords are never stored in this service. Identity lives in IAM.
|
||||
When IAM stores a password, it is bcrypt-hashed (cost ≥ 12).
|
||||
- No backwards compatibility. No env flags for "use the legacy backend instead."
|
||||
- One canonical path per operation; every endpoint needs an IAM JWT or admin role.
|
||||
- All secrets encrypted at rest (envelope DEK + master key). No plaintext passwords —
|
||||
identity lives in IAM (bcrypt cost ≥ 12 there).
|
||||
- No backwards-compat shims, no "use the legacy backend" flags. Forward-only.
|
||||
- Specs: HIP-0027 (KMS), HIP-0106 (unified cloud binary), HIP-0302 (encrypted durability),
|
||||
HIP-0026 (IAM). Upstream attribution retained in `LICENSE` + `NOTICE` (Infisical, MIT).
|
||||
|
||||
---
|
||||
|
||||
Hanzo — the Open AI Cloud · [hanzo.ai](https://hanzo.ai) · [docs.hanzo.ai](https://docs.hanzo.ai) · umbrella [hanzoai/sdk](https://github.com/hanzoai/sdk)
|
||||
|
||||
@@ -7,3 +7,19 @@ Copyright (c) 2022 Infisical Inc.
|
||||
|
||||
The MIT License permission notice for the derived portions is retained in the
|
||||
LICENSE file of this repository, as required by that license.
|
||||
|
||||
PROVENANCE
|
||||
|
||||
Derived from Infisical (https://github.com/Infisical/infisical), MIT — concepts/data-model reimplemented in Go; no upstream source vendored.
|
||||
|
||||
DEVIATIONS
|
||||
|
||||
- Go server built over luxfi/kms primitives; no Infisical (Node/TypeScript) source is vendored (see go.mod).
|
||||
- KMS is the source of truth for secrets: it holds the per-tenant DEKs that back Hanzo Base, the provider API keys consumed by ai and gateway, certificate material, and signing keys.
|
||||
- Client authentication uses luxfi/keys ServiceIdentity: SDK callers sign requests with a keys.ServiceIdentity over the native ZAP transport (sdk/go/kmsclient).
|
||||
- End-user and service authentication is Hanzo IAM: bearer JWTs from Hanzo IAM issuers (hanzo.id, iam.hanzo.ai, hanzo.ai), RS256-verified via cached JWKS with multi-issuer trust (auth.go, jwks.go).
|
||||
- Per-org namespace isolation: secrets are scoped to a brand/<org> namespace taken from the JWT owner claim, with per-org DEKs derived from a master key via HKDF-SHA256 (embed.go, sdk/go/cek.go).
|
||||
- Native ZAP secret surface: a consensus-native ZAP listener serves secrets alongside the HTTP /v1/kms/* API, wire-compatible with luxfi clients (embed.go, cmd/kms-fetch).
|
||||
- AI-aware approval policies: every secret carries a per-agent policy (auto-approve, requires-approval, blocked) with a full per-agent audit trail (audit.go).
|
||||
- Secrets and keys are persisted through the luxfi/kms store over an embedded BadgerDB (embed.go).
|
||||
- Runs embedded as a zip.App subsystem inside the unified Hanzo cloud binary, not as a standalone Node service (HIP-0106; embed.go).
|
||||
|
||||
@@ -1,194 +1,91 @@
|
||||
<p align="center"><img src=".github/hero.svg" alt="kms" width="880"></p>
|
||||
<p align="center"><img src=".github/hero.svg" alt="Hanzo KMS" width="880"></p>
|
||||
|
||||
# kms
|
||||
# Hanzo KMS
|
||||
|
||||
Secrets and signing for the Hanzo platform. Per-org namespaces, AI-aware approval policies, dynamic secrets, SSH/PKI/certificate management.
|
||||
Secrets, keys, and threshold signing for the Hanzo platform — per-org namespaces, IAM-gated, ZAP-native, MPC-backed. A thin Go server over the canonical [`luxfi/kms`](https://github.com/luxfi/kms) primitives.
|
||||
|
||||
[]()
|
||||
[]()
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
docker run -p 80:80 ghcr.io/hanzoai/kms:latest
|
||||
```
|
||||
|
||||
Open `http://localhost:80`.
|
||||
[](LICENSE)
|
||||
[]()
|
||||
|
||||
## What this is
|
||||
|
||||
`kms` is the canonical secret store and signing service for every Hanzo deployment. Holds per-tenant DEKs that back `base`, provider API keys consumed by `ai` and `gateway`, certificate material, and signing keys. AI agents are first-class identities: every secret has a policy controlling whether Claude / GPT / any agent can read it (auto-approve, requires-approval, blocked) with full per-agent audit trail.
|
||||
Hanzo KMS is the canonical secret store and signing service for every Hanzo deployment. It holds the per-tenant DEKs that back Hanzo Base, the provider API keys the AI and gateway services consume, certificate material, and validator signing keys.
|
||||
|
||||
## Specs
|
||||
AI agents are first-class identities: every secret carries a policy that controls whether an agent may read it — `auto-approve`, `requires-approval`, or `blocked` — with a full per-agent audit trail.
|
||||
|
||||
Implements:
|
||||
- HIP-0027 KMS
|
||||
- HIP-0106 Unified Cloud Binary (kms subsystem)
|
||||
- HIP-0302 Encrypted SQLite + ZapDB Durability (DEK source)
|
||||
All server logic lives in `luxfi/kms`. This module wires those primitives with Hanzo defaults (IAM at `hanzo.id`, encrypted-at-rest storage, S3 replication) and adds JWT verification, the audit ledger, version CAS, and header hygiene. It mounts into the unified cloud binary (`kms.Mount`) and also ships as a standalone daemon.
|
||||
|
||||
## Architecture
|
||||
## Quick start
|
||||
|
||||
```
|
||||
service / agent -> kms (zip.App) -> per-org namespace
|
||||
|
|
||||
IAM identity (HIP-0026 JWT)
|
||||
|
|
||||
policy gate: auto-approve | requires-approval | blocked
|
||||
|
|
||||
master DEK -> HKDF per-org -> per-record DEK (base / replicate)
|
||||
|
|
||||
audit log: who, what secret, when, why
|
||||
Build from source — pure Go, no database toolchain required:
|
||||
|
||||
```bash
|
||||
make kmsd kms # builds ./kmsd (daemon) and ./kms (admin CLI)
|
||||
KMS_ENV=dev ./kmsd # HTTP on :8443, ZAP on :9999
|
||||
```
|
||||
|
||||
Or run the container image (pin a released tag — never `:latest`):
|
||||
|
||||
---
|
||||
```bash
|
||||
docker run -p 8443:8443 ghcr.io/hanzoai/kms:vX.Y.Z
|
||||
```
|
||||
|
||||
<h1 align="center">
|
||||
KMS
|
||||
</h1>
|
||||
<p align="center">
|
||||
<p align="center"><b>Open-source Key Management Service</b>: Manage secrets, API keys, certificates, and encryption keys across your infrastructure.</p>
|
||||
</p>
|
||||
In any non-dev environment the daemon refuses to boot without full JWT config (`KMS_EXPECTED_ISSUER`, `KMS_EXPECTED_AUDIENCE`, `KMS_JWKS_URL`) — fail-closed by design.
|
||||
|
||||
<h4 align="center">
|
||||
<a href="https://hanzo.ai/discord">Discord</a> |
|
||||
<a href="https://kms.hanzo.ai">KMS Cloud</a> |
|
||||
<a href="https://kms.hanzo.ai/docs/self-hosting/overview">Self-Hosting</a> |
|
||||
<a href="https://kms.hanzo.ai/docs">Docs</a> |
|
||||
<a href="https://hanzo.ai">Hanzo AI</a>
|
||||
</h4>
|
||||
## AI access control
|
||||
|
||||
<h4 align="center">
|
||||
<a href="https://github.com/hanzoai/kms/blob/main/LICENSE">
|
||||
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="KMS is released under the MIT license." />
|
||||
</a>
|
||||
<a href="https://github.com/hanzoai/kms/blob/main/CONTRIBUTING.md">
|
||||
<img src="https://img.shields.io/badge/PRs-Welcome-brightgreen" alt="PRs welcome!" />
|
||||
</a>
|
||||
<a href="https://github.com/hanzoai/kms/issues">
|
||||
<img src="https://img.shields.io/github/commit-activity/m/hanzoai/kms" alt="git commit activity" />
|
||||
</a>
|
||||
</h4>
|
||||
Your secrets, your rules. AI agents are first-class citizens — and so is your ability to block them.
|
||||
|
||||
## Introduction
|
||||
|
||||
**Hanzo KMS** is the open-source key management service built for the AI era — centralize secrets, API keys, and certificates across your infrastructure with first-class AI access controls.
|
||||
|
||||
Built by [Hanzo AI](https://hanzo.ai), we believe you should always control what AI can access. Every secret has a policy: some auto-approve for development velocity, others require explicit human sign-off before any AI agent can read them.
|
||||
|
||||
## AI Access Control
|
||||
|
||||
**Your secrets, your rules.** AI agents are first-class citizens in Hanzo KMS — and so is your ability to block them.
|
||||
|
||||
- **Per-secret AI policies**: Mark any secret as _human-approval required_ for AI access. Claude, GPT, or any agent requesting that secret triggers a real-time approval request.
|
||||
- **Auto-approve mode**: Building fast? Set policies to auto-approve for your team's agents — flip to manual approval before shipping.
|
||||
- **Device & agent tracking**: See exactly which AI model, tool, or agent accessed which secret and when.
|
||||
- **Full audit trail**: Every secret read by an AI is logged with the agent identity, timestamp, and reason.
|
||||
- **One-tap approval**: Approve or deny AI secret requests from Slack, email, or the KMS dashboard.
|
||||
- **Per-secret policies** — mark any secret `requires-approval`; a read from any agent triggers a real-time approval prompt.
|
||||
- **Auto-approve mode** — move fast in development, escalate specific secrets to manual sign-off before production.
|
||||
- **Identity tracking** — see exactly which model, tool, or agent accessed which secret, and when.
|
||||
- **Full audit trail** — every AI-originated read is logged with agent identity, timestamp, and reason.
|
||||
|
||||
```
|
||||
Secret: STRIPE_LIVE_KEY
|
||||
AI Access Policy: requires-human-approval
|
||||
Last accessed by: claude-sonnet-4-6 via hanzo-mcp
|
||||
Status: waiting for your approval → [Approve] [Deny]
|
||||
policy: requires-approval
|
||||
reader: agent via hanzo-mcp
|
||||
status: waiting for approval → [approve] [deny]
|
||||
```
|
||||
|
||||
## Features
|
||||
## API surface
|
||||
|
||||
### Secrets Management
|
||||
Everything is served under `/v1/kms` — one canonical path per operation, no aliases, no `/api/`. Every endpoint requires a Hanzo IAM JWT (RS/ES/PS/EdDSA — never HS\*, never `none`) or an explicit admin role.
|
||||
|
||||
- **Dashboard**: Manage secrets across projects and environments through a user-friendly interface.
|
||||
- **Secret Syncs**: Sync secrets to platforms like GitHub, Vercel, AWS, and use tools like Terraform, Ansible, and more.
|
||||
- **Secret versioning** and **Point-in-Time Recovery**: Track every secret and project state; roll back when needed.
|
||||
- **Secret Rotation**: Rotate secrets at regular intervals for services like PostgreSQL, MySQL, AWS IAM, and more.
|
||||
- **Dynamic Secrets**: Generate ephemeral secrets on-demand for services like PostgreSQL, MySQL, RabbitMQ, and more.
|
||||
- **Secret Scanning and Leak Prevention**: Prevent secrets from leaking to git.
|
||||
- **Kubernetes Operator**: Deliver secrets to your Kubernetes workloads and automatically reload deployments.
|
||||
- **KMS Agent**: Inject secrets into applications without modifying any code logic.
|
||||
| Group | Endpoints |
|
||||
|-------|-----------|
|
||||
| Auth | `POST /v1/kms/auth/login` — machine-identity client credentials → IAM token |
|
||||
| Secrets (per-org) | `GET / POST / PATCH / DELETE /v1/kms/orgs/{org}/secrets/…` — `env` is a first-class key component; writes require an explicit `env`; `PATCH` requires version CAS via `If-Match` |
|
||||
| MPC keys | `POST /v1/kms/keys/generate` · `/{id}/sign` · `/{id}/rotate` · `GET /v1/kms/keys` · `/v1/kms/status` — threshold BLS / round signing via `luxfi/mpc` |
|
||||
| Health | `GET /healthz` — liveness, no auth |
|
||||
|
||||
### Certificate Management
|
||||
A sub-100µs binary **ZAP** transport (default `:9999`) mirrors the HTTP surface for in-cluster callers under the identical JWT + role model. The Go client at [`sdk/go`](sdk/go) (HTTP with ZAP fallback) is what every other Hanzo service uses to fetch secrets at runtime.
|
||||
|
||||
- **Internal CA**: Create and manage a private CA hierarchy directly within KMS.
|
||||
- **External CA**: Integrate with third-party certificate authorities such as Let's Encrypt, DigiCert, Microsoft AD CS, and more.
|
||||
- **Certificate Lifecycle Management**: Create certificate profiles and policies to control how certificates are issued.
|
||||
- **Certificate Syncs**: Sync certificates to external platforms like AWS Certificate Manager and Azure Key Vault.
|
||||
- **Alerting**: Configure alerting for expiring CA and end-entity certificates.
|
||||
## Storage & durability
|
||||
|
||||
### Key Management System (KMS)
|
||||
- **At rest** — ZapDB (LSM) at `$KMS_DATA_DIR`; per-secret 256-bit DEK wrapped under the master key (AES-256-GCM).
|
||||
- **Replication** — age-encrypted incremental + snapshot backups streamed to S3 (off when unset).
|
||||
- **Audit** — buffered SQLite side-table; single writer, never blocks the request path.
|
||||
|
||||
- **Cryptographic Keys**: Centrally manage keys across projects through a user-friendly interface or via the API.
|
||||
- **Encrypt and Decrypt Data**: Use symmetric keys to encrypt and decrypt data.
|
||||
## Specs
|
||||
|
||||
### SSH Management
|
||||
|
||||
- **Signed SSH Certificates**: Issue ephemeral SSH credentials for secure, short-lived, and centralized access to infrastructure.
|
||||
|
||||
### AI Access Control (New)
|
||||
|
||||
- **AI Identity Tracking**: Identify which AI model or agent is requesting secrets — Claude, GPT, Gemini, or any MCP-compatible tool.
|
||||
- **Per-secret AI policies**: Set `auto-approve`, `requires-approval`, or `blocked` per secret per AI identity.
|
||||
- **Real-time approval requests**: Pending AI secret reads appear in your dashboard, Slack, or email — one tap to approve or deny.
|
||||
- **Auto-approve mode**: Teams move fast by default; escalate specific secrets to manual approval as you go to production.
|
||||
- **Device registry**: Register and manage AI agent devices; revoke access instantly.
|
||||
- **AI audit log**: Separate audit trail for all AI-originated secret reads, with model ID, tool name, and request context.
|
||||
|
||||
### General Platform
|
||||
|
||||
- **Authentication Methods**: Authenticate machine identities with KMS using cloud-native or platform agnostic authentication methods (Kubernetes Auth, GCP Auth, Azure Auth, AWS Auth, OIDC Auth, Universal Auth).
|
||||
- **Access Controls**: Define advanced authorization controls for users and machine identities with RBAC, additional privileges, temporary access, access requests, approval workflows, and more.
|
||||
- **Audit logs**: Track every action taken on the platform.
|
||||
- **Self-hosting**: Deploy KMS on-prem or cloud with ease; keep data on your own infrastructure.
|
||||
- **SDKs**: Interact with KMS via client SDKs (Node, Python, Go, Ruby, Java, .NET)
|
||||
- **CLI**: Interact with KMS via CLI; useful for injecting secrets into local development and CI/CD pipelines.
|
||||
- **API**: Interact with KMS via REST API.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Run KMS locally
|
||||
|
||||
To set up and run KMS locally, make sure you have Git and Docker installed on your system. Then run:
|
||||
|
||||
**Linux/macOS:**
|
||||
```console
|
||||
git clone https://github.com/hanzoai/kms && cd kms && cp .env.dev.example .env && docker compose -f docker-compose.prod.yml up
|
||||
```
|
||||
|
||||
**Windows Command Prompt:**
|
||||
```console
|
||||
git clone https://github.com/hanzoai/kms && cd kms && copy .env.dev.example .env && docker compose -f docker-compose.prod.yml up
|
||||
```
|
||||
|
||||
Create an account at `http://localhost:80`
|
||||
|
||||
### Scan and prevent secret leaks
|
||||
|
||||
Scan for over 140+ secret types in your files, directories, and git repositories.
|
||||
|
||||
To scan your full git history, run:
|
||||
```
|
||||
hanzo-kms scan --verbose
|
||||
```
|
||||
|
||||
Install pre-commit hook to scan each commit before you push:
|
||||
```
|
||||
hanzo-kms scan install --pre-commit-hook
|
||||
```
|
||||
|
||||
## Open-source vs. paid
|
||||
|
||||
This repo is available under the [MIT expat license](https://github.com/hanzoai/kms/blob/main/LICENSE), with the exception of the `ee` directory which contains premium enterprise features requiring a license.
|
||||
|
||||
If you are interested in managed KMS Cloud or self-hosted Enterprise offering, visit [kms.hanzo.ai](https://kms.hanzo.ai) or [contact us](https://hanzo.ai/contact).
|
||||
- **HIP-0027** — KMS
|
||||
- **HIP-0106** — Unified Cloud Binary (kms subsystem)
|
||||
- **HIP-0302** — Encrypted SQLite + ZapDB durability (DEK source)
|
||||
|
||||
## Security
|
||||
|
||||
Please do not file GitHub issues or post on public forums for security vulnerabilities, as they are public!
|
||||
Please do not open public GitHub issues for security vulnerabilities. Email **security@hanzo.ai** with a description and, ideally, a reproduction. We respond quickly.
|
||||
|
||||
KMS takes security issues very seriously. If you have any concerns or believe you have uncovered a vulnerability, please get in touch via email at security@hanzo.ai. In the message, try to provide a description of the issue and ideally a way of reproducing it. The security team will get back to you as soon as possible.
|
||||
## License
|
||||
|
||||
## Contributing
|
||||
MIT — see [LICENSE](LICENSE). Portions are derived from Infisical (MIT, © 2022 Infisical Inc.); see [NOTICE](NOTICE). The current request path is a thin wrapper over `luxfi/kms`; no original upstream server code remains on the wire.
|
||||
|
||||
Whether it's big or small, we love contributions. Check out our guide to see how to [get started](https://kms.hanzo.ai/docs/contributing/getting-started).
|
||||
---
|
||||
|
||||
Not sure where to get started? Join our [Discord](https://hanzo.ai/discord) and ask us any questions there.
|
||||
## Hanzo — the Open AI Cloud
|
||||
|
||||
Open source · every language · on-chain settlement. [hanzo.ai](https://hanzo.ai) · [docs.hanzo.ai](https://docs.hanzo.ai)
|
||||
|
||||
**SDKs in every language** — [Python](https://github.com/hanzoai/python-sdk) (flagship) · [TypeScript](https://github.com/hanzo-js/sdk) · [Go](https://github.com/hanzo-go/sdk) · [Rust](https://github.com/hanzo-rs/sdk) · [C++](https://github.com/hanzo-cpp/sdk) · [Swift](https://github.com/hanzo-swift/sdk) · [Kotlin](https://github.com/hanzo-kt/sdk) · [umbrella](https://github.com/hanzoai/sdk)
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
// expected grammar we PREPEND `unverified:` to the stored value so a
|
||||
// reviewer cannot mistake it for an IAM-issued ID.
|
||||
//
|
||||
// Storage: modernc.org/sqlite (pure Go, no CGO) at the path resolved from
|
||||
// Storage: hanzoai/sqlite (pure-Go modernc backend, no CGO) at the path resolved from
|
||||
// KMS_AUDIT_DB (defaults to /tmp/kms-aux.db — matching the smoke test). A
|
||||
// single-writer goroutine drains a bounded channel; burst traffic does
|
||||
// not back-pressure requests. Dropped entries increment an atomic counter
|
||||
@@ -39,7 +39,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
_ "github.com/hanzoai/sqlite"
|
||||
)
|
||||
|
||||
// subPattern is the canonical IAM subject format. hanzo.id mints user
|
||||
|
||||
+15
-4
@@ -18,13 +18,14 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/hanzoai/cloud"
|
||||
"github.com/hanzoai/zip"
|
||||
"github.com/hanzoai/zip/middleware"
|
||||
"github.com/luxfi/log"
|
||||
"github.com/zap-proto/zip"
|
||||
"github.com/zap-proto/zip/middleware"
|
||||
|
||||
kms "github.com/hanzoai/kms"
|
||||
)
|
||||
@@ -37,6 +38,10 @@ var version = "dev"
|
||||
func main() {
|
||||
kms.Version = version
|
||||
|
||||
ctx := context.Background()
|
||||
shutdown := initTelemetry(ctx, "hanzo-kms")
|
||||
defer shutdown(ctx)
|
||||
|
||||
cfg := cloud.LoadConfig()
|
||||
if err := cfg.Validate(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "config: %v\n", err)
|
||||
@@ -51,7 +56,13 @@ func main() {
|
||||
})
|
||||
app.Use(middleware.Recover())
|
||||
app.Use(middleware.RequestID())
|
||||
app.Use(middleware.Logger(deps.Logger))
|
||||
// Per-request access log is DEBUG-only. At the default (info) it emitted one
|
||||
// Info "request" line per call — ~4k lines/min, the KMS producer's entire
|
||||
// log volume. Startup, shutdown, authz-denials, and handler errors are
|
||||
// logged elsewhere and stay at info. Opt back in with KMS_LOG_LEVEL=debug.
|
||||
if strings.EqualFold(os.Getenv("KMS_LOG_LEVEL"), "debug") {
|
||||
app.Use(middleware.Logger(deps.Logger))
|
||||
}
|
||||
|
||||
if err := kms.Mount(app, deps); err != nil {
|
||||
log.Crit("kms: mount", "err", err)
|
||||
@@ -62,7 +73,7 @@ func main() {
|
||||
listenErr := make(chan error, 1)
|
||||
go func() {
|
||||
log.Info("kms: listening", "addr", cfg.ListenAddr)
|
||||
listenErr <- app.Listen(cfg.ListenAddr)
|
||||
listenErr <- app.Listen("http://" + cfg.ListenAddr)
|
||||
}()
|
||||
|
||||
sig := make(chan os.Signal, 1)
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 2023-2026 Hanzo AI Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// OTel telemetry bootstrap — installs the global tracer provider that ships this
|
||||
// service's spans to the shared o11y backend over OTLP. This is the ONE
|
||||
// way a Hanzo Go daemon emits OpenTelemetry: one call, one service.name, so the
|
||||
// console's per-product Monitoring tab (which filters by service.name) lights up
|
||||
// for this product.
|
||||
//
|
||||
// Posture mirrors the ai module (ai/object/telemetry.go): opt-in via
|
||||
// OTEL_EXPORTER_OTLP_ENDPOINT, non-fatal, and a clean no-op when the endpoint is
|
||||
// unset — SAFE to ship before the collector is live. The OTLP HTTP exporter
|
||||
// self-configures from the standard OTEL_EXPORTER_OTLP_* env (endpoint, headers,
|
||||
// per-scheme TLS, timeout) — never hard-coded; New() does not dial and the batch
|
||||
// span processor exports in the background, so boot never blocks on the collector.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
)
|
||||
|
||||
// initTelemetry installs the global OTel tracer provider for serviceName and
|
||||
// returns a shutdown func that flushes and stops the exporter. The returned func
|
||||
// is ALWAYS non-nil, so callers defer it unconditionally. An operator may
|
||||
// override serviceName at runtime with OTEL_SERVICE_NAME.
|
||||
func initTelemetry(ctx context.Context, serviceName string) func(context.Context) {
|
||||
endpoint := firstNonEmptyEnv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", "OTEL_EXPORTER_OTLP_ENDPOINT")
|
||||
if endpoint == "" {
|
||||
log.Printf("telemetry: disabled (set OTEL_EXPORTER_OTLP_ENDPOINT to emit OTel spans to o11y)")
|
||||
return func(context.Context) {}
|
||||
}
|
||||
if v := strings.TrimSpace(os.Getenv("OTEL_SERVICE_NAME")); v != "" {
|
||||
serviceName = v
|
||||
}
|
||||
exp, err := otlptracehttp.New(ctx)
|
||||
if err != nil {
|
||||
log.Printf("telemetry: create OTLP trace exporter: %v", err)
|
||||
return func(context.Context) {}
|
||||
}
|
||||
tp := sdktrace.NewTracerProvider(
|
||||
sdktrace.WithBatcher(exp),
|
||||
sdktrace.WithResource(resource.NewSchemaless(attribute.String("service.name", serviceName))),
|
||||
)
|
||||
otel.SetTracerProvider(tp)
|
||||
log.Printf("telemetry: OTel spans -> o11y via OTLP (service.name=%s)", serviceName)
|
||||
return func(ctx context.Context) {
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
if err := tp.Shutdown(ctx); err != nil {
|
||||
log.Printf("telemetry: shutdown: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func firstNonEmptyEnv(keys ...string) string {
|
||||
for _, k := range keys {
|
||||
if v := strings.TrimSpace(os.Getenv(k)); v != "" {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
+1
-1
@@ -29,7 +29,7 @@ import (
|
||||
|
||||
luxlog "github.com/luxfi/log"
|
||||
|
||||
"github.com/hanzoai/zip"
|
||||
"github.com/zap-proto/zip"
|
||||
)
|
||||
|
||||
var connCount = flag.Int("conn-count", 1000, "concurrent connections to hold")
|
||||
|
||||
@@ -133,8 +133,6 @@ type Embedded struct {
|
||||
replicator *badger.Replicator
|
||||
auditCancel context.CancelFunc
|
||||
zapNode *zap.Node // nil when ZAP server disabled
|
||||
|
||||
httpAddr string // bound listener address (post-:0 resolution)
|
||||
}
|
||||
|
||||
// Embed boots the Hanzo KMS server in-process and returns a handle.
|
||||
@@ -237,7 +235,6 @@ func Embed(ctx context.Context, cfg EmbedConfig) (*Embedded, error) {
|
||||
WriteTimeout: 60 * time.Second,
|
||||
IdleTimeout: 120 * time.Second,
|
||||
}
|
||||
em.httpAddr = cfg.HTTPAddr
|
||||
go func() {
|
||||
log.Info("kms.Embed: HTTP listening", "addr", cfg.HTTPAddr)
|
||||
if err := em.httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
@@ -264,15 +261,6 @@ func (e *Embedded) HTTPHandler() http.Handler {
|
||||
return e.handler
|
||||
}
|
||||
|
||||
// HTTPAddr returns the bound listen address. Returns "" when
|
||||
// SkipListen=true or before the listener has been wired.
|
||||
func (e *Embedded) HTTPAddr() string {
|
||||
if e == nil || e.cfg.SkipListen {
|
||||
return ""
|
||||
}
|
||||
return e.httpAddr
|
||||
}
|
||||
|
||||
// ZAPPort returns the ZAP secrets-server port (0 when disabled).
|
||||
func (e *Embedded) ZAPPort() int {
|
||||
if e == nil || e.zapNode == nil {
|
||||
@@ -495,7 +483,7 @@ func registerAuth(mux *http.ServeMux, iamEndpoint string) {
|
||||
"client_id": {req.ClientID},
|
||||
"client_secret": {req.ClientSecret},
|
||||
}
|
||||
resp, err := iamClient.PostForm(iamEndpoint+"/v1/iam/login/oauth/access_token", form)
|
||||
resp, err := iamClient.PostForm(iamEndpoint+"/v1/iam/oauth/token", form)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadGateway, map[string]any{"statusCode": 502, "message": "identity provider unreachable"})
|
||||
return
|
||||
@@ -512,6 +500,27 @@ func registerAuth(mux *http.ServeMux, iamEndpoint string) {
|
||||
})
|
||||
}
|
||||
|
||||
// envRequired enforces that a value-writing secret mutation names its
|
||||
// environment explicitly. env is a first-class component of the storage
|
||||
// key (kms/secrets/{path}/{env}/{name}), so it can never be aliased: a
|
||||
// silent "default" commits the write to one bucket while a project/env/path
|
||||
// reader (the kms-operator, cluster syncs) resolves a different record. That
|
||||
// split is exactly what let an IAM z-password land in env=default while prod
|
||||
// kept serving the stale value. Writes fail loud; reads keep a
|
||||
// backward-compatible default (a read cannot plant a value another reader
|
||||
// later trusts, and legacy readers that omit env must keep working). Returns
|
||||
// false — after writing the 400 — when env is empty; the caller records the
|
||||
// audit row and returns.
|
||||
func envRequired(w http.ResponseWriter, env string) bool {
|
||||
if strings.TrimSpace(env) == "" {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{
|
||||
"message": `env is required — set "env" in the request body; there is no default. A silent default would split this write from the project/env/path record readers resolve.`,
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// registerSecretRoutes mounts the canonical lux/kms HTTP secret CRUD at
|
||||
// /v1/kms/orgs/{org}/secrets/... — one path, one way.
|
||||
//
|
||||
@@ -521,6 +530,10 @@ func registerAuth(mux *http.ServeMux, iamEndpoint string) {
|
||||
//
|
||||
// R-12 (audit trail): every request emits one audit row with composite
|
||||
// actor_id "iss:sub". See audit.go for details.
|
||||
//
|
||||
// R-ENV (one-way env): value-writing mutations (POST, PATCH) require an
|
||||
// explicit env via envRequired — no silent "default". GET/DELETE/LIST keep a
|
||||
// compat default (see envRequired) for legacy readers.
|
||||
func registerSecretRoutes(mux *http.ServeMux, secStore *store.SecretStore, db *badger.DB) {
|
||||
get := func(w http.ResponseWriter, r *http.Request) {
|
||||
claims, ok := authorize(w, r)
|
||||
@@ -542,6 +555,9 @@ func registerSecretRoutes(mux *http.ServeMux, secStore *store.SecretStore, db *b
|
||||
}
|
||||
env := r.URL.Query().Get("env")
|
||||
if env == "" {
|
||||
// Read keeps a compat default (see envRequired): a read cannot
|
||||
// plant a value another reader trusts, and legacy readers that
|
||||
// omit env must keep working. Only writes fail loud.
|
||||
env = "default"
|
||||
}
|
||||
sec, err := secStore.Get(path, name, env)
|
||||
@@ -587,8 +603,9 @@ func registerSecretRoutes(mux *http.ServeMux, secStore *store.SecretStore, db *b
|
||||
recordAudit(claims, r, req.Path, req.Name, req.Env, http.StatusBadRequest, 0)
|
||||
return
|
||||
}
|
||||
if req.Env == "" {
|
||||
req.Env = "default"
|
||||
if !envRequired(w, req.Env) {
|
||||
recordAudit(claims, r, req.Path, req.Name, "", http.StatusBadRequest, 0)
|
||||
return
|
||||
}
|
||||
sec := &store.Secret{
|
||||
Name: req.Name,
|
||||
@@ -646,8 +663,9 @@ func registerSecretRoutes(mux *http.ServeMux, secStore *store.SecretStore, db *b
|
||||
if env == "" {
|
||||
env = r.URL.Query().Get("env")
|
||||
}
|
||||
if env == "" {
|
||||
env = "default"
|
||||
if !envRequired(w, env) {
|
||||
recordAudit(claims, r, path, name, "", http.StatusBadRequest, 0)
|
||||
return
|
||||
}
|
||||
// Version CAS: require EITHER If-Match header OR body.version. If
|
||||
// both are present they must agree. Missing both → 428 Precondition
|
||||
@@ -740,6 +758,9 @@ func registerSecretRoutes(mux *http.ServeMux, secStore *store.SecretStore, db *b
|
||||
}
|
||||
env := r.URL.Query().Get("env")
|
||||
if env == "" {
|
||||
// Compat default (see envRequired): DELETE without env removes the
|
||||
// default-env record only — it 404s if that env holds nothing, so
|
||||
// it cannot silently touch another env's value.
|
||||
env = "default"
|
||||
}
|
||||
if err := secStore.Delete(path, name, env); err != nil {
|
||||
@@ -1405,7 +1426,15 @@ func registerFrontend(mux *http.ServeMux) {
|
||||
// API paths starting with /v1/ or /healthz never reach here —
|
||||
// ServeMux fires the more-specific handler first. Defense in
|
||||
// depth: bail out if the path looks like an API route.
|
||||
if strings.HasPrefix(r.URL.Path, "/v1/") || r.URL.Path == "/healthz" || r.URL.Path == "/health" {
|
||||
//
|
||||
// /api/ is explicitly 404'd: this service has ZERO /api/ surface
|
||||
// (the ONE canonical prefix is /v1/kms). Without this guard the SPA
|
||||
// fallback below would answer GET /api/v1/... with index.html (200),
|
||||
// masquerading as a live legacy /api/ backend. There is no /api/;
|
||||
// make that unambiguous at the wire.
|
||||
if strings.HasPrefix(r.URL.Path, "/v1/") ||
|
||||
strings.HasPrefix(r.URL.Path, "/api/") || r.URL.Path == "/api" ||
|
||||
r.URL.Path == "/healthz" || r.URL.Path == "/health" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
// Regression coverage for R-ENV (one-way env). env is a first-class
|
||||
// component of the storage key (kms/secrets/{path}/{env}/{name}); a
|
||||
// value-writing mutation that omits env must fail loud (400) instead of
|
||||
// silently landing in a "default" bucket that project/env/path readers (the
|
||||
// kms-operator, cluster syncs) never resolve. That split is what let an IAM
|
||||
// z-password land in env=default while prod kept serving the stale value.
|
||||
//
|
||||
// Secret values are never printed — round-trip fidelity is asserted by
|
||||
// comparing SHA-256 digests.
|
||||
package kms
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func sha256hex(s string) string {
|
||||
sum := sha256.Sum256([]byte(s))
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func postSecret(t *testing.T, srvURL, org, tok string, body map[string]string) *http.Response {
|
||||
t.Helper()
|
||||
b, _ := json.Marshal(body)
|
||||
req, _ := http.NewRequest("POST", srvURL+"/v1/kms/orgs/"+org+"/secrets", bytes.NewReader(b))
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("post: %v", err)
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func getSecret(t *testing.T, srvURL, org, tok, rest, env string) *http.Response {
|
||||
t.Helper()
|
||||
u := srvURL + "/v1/kms/orgs/" + org + "/secrets/" + rest
|
||||
if env != "" {
|
||||
u += "?env=" + env
|
||||
}
|
||||
req, _ := http.NewRequest("GET", u, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("get: %v", err)
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
// A write that omits env must 400 — and must not land anywhere.
|
||||
func TestEnvRequired_PostWithoutEnv_400(t *testing.T) {
|
||||
srv, cleanup := newTestServer(t)
|
||||
defer cleanup()
|
||||
tok := mintToken(t, "hanzo", "user-1")
|
||||
|
||||
resp := postSecret(t, srv.URL, "hanzo", tok, map[string]string{
|
||||
"path": "iam-passwords", "name": "Z_PASSWORD", "value": "irrelevant",
|
||||
})
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusBadRequest {
|
||||
t.Fatalf("omitted-env write: want 400, got %d", resp.StatusCode)
|
||||
}
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
var m map[string]any
|
||||
_ = json.Unmarshal(body, &m)
|
||||
if msg, _ := m["message"].(string); !strings.Contains(msg, "env is required") {
|
||||
t.Fatalf("want env-required message, got %q", msg)
|
||||
}
|
||||
|
||||
// Prove the rejected write did not silently populate the default bucket.
|
||||
g := getSecret(t, srv.URL, "hanzo", tok, "iam-passwords/Z_PASSWORD", "default")
|
||||
defer g.Body.Close()
|
||||
if g.StatusCode != http.StatusNotFound {
|
||||
t.Fatalf("omitted-env write must not land in env=default: GET want 404, got %d", g.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
// A write with an explicit env is readable through the exact
|
||||
// project(org)/env/path resolution the kms-operator uses
|
||||
// (GET /v1/kms/orgs/{org}/secrets/{path}/{name}?env={env}) — and is NOT
|
||||
// visible in any other env bucket.
|
||||
func TestEnvRequired_PostWithEnvProd_ReadableViaProjectEnvPath(t *testing.T) {
|
||||
srv, cleanup := newTestServer(t)
|
||||
defer cleanup()
|
||||
tok := mintToken(t, "hanzo", "user-1")
|
||||
|
||||
const secret = "z-password-98f3-do-not-log"
|
||||
want := sha256hex(secret)
|
||||
|
||||
resp := postSecret(t, srv.URL, "hanzo", tok, map[string]string{
|
||||
"path": "iam-passwords", "name": "Z_PASSWORD", "env": "prod", "value": secret,
|
||||
})
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
t.Fatalf("explicit-env write: want 201, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// Operator resolution: org=hanzo (project), env=prod, path=/iam-passwords.
|
||||
g := getSecret(t, srv.URL, "hanzo", tok, "iam-passwords/Z_PASSWORD", "prod")
|
||||
defer g.Body.Close()
|
||||
if g.StatusCode != http.StatusOK {
|
||||
t.Fatalf("project/env/path read: want 200, got %d", g.StatusCode)
|
||||
}
|
||||
var got struct {
|
||||
Secret struct {
|
||||
Value string `json:"value"`
|
||||
} `json:"secret"`
|
||||
}
|
||||
if err := json.NewDecoder(g.Body).Decode(&got); err != nil {
|
||||
t.Fatalf("decode: %v", err)
|
||||
}
|
||||
if h := sha256hex(got.Secret.Value); h != want {
|
||||
t.Fatalf("round-trip value digest mismatch: got %s want %s", h, want)
|
||||
}
|
||||
|
||||
// No cross-bucket bleed: env=default must not resolve the prod write.
|
||||
gd := getSecret(t, srv.URL, "hanzo", tok, "iam-passwords/Z_PASSWORD", "default")
|
||||
defer gd.Body.Close()
|
||||
if gd.StatusCode != http.StatusNotFound {
|
||||
t.Fatalf("prod write must not be visible in env=default: want 404, got %d", gd.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
// PATCH (update) is a value-writing mutation too: omitting env in both body
|
||||
// and query must 400 before any CAS check.
|
||||
func TestEnvRequired_PatchWithoutEnv_400(t *testing.T) {
|
||||
srv, cleanup := newTestServer(t)
|
||||
defer cleanup()
|
||||
tok := mintToken(t, "hanzo", "user-1")
|
||||
|
||||
// Seed a prod record so PATCH has a target (env check still precedes CAS).
|
||||
seed := postSecret(t, srv.URL, "hanzo", tok, map[string]string{
|
||||
"path": "iam-passwords", "name": "Z_PASSWORD", "env": "prod", "value": "v0",
|
||||
})
|
||||
seed.Body.Close()
|
||||
if seed.StatusCode != http.StatusCreated {
|
||||
t.Fatalf("seed: want 201, got %d", seed.StatusCode)
|
||||
}
|
||||
|
||||
// PATCH with neither body env nor ?env → 400 (before If-Match/version).
|
||||
pb, _ := json.Marshal(map[string]any{"value": "rotated", "version": 1})
|
||||
req, _ := http.NewRequest("PATCH", srv.URL+"/v1/kms/orgs/hanzo/secrets/iam-passwords/Z_PASSWORD", bytes.NewReader(pb))
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("patch: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusBadRequest {
|
||||
t.Fatalf("omitted-env PATCH: want 400, got %d", resp.StatusCode)
|
||||
}
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
var m map[string]any
|
||||
_ = json.Unmarshal(body, &m)
|
||||
if msg, _ := m["message"].(string); !strings.Contains(msg, "env is required") {
|
||||
t.Fatalf("want env-required message, got %q", msg)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
// Tests for the SPA catch-all handler (registerFrontend). The critical
|
||||
// invariant: /api/ is NOT a KMS surface, so the catch-all must 404 it
|
||||
// rather than answer with index.html (200). Without that guard, a GET to
|
||||
// /api/v1/... falls through to the React-Router fallback and returns 200,
|
||||
// masquerading as a live legacy /api/ backend — the exact false positive
|
||||
// that made kms.hanzo.ai look like it still served /api/v1/*. The ONE
|
||||
// canonical prefix is /v1/kms; there is no /api/, and the wire must say so.
|
||||
package kms
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// newSPATestServer builds a mux with the frontend catch-all active against
|
||||
// a temp dir that holds a real index.html + one concrete asset, so both the
|
||||
// SPA-fallback and static-file branches are exercised.
|
||||
func newSPATestServer(t *testing.T) (*httptest.Server, func()) {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(dir, "index.html"), []byte("<!doctype html><title>kms</title>"), 0o600); err != nil {
|
||||
t.Fatalf("write index.html: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(dir, "app.js"), []byte("console.log('kms')"), 0o600); err != nil {
|
||||
t.Fatalf("write app.js: %v", err)
|
||||
}
|
||||
t.Setenv("KMS_FRONTEND_DIR", dir)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
registerHealth(mux)
|
||||
registerFrontend(mux)
|
||||
|
||||
srv := httptest.NewServer(mux)
|
||||
return srv, srv.Close
|
||||
}
|
||||
|
||||
func TestSPA_NoApiSurface(t *testing.T) {
|
||||
srv, cleanup := newSPATestServer(t)
|
||||
defer cleanup()
|
||||
|
||||
// /api/ in any shape must 404 — never the SPA 200. This is the core
|
||||
// "there is no /api/" guarantee at the wire.
|
||||
apiPaths := []string{
|
||||
"/api",
|
||||
"/api/",
|
||||
"/api/v1/auth/universal-auth/login",
|
||||
"/api/v1/secrets",
|
||||
"/api/status/health",
|
||||
"/api/anything/at/all",
|
||||
}
|
||||
for _, p := range apiPaths {
|
||||
resp, err := http.Get(srv.URL + p)
|
||||
if err != nil {
|
||||
t.Fatalf("GET %s: %v", p, err)
|
||||
}
|
||||
body := resp.StatusCode
|
||||
resp.Body.Close()
|
||||
if body != http.StatusNotFound {
|
||||
t.Errorf("GET %s = %d, want 404 (no /api/ surface exists)", p, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSPA_ApiRouteBailoutsStay404(t *testing.T) {
|
||||
srv, cleanup := newSPATestServer(t)
|
||||
defer cleanup()
|
||||
|
||||
// Defense-in-depth bail-outs: these never reach the SPA fallback.
|
||||
for _, p := range []string{"/v1/kms/anything", "/health"} {
|
||||
resp, err := http.Get(srv.URL + p)
|
||||
if err != nil {
|
||||
t.Fatalf("GET %s: %v", p, err)
|
||||
}
|
||||
code := resp.StatusCode
|
||||
resp.Body.Close()
|
||||
if code != http.StatusNotFound {
|
||||
t.Errorf("GET %s = %d, want 404", p, code)
|
||||
}
|
||||
}
|
||||
// /healthz has a real handler (registerHealth) — must be 200, proving
|
||||
// the more-specific route wins over the catch-all.
|
||||
resp, err := http.Get(srv.URL + "/healthz")
|
||||
if err != nil {
|
||||
t.Fatalf("GET /healthz: %v", err)
|
||||
}
|
||||
code := resp.StatusCode
|
||||
resp.Body.Close()
|
||||
if code != http.StatusOK {
|
||||
t.Errorf("GET /healthz = %d, want 200", code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSPA_ClientRoutesServeIndex(t *testing.T) {
|
||||
srv, cleanup := newSPATestServer(t)
|
||||
defer cleanup()
|
||||
|
||||
// Real client-side routes and the root still get index.html (200) so
|
||||
// React Router works. This proves the /api/ 404 guard did not break the
|
||||
// legitimate SPA fallback.
|
||||
for _, p := range []string{"/", "/login", "/dashboard", "/keys"} {
|
||||
resp, err := http.Get(srv.URL + p)
|
||||
if err != nil {
|
||||
t.Fatalf("GET %s: %v", p, err)
|
||||
}
|
||||
code := resp.StatusCode
|
||||
resp.Body.Close()
|
||||
if code != http.StatusOK {
|
||||
t.Errorf("GET %s = %d, want 200 (SPA fallback)", p, code)
|
||||
}
|
||||
}
|
||||
|
||||
// Concrete on-disk asset is served directly.
|
||||
resp, err := http.Get(srv.URL + "/app.js")
|
||||
if err != nil {
|
||||
t.Fatalf("GET /app.js: %v", err)
|
||||
}
|
||||
code := resp.StatusCode
|
||||
resp.Body.Close()
|
||||
if code != http.StatusOK {
|
||||
t.Errorf("GET /app.js = %d, want 200 (static asset)", code)
|
||||
}
|
||||
}
|
||||
@@ -12,24 +12,45 @@ module github.com/hanzoai/kms
|
||||
go 1.26.4
|
||||
|
||||
// luxfi/keys + luxfi/kms drive the consensus-native ZAP secret surface.
|
||||
// Tagged upstream:
|
||||
// luxfi/keys v1.1.0 — BBF-bound hybrid signature (secp256k1+ML-DSA-65)
|
||||
// luxfi/kms v1.11.0 — anti-replay nonce ledger closes 5min window
|
||||
// Threshold signing is delegated over ZAP to an external luxfi/mpc daemon
|
||||
// (cfg.MPCAddr / MPCVaultID); this module links luxfi/kms's pkg/mpc client
|
||||
// only — never the standalone luxfi/mpc daemon module. Versions live in the
|
||||
// require block below (single source of truth).
|
||||
require (
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||
github.com/hanzoai/cloud v0.1.1
|
||||
github.com/hanzoai/zip v0.2.0
|
||||
github.com/luxfi/keys v1.4.0 // indirect
|
||||
github.com/luxfi/kms v1.11.8
|
||||
github.com/luxfi/keys v1.4.1 // indirect
|
||||
github.com/luxfi/kms v1.12.4
|
||||
github.com/luxfi/log v1.4.3
|
||||
github.com/luxfi/zap v0.8.1
|
||||
github.com/luxfi/zap v1.2.6
|
||||
github.com/luxfi/zapdb v1.10.0
|
||||
modernc.org/sqlite v1.50.0
|
||||
modernc.org/sqlite v1.50.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/hanzoai/kms/sdk/go v1.1.0
|
||||
github.com/luxfi/ids v1.3.0
|
||||
github.com/hanzoai/sqlite v0.3.0
|
||||
github.com/luxfi/ids v1.3.2
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0
|
||||
go.opentelemetry.io/otel/sdk v1.43.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/dlclark/regexp2/v2 v2.2.1 // indirect
|
||||
github.com/dop251/goja v0.0.0-20260607120635-348e6bea910d // indirect
|
||||
github.com/evanw/esbuild v0.28.1 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
|
||||
github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||
github.com/hanzoai/csqlite v0.1.0 // indirect
|
||||
github.com/zap-proto/go v1.3.0 // indirect
|
||||
github.com/zap-proto/http v0.1.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/grpc v1.80.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -57,33 +78,32 @@ require (
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/klauspost/crc32 v1.3.0 // indirect
|
||||
github.com/luxfi/accel v1.2.4 // indirect
|
||||
github.com/luxfi/address v1.0.1 // indirect
|
||||
github.com/luxfi/address v1.1.1 // indirect
|
||||
github.com/luxfi/age v1.6.0 // indirect
|
||||
github.com/luxfi/cache v1.2.1 // indirect
|
||||
github.com/luxfi/constants v1.5.8 // indirect
|
||||
github.com/luxfi/container v0.0.4 // indirect
|
||||
github.com/luxfi/crypto v1.20.0 // indirect
|
||||
github.com/luxfi/formatting v1.0.1 // indirect
|
||||
github.com/luxfi/geth v1.16.98 // indirect
|
||||
github.com/luxfi/cache v1.3.1 // indirect
|
||||
github.com/luxfi/constants v1.6.2 // indirect
|
||||
github.com/luxfi/container v0.2.1 // indirect
|
||||
github.com/luxfi/crypto v1.20.2 // indirect
|
||||
github.com/luxfi/formatting v1.1.1 // indirect
|
||||
github.com/luxfi/geth v1.20.1 // indirect
|
||||
github.com/luxfi/go-bip32 v1.1.0 // indirect
|
||||
github.com/luxfi/go-bip39 v1.2.0 // indirect
|
||||
github.com/luxfi/math v1.4.1 // indirect
|
||||
github.com/luxfi/math v1.5.1 // indirect
|
||||
github.com/luxfi/math/big v0.1.0 // indirect
|
||||
github.com/luxfi/mdns v0.1.1 // indirect
|
||||
github.com/luxfi/metric v1.5.7 // indirect
|
||||
github.com/luxfi/metric v1.8.1 // indirect
|
||||
github.com/luxfi/mock v0.1.1 // indirect
|
||||
github.com/luxfi/proto v1.0.0 // indirect
|
||||
github.com/luxfi/protocol v0.0.2 // indirect
|
||||
github.com/luxfi/sampler v1.1.0 // indirect
|
||||
github.com/luxfi/tls v1.0.3 // indirect
|
||||
github.com/luxfi/vm v1.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.21 // indirect
|
||||
github.com/luxfi/tls v1.1.1 // indirect
|
||||
github.com/luxfi/vm v1.3.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.15 // indirect
|
||||
github.com/mattn/go-isatty v0.0.22 // indirect
|
||||
github.com/miekg/dns v1.1.72 // indirect
|
||||
github.com/minio/crc64nvme v1.1.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minio-go/v7 v7.0.100 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/mr-tron/base58 v1.3.0 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
@@ -92,14 +112,15 @@ require (
|
||||
github.com/tinylib/msgp v1.6.4 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.70.0 // indirect
|
||||
github.com/zap-proto/zip v1.1.0
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel v1.44.0
|
||||
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.44.0 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.52.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect
|
||||
golang.org/x/exp v0.0.0-20260529124908-c761662dc8c9 // indirect
|
||||
golang.org/x/mod v0.36.0 // indirect
|
||||
golang.org/x/net v0.55.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
@@ -113,3 +134,11 @@ require (
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
||||
// The kmsclient library lives in this repo (sdk/go) and is released together
|
||||
// with the server. Build the in-tree copy so cmd/kms and cmd/kms-fetch pick up
|
||||
// the fix that sends env explicitly on the HTTP path (see sdk/go/kmsclient).
|
||||
// The require above stays at the last published tag so dropping this replace
|
||||
// falls back to a real version, never a missing one; publish sdk/go/v1.1.1 and
|
||||
// bump the require when cutting the next SDK release.
|
||||
replace github.com/hanzoai/kms/sdk/go => ./sdk/go
|
||||
|
||||
@@ -2,6 +2,8 @@ c2sp.org/CCTV/age v0.0.0-20251208015420-e9274a7bdbfd h1:ZLsPO6WdZ5zatV4UfVpr7oAw
|
||||
c2sp.org/CCTV/age v0.0.0-20251208015420-e9274a7bdbfd/go.mod h1:SrHC2C7r5GkDk8R+NFVzYy/sdj0Ypg9htaPXQq5Cqeo=
|
||||
filippo.io/hpke v0.4.0 h1:p575VVQ6ted4pL+it6M00V/f2qTZITO0zgmdKCkd5+A=
|
||||
filippo.io/hpke v0.4.0/go.mod h1:EmAN849/P3qdeK+PCMkDpDm83vRHM5cDipBJ8xbQLVY=
|
||||
github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE=
|
||||
github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro=
|
||||
github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
@@ -30,6 +32,8 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
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.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
@@ -50,8 +54,14 @@ github.com/dgraph-io/ristretto/v2 v2.4.0 h1:I/w09yLjhdcVD2QV192UJcq8dPBaAJb9pOuM
|
||||
github.com/dgraph-io/ristretto/v2 v2.4.0/go.mod h1:0KsrXtXvnv0EqnzyowllbVJB8yBonswa2lTCK2gGo9E=
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dlclark/regexp2/v2 v2.2.1 h1:mf4KkFUj0gJuarK8P+LgiS+Lit7m9N1yAwEfPbee7R0=
|
||||
github.com/dlclark/regexp2/v2 v2.2.1/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU=
|
||||
github.com/dop251/goja v0.0.0-20260607120635-348e6bea910d h1:xbM5U2EvWKkHxzEQJ2DEn20FwolWZahuTnVHr6WL3Q4=
|
||||
github.com/dop251/goja v0.0.0-20260607120635-348e6bea910d/go.mod h1:Sc+QOu1WruvaaeT/cxFez/pXHpI9ZDjg/E8QNfSVveI=
|
||||
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/evanw/esbuild v0.28.1 h1:ds+yuRyUaZGx++GR56CrCeuXh8PVhVM4xq8v7PNELFc=
|
||||
github.com/evanw/esbuild v0.28.1/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fxamacker/cbor/v2 v2.9.1 h1:2rWm8B193Ll4VdjsJY28jxs70IdDsHRWgQYAI80+rMQ=
|
||||
@@ -63,6 +73,10 @@ 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-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q=
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/gofiber/fiber/v3 v3.2.0 h1:g9+09D320foINPpCnR3ibQ5oBEFHjAWRRfDG1te54u8=
|
||||
github.com/gofiber/fiber/v3 v3.2.0/go.mod h1:FHOsc2Db7HhHpsE62QAaJlXVV1pNkbZEptZ4jtti7m4=
|
||||
github.com/gofiber/schema v1.7.1 h1:oSJBKdgP8JeIME4TQSAqlNKTU2iBB+2RNmKi8Nsc+TI=
|
||||
@@ -78,6 +92,8 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
|
||||
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/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs=
|
||||
github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
@@ -95,12 +111,14 @@ github.com/gorilla/rpc v1.2.1/go.mod h1:uNpOihAlF5xRFLuTYhfR0yfCTm0WTQSQttkMSptR
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
|
||||
github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||
github.com/hanzoai/cloud v0.1.1 h1:yVXuxz7o7sNnSA2+nuq+c0RnEA0Ic734S/r8/WTlLHk=
|
||||
github.com/hanzoai/cloud v0.1.1/go.mod h1:4V7Uk80+9SI1uZTpCEMwub2STABVd9hD16mLQ4DY2uo=
|
||||
github.com/hanzoai/kms/sdk/go v1.1.0 h1:mAYen6gM5mQD5xJu9NQNptrjsF3wvwi4D42GPUPC6R4=
|
||||
github.com/hanzoai/kms/sdk/go v1.1.0/go.mod h1:Qy0NgiZIanuPVAI/pVolFg325wroN7g20Bd2jcbtkiI=
|
||||
github.com/hanzoai/zip v0.2.0 h1:2GI1OoUv4QdCxmfiwoiyYsSHpiPAxrp0v+Gu4BLtTVI=
|
||||
github.com/hanzoai/zip v0.2.0/go.mod h1:3G+k2wy5bQ1wld66m0OPH8/LJ0kgIeNi1KPymMmlbf4=
|
||||
github.com/hanzoai/csqlite v0.1.0 h1:suwC3dh0INlfP/U0Es6cDf6JNQ+2+GVLLATPWCUux6k=
|
||||
github.com/hanzoai/csqlite v0.1.0/go.mod h1:H31a/O6VXuklR9UBkgY++bmAK5uzVfXPqU0F6P9Wsos=
|
||||
github.com/hanzoai/sqlite v0.3.0 h1:Siqa7b89CgSCteg5ubXOCQiPRxYIJGb9kMemwjXMIUQ=
|
||||
github.com/hanzoai/sqlite v0.3.0/go.mod h1:/eumCoj+3FLbLXr3jZomamv2DdJvh4No2nfYDdlGgUU=
|
||||
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
|
||||
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=
|
||||
@@ -111,8 +129,6 @@ github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
|
||||
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
@@ -124,78 +140,64 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/luxfi/accel v1.1.9 h1:Tsk6gXj2uKE19501bD0ajRYdeCHIlTGb6jYyLc+F8hc=
|
||||
github.com/luxfi/accel v1.1.9/go.mod h1:K00BcnLzEYMPHwCFq8Tf/450ApmTs9xBVvYOnofJCkc=
|
||||
github.com/luxfi/accel v1.2.4 h1:5VbIHyEvvfobn2zBiTFODxDw1CeqxCepZOLlvkuf9yQ=
|
||||
github.com/luxfi/accel v1.2.4/go.mod h1:ISIwAX+ZfsL/S5nsP2JvfldXN6Nc+QzoWf6Jtaq+xsQ=
|
||||
github.com/luxfi/address v1.0.1 h1:Sc4keyuVzBIvHr7uVeYZf2/WY9YDGUgDi/iiWenj49g=
|
||||
github.com/luxfi/address v1.0.1/go.mod h1:5j3Eh66v9zvv1GbNdZwt+23krV8JlSDaRzmWZU8ZRM0=
|
||||
github.com/luxfi/age v1.5.0 h1:G69HbSV4R3vKEH9B0CulnRaMdSdf4RalMgP8xKmxHeI=
|
||||
github.com/luxfi/age v1.5.0/go.mod h1:iAYAxgvrXxcy746+Ovh/eWWDuF9teJLNcCSSOX9RYW0=
|
||||
github.com/luxfi/address v1.1.1 h1:4afWzyBWzTiZN7RenBtdMC9LIvP9L4CSBzSquwKEAgI=
|
||||
github.com/luxfi/address v1.1.1/go.mod h1:KG0jUBcgoJYeieKP5jboCq9UewwDBIOus8ZCqUMVlw8=
|
||||
github.com/luxfi/age v1.6.0 h1:KMD8gSOP4NVCb7NWSlRcgBZNV2xm2a+qQWPyPmiX6f4=
|
||||
github.com/luxfi/age v1.6.0/go.mod h1:7cu9CIyikgyAvr5MlXFapEDQ15yBaHOSdKkK5lG04WE=
|
||||
github.com/luxfi/cache v1.2.1 h1:kAzOS55/hmYeNKR+0HAKv4ma48Y6JjkI8UQeqdZ8bfI=
|
||||
github.com/luxfi/cache v1.2.1/go.mod h1:co7JTxZZHpKT31Yh01LFp5aZOxmoUg157FhBLQdQHVU=
|
||||
github.com/luxfi/constants v1.5.8 h1:iNP9AWNUcM4Tps7jYnx49CwtCWAC9mYRxJfGou2za0g=
|
||||
github.com/luxfi/constants v1.5.8/go.mod h1:Pu5jWHdnUtQRbWC43yTUjU/pbIIKMDOd2a2yroSfo48=
|
||||
github.com/luxfi/container v0.0.4 h1:BXhF82WyfqVP5mjlNcr7tP0Fcnvl0Ap1rkiu+rq5XuM=
|
||||
github.com/luxfi/container v0.0.4/go.mod h1:Z3SpmMF5d4t77MM0nHYXURpn+EMVaeu1fhbd/3BGaek=
|
||||
github.com/luxfi/crypto v1.19.17 h1:l2LLu7UFyICtJVfraLDLRi+lFGiDXKHSL18M9/m1gsQ=
|
||||
github.com/luxfi/crypto v1.19.17/go.mod h1:INjdZtke85k8hX/QAmTMAY8bbZ4gzGZQLqURg3xf6Gk=
|
||||
github.com/luxfi/crypto v1.20.0 h1:JNsQ25sVO6T8XuIHRue4akOpnt5pNmk1xg5hzmU6dNE=
|
||||
github.com/luxfi/crypto v1.20.0/go.mod h1:bLCBuIV/KDjPytld7jSYe1WbfWknPQXcivq88Qo96QU=
|
||||
github.com/luxfi/formatting v1.0.1 h1:ZnE1rAdEUds9yAegdVdGDOBGN6hLMPOv6E03Fp8IEYo=
|
||||
github.com/luxfi/formatting v1.0.1/go.mod h1:mYzNf5DJOiqSSKUPzNj5dKy4tstFbN3pZlkI5716eKc=
|
||||
github.com/luxfi/geth v1.16.98 h1:w187TtKuGStf3tm2bshuHVKBv2Frjx0lT54kQVXyNHA=
|
||||
github.com/luxfi/geth v1.16.98/go.mod h1:6kEzSExdk9CPQDPXALt6P3HfQqBq7KF1Jrrv9gBpxbU=
|
||||
github.com/luxfi/go-bip32 v1.0.2 h1:7vFbb+Wr4Z499q2tuCLdd7wWjtn8sH+HWBlx76mhH9Y=
|
||||
github.com/luxfi/go-bip32 v1.0.2/go.mod h1:bc7/LXDKAJQZ/F0Xjf5yXaTZxY9/ssLb4FC+Hxn/cDk=
|
||||
github.com/luxfi/cache v1.3.1 h1:grQhi/B5GKypG7avDMeY143QTgFbfEvQICKNIh1Cw6U=
|
||||
github.com/luxfi/cache v1.3.1/go.mod h1:2MokdbeNUy/9O3mdREWkE6BiN7tRvePkXiKkcb+4M7g=
|
||||
github.com/luxfi/constants v1.6.2 h1:pXHdKIFbfE9qX4xOjq2LxYvagNhhNvspUVEbPcIEKfA=
|
||||
github.com/luxfi/constants v1.6.2/go.mod h1:r0oH8C/+r/XFYBq1AJxt6zWRKKRKgDzrEMop/CCs9rI=
|
||||
github.com/luxfi/container v0.2.1 h1:MTnfKXzS5+oxV5jKZerdOxSA6iMPaQI9/FWGufizzaw=
|
||||
github.com/luxfi/container v0.2.1/go.mod h1:B+uM0wP0lGvt/SSK7QOEn/qBcsHzILVHlKikdCyzSgM=
|
||||
github.com/luxfi/crypto v1.20.2 h1:L81WEsU/hs2A76F5PWBusG0yU74QqkDdUqqgexWUxh4=
|
||||
github.com/luxfi/crypto v1.20.2/go.mod h1:qYHOM0lO4PRh7LEaObxFQUIMjmT1/paVm/WgZkobT1k=
|
||||
github.com/luxfi/formatting v1.1.1 h1:MJhVXIPh1dbysvYEjtaEA/Z0FUTiI7n0DwOF54FS08c=
|
||||
github.com/luxfi/formatting v1.1.1/go.mod h1:zhBWp6fLZduhpiAdPgVDdPVOyhw4FvwRUksF6+xKQCE=
|
||||
github.com/luxfi/geth v1.20.1 h1:QUGQr4AKvADjwMi7t8a0OfoyxShgEcI9pwie1jFYfm0=
|
||||
github.com/luxfi/geth v1.20.1/go.mod h1:GV5bIMEgWviRN+jPXERyVpI16H3iHqPcdIokDoZdrvU=
|
||||
github.com/luxfi/go-bip32 v1.1.0 h1:zjy19WKa1KJnGamRNyOZM3l/MPtV/sax7M4NMPwLHZQ=
|
||||
github.com/luxfi/go-bip32 v1.1.0/go.mod h1:QyDXlzWL3xRiCbMUi4Z3J52Q/SMoCvdRLXXlYvSZor8=
|
||||
github.com/luxfi/go-bip39 v1.1.2 h1:p+wLMPGs6MLQh7q0YIsmy2EhHL7LHiELEGTJko6t/Jg=
|
||||
github.com/luxfi/go-bip39 v1.1.2/go.mod h1:96de9VkR2kY/ASAnhMtvt3TSh+PZkAFAngNj0GjRGDo=
|
||||
github.com/luxfi/go-bip39 v1.2.0 h1:fx3pFuSGawCG4In6pA4OLLStqbgIqD1j8EygFskoHzY=
|
||||
github.com/luxfi/go-bip39 v1.2.0/go.mod h1:if+2OVbG4k4jKIuBt/Rse1KV1kgWQM5j5xFbUtwbNtc=
|
||||
github.com/luxfi/ids v1.2.15 h1:omE+E4+0Poj9DzM11ejSFgteaSQ3KDHi5g54iH6jcxI=
|
||||
github.com/luxfi/ids v1.2.15/go.mod h1:Fj73K5xcblvdE0SxU/ip+jE8VqNdu+80548su5KJ7xI=
|
||||
github.com/luxfi/ids v1.3.0 h1:11xnwRDm6zQzbqcRnkFujOYkvhK4Fs/+g+sKRlRUNsU=
|
||||
github.com/luxfi/ids v1.3.0/go.mod h1:6vpdcdZW0qxeade+3xby8aLTutbcJ7O0r8+fNQrksGI=
|
||||
github.com/luxfi/keys v1.2.0 h1:+AriQNM7FOylAEls1XvFdlSOXDfoyc6X3ZfJRWQ2I9g=
|
||||
github.com/luxfi/keys v1.2.0/go.mod h1:SjsAaxo6sGmSp9OaHXUiVCqsknO8iPspN6jMOoEAMb8=
|
||||
github.com/luxfi/keys v1.4.0 h1:b3Z0oCyX2WByKOPtvVWh+vuqSnqknfHMVtOyVLQxRfQ=
|
||||
github.com/luxfi/keys v1.4.0/go.mod h1:jZy5wnMKbPIqlzsdOQeh/PdJ6tWk+iu/t58opJ90kgE=
|
||||
github.com/luxfi/kms v1.11.8 h1:cDEEpx/zAyfRZoGIxFd97Hs9cB7k8tvLet9iLCif7XM=
|
||||
github.com/luxfi/kms v1.11.8/go.mod h1:XhLUVqN4RBv6j4Bj3MNgTZmHCnm74jH7RqqK0b9xbzw=
|
||||
github.com/luxfi/ids v1.3.2 h1:c6Rft5kZB4XqiCtWaGH47bfhaNFm3FGRfhEzI01GVeI=
|
||||
github.com/luxfi/ids v1.3.2/go.mod h1:+5l8cYMbKpORJbQ2r98CYJo9TQATgUdnmzpYFZWMwwc=
|
||||
github.com/luxfi/keys v1.4.1 h1:2Zcoovaz9OLPz7m7VGXfRrGnrlqt0GeUpJclsPBi4EU=
|
||||
github.com/luxfi/keys v1.4.1/go.mod h1:P8EUP5DKrR1SUZBGZjDT3rWcp2P1miUlVh7IBRNBphU=
|
||||
github.com/luxfi/kms v1.12.4 h1:7eIGopBtGN4YqbJCTo7SE3oOcnVUoUDv2u1gS3UeMcw=
|
||||
github.com/luxfi/kms v1.12.4/go.mod h1:hdbnJp5S+BKW2RcH1+qSC87NZkIGEL5Z8/9UJyKBikw=
|
||||
github.com/luxfi/log v1.4.3 h1:xkUKRWvQ4ZwvlUC2e0/RTtHYZOYSMvSQ9W9lbjwBmiI=
|
||||
github.com/luxfi/log v1.4.3/go.mod h1:myIkufyiQomSQH34K981kbz6cG4WUoerRUh7F4XhlQI=
|
||||
github.com/luxfi/math v1.4.1 h1:1t9bCCsEqnl9yIKrShlbs80DBKyYTWdnzkVfBqEeO7Q=
|
||||
github.com/luxfi/math v1.4.1/go.mod h1:QvbRxauQyE1w4lvbcLSe6c8yeJz2Zj1Bq1rayGgs2tA=
|
||||
github.com/luxfi/math v1.5.1 h1:FDOY75e4vn/Xra1ij99xOS/9XdxQGCPP6HONHRkCwfg=
|
||||
github.com/luxfi/math v1.5.1/go.mod h1:3j9R24hVfPhrbvs45YSJP7jAyVNfwx/cj/+lAO8IGro=
|
||||
github.com/luxfi/math/big v0.1.0 h1:Vz4c0RsZVPdIKPsHPgAJChH/R3p15WHRUz7LkLf+NIQ=
|
||||
github.com/luxfi/math/big v0.1.0/go.mod h1:BuxSu22RbO93xBLk5Eam5nldFponoJ73xDFz4uJ3Huk=
|
||||
github.com/luxfi/mdns v0.1.1 h1:g2eRr9AXcziPkkcd24M+Qu9ApEpoKKjfI79QSNqv0rQ=
|
||||
github.com/luxfi/mdns v0.1.1/go.mod h1:dbp5f3h3aE7CGzwbaWzBM9cwdcekhmSrWhQevgYhhNA=
|
||||
github.com/luxfi/metric v1.5.7 h1:LoSPEUpak2SLcynF+LT2cXjl9ECp4nY+Lia9zudmDv4=
|
||||
github.com/luxfi/metric v1.5.7/go.mod h1:CMguEhyuLi4YUWyXimJ+UHply99BDFrL0pxedB7rBqM=
|
||||
github.com/luxfi/metric v1.8.1 h1:v58GgPFAOLPVxSa/JiNLwqJQNEFHdWbXZV28piMXX4s=
|
||||
github.com/luxfi/metric v1.8.1/go.mod h1:R1OPAIeW4UBW3osK7j2r3/XPmczfNRFTXg4bnlemTuE=
|
||||
github.com/luxfi/mock v0.1.1 h1:0HEtIjg1J6CWz+IUyP6rsGqNWTcmxjFnSQIhaDuARwY=
|
||||
github.com/luxfi/mock v0.1.1/go.mod h1:jo35akl3Vtd8LbzDts8VJ0jmSVycrd1/eBi6g6t5hKU=
|
||||
github.com/luxfi/proto v1.0.0 h1:nlxv4lt/i75XDB2Q3nTqC1o0RDPmJ9K9CJ8UyYvceak=
|
||||
github.com/luxfi/proto v1.0.0/go.mod h1:pZLKsCmhiPtmm3z7ezBbnsYT2m79q+cFSmzxvuBeANE=
|
||||
github.com/luxfi/pq v1.1.0 h1:ADplfUSyirLymSxs3Ix0HeDTyl5oswCNUpXJt/5vLY8=
|
||||
github.com/luxfi/pq v1.1.0/go.mod h1:KT5rG9ztpzIkT9QSnXK4WFqBBLzKCLjY7l1c/unBi8I=
|
||||
github.com/luxfi/protocol v0.0.2 h1:TAuXMm1K4VDpG3B3brq1EE9Lb7XlbhQmVBObskgNhmc=
|
||||
github.com/luxfi/protocol v0.0.2/go.mod h1:Yjn2VRwn5PXim0+JTalAyrVG6YbmuSnYOfeUXSbAsqA=
|
||||
github.com/luxfi/sampler v1.1.0 h1:u3iRDl7V06ARh0e85h3HT+aZ1saCFo2yMMsh+dCJbqk=
|
||||
github.com/luxfi/sampler v1.1.0/go.mod h1:kJa53S3tC9+VSbuV3RFu68MmbCCBlr2UM39LOClQ/Hs=
|
||||
github.com/luxfi/tls v1.0.3 h1:rK3nxSAxrUOOSHOZnKChwV4f6UJ+cfOl8KWJXAQx/SI=
|
||||
github.com/luxfi/tls v1.0.3/go.mod h1:dQqSiGE7YxXUxOwICoReUuIitBms9DYOaCeteBwmIWw=
|
||||
github.com/luxfi/vm v1.2.0 h1:jTwQRHdC9VmyRZTPSn+IqjMju7f6xlxLc6P9CEg+Y2M=
|
||||
github.com/luxfi/vm v1.2.0/go.mod h1:qasVIBRerVQuvy9vFGrX3H8X8pPMPG5un/KbZSyq5YY=
|
||||
github.com/luxfi/zap v0.8.1 h1:IzYWf4T35QjsUJhoNbuhew8MDDRYmlZUVx4gKBNLjMA=
|
||||
github.com/luxfi/zap v0.8.1/go.mod h1:gUoQ/V9nTLLIb4/n0bY/L2zQDYK6pDBG121rK4t8bpg=
|
||||
github.com/luxfi/tls v1.1.1 h1:BSZ0gHSp7U8vzlmzx7WSSCz+b7Ky4JtD9HDDhn7vrDg=
|
||||
github.com/luxfi/tls v1.1.1/go.mod h1:+5TDy8UtLL+tz124brZzpUDBRj+sKrq0JFqdmpMUHgw=
|
||||
github.com/luxfi/vm v1.3.1 h1:rLCbygaajehVkUoJ5czwhpUAJaC5J5okq3p+j2QSPSo=
|
||||
github.com/luxfi/vm v1.3.1/go.mod h1:uViH3COP8hhCbj42v1MTohkPCDwYQCZanNIOb/StWqY=
|
||||
github.com/luxfi/zap v1.2.6 h1:NBpbm9Gib41Oi/XAkAZKQ3hb+xCafo7JsrUjw+bKiAc=
|
||||
github.com/luxfi/zap v1.2.6/go.mod h1:sTAe/AMMamoE85cVoe81+NbqHJkgvqS0LhY9ByHEmr0=
|
||||
github.com/luxfi/zapdb v1.10.0 h1:1lLHEmkyC0BucnA/zjQYsMkUVxuEo2vQkEaQGjYfuuc=
|
||||
github.com/luxfi/zapdb v1.10.0/go.mod h1:Qukh3hDRD0MnxA6z+a28JTnXhN85AiLLgp6TYr4QAMc=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs=
|
||||
github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY=
|
||||
github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
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.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||
@@ -205,8 +207,8 @@ github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.100 h1:ShkWi8Tyj9RtU57OQB2HIXKz4bFgtVib0bbT1sbtLI8=
|
||||
github.com/minio/minio-go/v7 v7.0.100/go.mod h1:EtGNKtlX20iL2yaYnxEigaIvj0G0GwSDnifnG8ClIdw=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/mr-tron/base58 v1.3.0 h1:K6Y13R2h+dku0wOqKtecgRnBUBPrZzLZy5aIj8lCcJI=
|
||||
github.com/mr-tron/base58 v1.3.0/go.mod h1:2BuubE67DCSWwVfx37JWNG8emOC0sHEU4/HpcYgCLX8=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
@@ -257,14 +259,32 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/zap-proto/go v1.3.0 h1:S3rMoawwhH/BbSZ4G8zG05hJoQnMSMDPzIq75diCTqE=
|
||||
github.com/zap-proto/go v1.3.0/go.mod h1:914SNGTH6Rv3Yu1MweWJBPEN8FZlo5C39QyhaB0C7Q0=
|
||||
github.com/zap-proto/http v0.1.0 h1:aA8NmU/a0yM7QFU5tjXPYHFiBUcoHn92wjMy181o50M=
|
||||
github.com/zap-proto/http v0.1.0/go.mod h1:UYfGhDDCetgxs65XSev8Lpf65COg5vKQK+cWwZGh4zQ=
|
||||
github.com/zap-proto/zip v1.1.0 h1:JDIVblpbbutKHt+wzVr4VACyJZwytGuR1aMuyQqOhnw=
|
||||
github.com/zap-proto/zip v1.1.0/go.mod h1:8ENateaAvSg2ACuO4B03ntl8dGeWxnpXpOc9zRDhZPA=
|
||||
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/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/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.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak=
|
||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
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.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
@@ -275,8 +295,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA=
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ=
|
||||
golang.org/x/exp v0.0.0-20260529124908-c761662dc8c9 h1:4d4PbuBNwaxMXkXI8yiIYjydtMU+04RHeuSxJdgKftM=
|
||||
golang.org/x/exp v0.0.0-20260529124908-c761662dc8c9/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
||||
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
||||
@@ -304,6 +324,7 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -320,6 +341,12 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
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=
|
||||
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/hanzoai/cloud"
|
||||
"github.com/hanzoai/zip"
|
||||
"github.com/zap-proto/zip"
|
||||
)
|
||||
|
||||
// Mount registers KMS routes with the shared cloud zip.App per HIP-0106.
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
badger "github.com/luxfi/zapdb"
|
||||
|
||||
"github.com/luxfi/kms/pkg/store"
|
||||
_ "modernc.org/sqlite"
|
||||
_ "github.com/hanzoai/sqlite"
|
||||
)
|
||||
|
||||
// newTestServerWithAudit wires the full handler chain + an isolated
|
||||
|
||||
+29
-29
@@ -3,12 +3,12 @@ module github.com/hanzoai/kms/sdk/go
|
||||
go 1.26.4
|
||||
|
||||
require (
|
||||
github.com/luxfi/crypto v1.19.17
|
||||
github.com/luxfi/ids v1.2.15
|
||||
github.com/luxfi/keys v1.2.0
|
||||
github.com/luxfi/crypto v1.20.2
|
||||
github.com/luxfi/ids v1.3.2
|
||||
github.com/luxfi/keys v1.4.1
|
||||
github.com/luxfi/kms v1.11.4
|
||||
github.com/luxfi/log v1.4.1
|
||||
github.com/luxfi/zap v0.8.1
|
||||
github.com/luxfi/log v1.4.3
|
||||
github.com/luxfi/zap v1.2.6
|
||||
github.com/luxfi/zapdb v1.10.0
|
||||
golang.org/x/crypto v0.52.0
|
||||
)
|
||||
@@ -30,46 +30,46 @@ require (
|
||||
github.com/gorilla/rpc v1.2.1 // indirect
|
||||
github.com/grandcat/zeroconf v1.0.0 // indirect
|
||||
github.com/holiman/uint256 v1.3.2 // indirect
|
||||
github.com/klauspost/compress v1.18.5 // indirect
|
||||
github.com/klauspost/compress v1.18.6 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/klauspost/crc32 v1.3.0 // indirect
|
||||
github.com/luxfi/accel v1.1.9 // indirect
|
||||
github.com/luxfi/address v1.0.1 // indirect
|
||||
github.com/luxfi/age v1.5.0 // indirect
|
||||
github.com/luxfi/cache v1.2.1 // indirect
|
||||
github.com/luxfi/constants v1.5.8-0.20260603055356-93c2c2ceb9ca // indirect
|
||||
github.com/luxfi/container v0.0.4 // indirect
|
||||
github.com/luxfi/formatting v1.0.1 // indirect
|
||||
github.com/luxfi/geth v1.16.98 // indirect
|
||||
github.com/luxfi/go-bip32 v1.0.2 // indirect
|
||||
github.com/luxfi/go-bip39 v1.1.2 // indirect
|
||||
github.com/luxfi/math v1.4.1 // indirect
|
||||
github.com/luxfi/accel v1.2.4 // indirect
|
||||
github.com/luxfi/address v1.1.1 // indirect
|
||||
github.com/luxfi/age v1.6.0 // indirect
|
||||
github.com/luxfi/cache v1.3.1 // indirect
|
||||
github.com/luxfi/constants v1.6.2 // indirect
|
||||
github.com/luxfi/container v0.2.1 // indirect
|
||||
github.com/luxfi/formatting v1.1.1 // indirect
|
||||
github.com/luxfi/geth v1.20.1 // indirect
|
||||
github.com/luxfi/go-bip32 v1.1.0 // indirect
|
||||
github.com/luxfi/go-bip39 v1.2.0 // indirect
|
||||
github.com/luxfi/math v1.5.1 // indirect
|
||||
github.com/luxfi/math/big v0.1.0 // indirect
|
||||
github.com/luxfi/mdns v0.1.1 // indirect
|
||||
github.com/luxfi/metric v1.5.7 // indirect
|
||||
github.com/luxfi/metric v1.8.1 // indirect
|
||||
github.com/luxfi/mock v0.1.1 // indirect
|
||||
github.com/luxfi/proto v1.0.0 // indirect
|
||||
github.com/luxfi/protocol v0.0.2 // indirect
|
||||
github.com/luxfi/sampler v1.1.0 // indirect
|
||||
github.com/luxfi/tls v1.0.3 // indirect
|
||||
github.com/luxfi/vm v1.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/luxfi/tls v1.1.1 // indirect
|
||||
github.com/luxfi/vm v1.3.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.15 // indirect
|
||||
github.com/mattn/go-isatty v0.0.22 // indirect
|
||||
github.com/miekg/dns v1.1.72 // indirect
|
||||
github.com/minio/crc64nvme v1.1.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minio-go/v7 v7.0.100 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/mr-tron/base58 v1.3.0 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/supranational/blst v0.3.16 // indirect
|
||||
github.com/tinylib/msgp v1.6.3 // indirect
|
||||
github.com/tinylib/msgp v1.6.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.44.0 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect
|
||||
golang.org/x/exp v0.0.0-20260529124908-c761662dc8c9 // indirect
|
||||
golang.org/x/mod v0.36.0 // indirect
|
||||
golang.org/x/net v0.55.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
|
||||
+61
-62
@@ -88,8 +88,8 @@ github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
|
||||
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
@@ -99,64 +99,64 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/luxfi/accel v1.1.9 h1:Tsk6gXj2uKE19501bD0ajRYdeCHIlTGb6jYyLc+F8hc=
|
||||
github.com/luxfi/accel v1.1.9/go.mod h1:K00BcnLzEYMPHwCFq8Tf/450ApmTs9xBVvYOnofJCkc=
|
||||
github.com/luxfi/address v1.0.1 h1:Sc4keyuVzBIvHr7uVeYZf2/WY9YDGUgDi/iiWenj49g=
|
||||
github.com/luxfi/address v1.0.1/go.mod h1:5j3Eh66v9zvv1GbNdZwt+23krV8JlSDaRzmWZU8ZRM0=
|
||||
github.com/luxfi/age v1.5.0 h1:G69HbSV4R3vKEH9B0CulnRaMdSdf4RalMgP8xKmxHeI=
|
||||
github.com/luxfi/age v1.5.0/go.mod h1:iAYAxgvrXxcy746+Ovh/eWWDuF9teJLNcCSSOX9RYW0=
|
||||
github.com/luxfi/cache v1.2.1 h1:kAzOS55/hmYeNKR+0HAKv4ma48Y6JjkI8UQeqdZ8bfI=
|
||||
github.com/luxfi/cache v1.2.1/go.mod h1:co7JTxZZHpKT31Yh01LFp5aZOxmoUg157FhBLQdQHVU=
|
||||
github.com/luxfi/constants v1.5.8-0.20260603055356-93c2c2ceb9ca h1:N2lM2gMJ2Hk5yZ6GqURWtg6nIVtodsjqMe+MGNiuCNw=
|
||||
github.com/luxfi/constants v1.5.8-0.20260603055356-93c2c2ceb9ca/go.mod h1:z+Wc7skybZAA+xuBWNcmtv402S/BFqixL+FiSQXPg8U=
|
||||
github.com/luxfi/container v0.0.4 h1:BXhF82WyfqVP5mjlNcr7tP0Fcnvl0Ap1rkiu+rq5XuM=
|
||||
github.com/luxfi/container v0.0.4/go.mod h1:Z3SpmMF5d4t77MM0nHYXURpn+EMVaeu1fhbd/3BGaek=
|
||||
github.com/luxfi/crypto v1.19.17 h1:l2LLu7UFyICtJVfraLDLRi+lFGiDXKHSL18M9/m1gsQ=
|
||||
github.com/luxfi/crypto v1.19.17/go.mod h1:INjdZtke85k8hX/QAmTMAY8bbZ4gzGZQLqURg3xf6Gk=
|
||||
github.com/luxfi/formatting v1.0.1 h1:ZnE1rAdEUds9yAegdVdGDOBGN6hLMPOv6E03Fp8IEYo=
|
||||
github.com/luxfi/formatting v1.0.1/go.mod h1:mYzNf5DJOiqSSKUPzNj5dKy4tstFbN3pZlkI5716eKc=
|
||||
github.com/luxfi/geth v1.16.98 h1:w187TtKuGStf3tm2bshuHVKBv2Frjx0lT54kQVXyNHA=
|
||||
github.com/luxfi/geth v1.16.98/go.mod h1:6kEzSExdk9CPQDPXALt6P3HfQqBq7KF1Jrrv9gBpxbU=
|
||||
github.com/luxfi/go-bip32 v1.0.2 h1:7vFbb+Wr4Z499q2tuCLdd7wWjtn8sH+HWBlx76mhH9Y=
|
||||
github.com/luxfi/go-bip32 v1.0.2/go.mod h1:bc7/LXDKAJQZ/F0Xjf5yXaTZxY9/ssLb4FC+Hxn/cDk=
|
||||
github.com/luxfi/go-bip39 v1.1.2 h1:p+wLMPGs6MLQh7q0YIsmy2EhHL7LHiELEGTJko6t/Jg=
|
||||
github.com/luxfi/go-bip39 v1.1.2/go.mod h1:96de9VkR2kY/ASAnhMtvt3TSh+PZkAFAngNj0GjRGDo=
|
||||
github.com/luxfi/ids v1.2.15 h1:omE+E4+0Poj9DzM11ejSFgteaSQ3KDHi5g54iH6jcxI=
|
||||
github.com/luxfi/ids v1.2.15/go.mod h1:Fj73K5xcblvdE0SxU/ip+jE8VqNdu+80548su5KJ7xI=
|
||||
github.com/luxfi/keys v1.2.0 h1:+AriQNM7FOylAEls1XvFdlSOXDfoyc6X3ZfJRWQ2I9g=
|
||||
github.com/luxfi/keys v1.2.0/go.mod h1:SjsAaxo6sGmSp9OaHXUiVCqsknO8iPspN6jMOoEAMb8=
|
||||
github.com/luxfi/kms v1.11.4 h1:qCvACPsoSVPSbxRzOco/63fpiMO8AZSKsx3k9kxCrbo=
|
||||
github.com/luxfi/accel v1.2.4 h1:5VbIHyEvvfobn2zBiTFODxDw1CeqxCepZOLlvkuf9yQ=
|
||||
github.com/luxfi/accel v1.2.4/go.mod h1:ISIwAX+ZfsL/S5nsP2JvfldXN6Nc+QzoWf6Jtaq+xsQ=
|
||||
github.com/luxfi/address v1.1.1 h1:4afWzyBWzTiZN7RenBtdMC9LIvP9L4CSBzSquwKEAgI=
|
||||
github.com/luxfi/address v1.1.1/go.mod h1:KG0jUBcgoJYeieKP5jboCq9UewwDBIOus8ZCqUMVlw8=
|
||||
github.com/luxfi/age v1.6.0 h1:KMD8gSOP4NVCb7NWSlRcgBZNV2xm2a+qQWPyPmiX6f4=
|
||||
github.com/luxfi/age v1.6.0/go.mod h1:7cu9CIyikgyAvr5MlXFapEDQ15yBaHOSdKkK5lG04WE=
|
||||
github.com/luxfi/cache v1.3.1 h1:grQhi/B5GKypG7avDMeY143QTgFbfEvQICKNIh1Cw6U=
|
||||
github.com/luxfi/cache v1.3.1/go.mod h1:2MokdbeNUy/9O3mdREWkE6BiN7tRvePkXiKkcb+4M7g=
|
||||
github.com/luxfi/constants v1.6.2 h1:pXHdKIFbfE9qX4xOjq2LxYvagNhhNvspUVEbPcIEKfA=
|
||||
github.com/luxfi/constants v1.6.2/go.mod h1:r0oH8C/+r/XFYBq1AJxt6zWRKKRKgDzrEMop/CCs9rI=
|
||||
github.com/luxfi/container v0.2.1 h1:MTnfKXzS5+oxV5jKZerdOxSA6iMPaQI9/FWGufizzaw=
|
||||
github.com/luxfi/container v0.2.1/go.mod h1:B+uM0wP0lGvt/SSK7QOEn/qBcsHzILVHlKikdCyzSgM=
|
||||
github.com/luxfi/crypto v1.20.2 h1:L81WEsU/hs2A76F5PWBusG0yU74QqkDdUqqgexWUxh4=
|
||||
github.com/luxfi/crypto v1.20.2/go.mod h1:qYHOM0lO4PRh7LEaObxFQUIMjmT1/paVm/WgZkobT1k=
|
||||
github.com/luxfi/formatting v1.1.1 h1:MJhVXIPh1dbysvYEjtaEA/Z0FUTiI7n0DwOF54FS08c=
|
||||
github.com/luxfi/formatting v1.1.1/go.mod h1:zhBWp6fLZduhpiAdPgVDdPVOyhw4FvwRUksF6+xKQCE=
|
||||
github.com/luxfi/geth v1.20.1 h1:QUGQr4AKvADjwMi7t8a0OfoyxShgEcI9pwie1jFYfm0=
|
||||
github.com/luxfi/geth v1.20.1/go.mod h1:GV5bIMEgWviRN+jPXERyVpI16H3iHqPcdIokDoZdrvU=
|
||||
github.com/luxfi/go-bip32 v1.1.0 h1:zjy19WKa1KJnGamRNyOZM3l/MPtV/sax7M4NMPwLHZQ=
|
||||
github.com/luxfi/go-bip32 v1.1.0/go.mod h1:QyDXlzWL3xRiCbMUi4Z3J52Q/SMoCvdRLXXlYvSZor8=
|
||||
github.com/luxfi/go-bip39 v1.2.0 h1:fx3pFuSGawCG4In6pA4OLLStqbgIqD1j8EygFskoHzY=
|
||||
github.com/luxfi/go-bip39 v1.2.0/go.mod h1:if+2OVbG4k4jKIuBt/Rse1KV1kgWQM5j5xFbUtwbNtc=
|
||||
github.com/luxfi/ids v1.3.2 h1:c6Rft5kZB4XqiCtWaGH47bfhaNFm3FGRfhEzI01GVeI=
|
||||
github.com/luxfi/ids v1.3.2/go.mod h1:+5l8cYMbKpORJbQ2r98CYJo9TQATgUdnmzpYFZWMwwc=
|
||||
github.com/luxfi/keys v1.4.1 h1:2Zcoovaz9OLPz7m7VGXfRrGnrlqt0GeUpJclsPBi4EU=
|
||||
github.com/luxfi/keys v1.4.1/go.mod h1:P8EUP5DKrR1SUZBGZjDT3rWcp2P1miUlVh7IBRNBphU=
|
||||
github.com/luxfi/kms v1.11.4 h1:mCGDItWs3MD6IsibbCZb5u5AlcySESGnKkyk6z6lrig=
|
||||
github.com/luxfi/kms v1.11.4/go.mod h1:Ku4LHH6J1oO9MaZDbn1e2fMnkRsN4QqV89DGwRFP/Ak=
|
||||
github.com/luxfi/log v1.4.1 h1:rIfFRodb9jrD/w7KayaUk0Oc+37PaQQdKEEMJCjR8gw=
|
||||
github.com/luxfi/log v1.4.1/go.mod h1:64IE3xRMJcpkQwnPUfJw3pDj7wU0kRS7BZ9wM7R72jk=
|
||||
github.com/luxfi/math v1.4.1 h1:1t9bCCsEqnl9yIKrShlbs80DBKyYTWdnzkVfBqEeO7Q=
|
||||
github.com/luxfi/math v1.4.1/go.mod h1:QvbRxauQyE1w4lvbcLSe6c8yeJz2Zj1Bq1rayGgs2tA=
|
||||
github.com/luxfi/log v1.4.3 h1:xkUKRWvQ4ZwvlUC2e0/RTtHYZOYSMvSQ9W9lbjwBmiI=
|
||||
github.com/luxfi/log v1.4.3/go.mod h1:myIkufyiQomSQH34K981kbz6cG4WUoerRUh7F4XhlQI=
|
||||
github.com/luxfi/math v1.5.1 h1:FDOY75e4vn/Xra1ij99xOS/9XdxQGCPP6HONHRkCwfg=
|
||||
github.com/luxfi/math v1.5.1/go.mod h1:3j9R24hVfPhrbvs45YSJP7jAyVNfwx/cj/+lAO8IGro=
|
||||
github.com/luxfi/math/big v0.1.0 h1:Vz4c0RsZVPdIKPsHPgAJChH/R3p15WHRUz7LkLf+NIQ=
|
||||
github.com/luxfi/math/big v0.1.0/go.mod h1:BuxSu22RbO93xBLk5Eam5nldFponoJ73xDFz4uJ3Huk=
|
||||
github.com/luxfi/mdns v0.1.1 h1:g2eRr9AXcziPkkcd24M+Qu9ApEpoKKjfI79QSNqv0rQ=
|
||||
github.com/luxfi/mdns v0.1.1/go.mod h1:dbp5f3h3aE7CGzwbaWzBM9cwdcekhmSrWhQevgYhhNA=
|
||||
github.com/luxfi/metric v1.5.7 h1:LoSPEUpak2SLcynF+LT2cXjl9ECp4nY+Lia9zudmDv4=
|
||||
github.com/luxfi/metric v1.5.7/go.mod h1:CMguEhyuLi4YUWyXimJ+UHply99BDFrL0pxedB7rBqM=
|
||||
github.com/luxfi/metric v1.8.1 h1:v58GgPFAOLPVxSa/JiNLwqJQNEFHdWbXZV28piMXX4s=
|
||||
github.com/luxfi/metric v1.8.1/go.mod h1:R1OPAIeW4UBW3osK7j2r3/XPmczfNRFTXg4bnlemTuE=
|
||||
github.com/luxfi/mock v0.1.1 h1:0HEtIjg1J6CWz+IUyP6rsGqNWTcmxjFnSQIhaDuARwY=
|
||||
github.com/luxfi/mock v0.1.1/go.mod h1:jo35akl3Vtd8LbzDts8VJ0jmSVycrd1/eBi6g6t5hKU=
|
||||
github.com/luxfi/pq v1.0.3 h1:pFlQm1+5FuKTDUh2y/23bXWkN4I2Rc5iuxJypwDFFMs=
|
||||
github.com/luxfi/pq v1.0.3/go.mod h1:8bppZcRElfrVt0n3nYCZW3iX1TvhvzNbdjNdK1irgIE=
|
||||
github.com/luxfi/proto v1.0.0 h1:nlxv4lt/i75XDB2Q3nTqC1o0RDPmJ9K9CJ8UyYvceak=
|
||||
github.com/luxfi/proto v1.0.0/go.mod h1:pZLKsCmhiPtmm3z7ezBbnsYT2m79q+cFSmzxvuBeANE=
|
||||
github.com/luxfi/pq v1.1.0 h1:ADplfUSyirLymSxs3Ix0HeDTyl5oswCNUpXJt/5vLY8=
|
||||
github.com/luxfi/pq v1.1.0/go.mod h1:KT5rG9ztpzIkT9QSnXK4WFqBBLzKCLjY7l1c/unBi8I=
|
||||
github.com/luxfi/protocol v0.0.2 h1:TAuXMm1K4VDpG3B3brq1EE9Lb7XlbhQmVBObskgNhmc=
|
||||
github.com/luxfi/protocol v0.0.2/go.mod h1:Yjn2VRwn5PXim0+JTalAyrVG6YbmuSnYOfeUXSbAsqA=
|
||||
github.com/luxfi/sampler v1.1.0 h1:u3iRDl7V06ARh0e85h3HT+aZ1saCFo2yMMsh+dCJbqk=
|
||||
github.com/luxfi/sampler v1.1.0/go.mod h1:kJa53S3tC9+VSbuV3RFu68MmbCCBlr2UM39LOClQ/Hs=
|
||||
github.com/luxfi/tls v1.0.3 h1:rK3nxSAxrUOOSHOZnKChwV4f6UJ+cfOl8KWJXAQx/SI=
|
||||
github.com/luxfi/tls v1.0.3/go.mod h1:dQqSiGE7YxXUxOwICoReUuIitBms9DYOaCeteBwmIWw=
|
||||
github.com/luxfi/vm v1.2.0 h1:jTwQRHdC9VmyRZTPSn+IqjMju7f6xlxLc6P9CEg+Y2M=
|
||||
github.com/luxfi/vm v1.2.0/go.mod h1:qasVIBRerVQuvy9vFGrX3H8X8pPMPG5un/KbZSyq5YY=
|
||||
github.com/luxfi/zap v0.8.1 h1:MiVRsRA808bXn6Orc81cjlGjqbn8gf7fr9aP5MPbLEM=
|
||||
github.com/luxfi/zap v0.8.1/go.mod h1:gUoQ/V9nTLLIb4/n0bY/L2zQDYK6pDBG121rK4t8bpg=
|
||||
github.com/luxfi/tls v1.1.1 h1:BSZ0gHSp7U8vzlmzx7WSSCz+b7Ky4JtD9HDDhn7vrDg=
|
||||
github.com/luxfi/tls v1.1.1/go.mod h1:+5TDy8UtLL+tz124brZzpUDBRj+sKrq0JFqdmpMUHgw=
|
||||
github.com/luxfi/vm v1.3.1 h1:rLCbygaajehVkUoJ5czwhpUAJaC5J5okq3p+j2QSPSo=
|
||||
github.com/luxfi/vm v1.3.1/go.mod h1:uViH3COP8hhCbj42v1MTohkPCDwYQCZanNIOb/StWqY=
|
||||
github.com/luxfi/zap v1.2.6 h1:NBpbm9Gib41Oi/XAkAZKQ3hb+xCafo7JsrUjw+bKiAc=
|
||||
github.com/luxfi/zap v1.2.6/go.mod h1:sTAe/AMMamoE85cVoe81+NbqHJkgvqS0LhY9ByHEmr0=
|
||||
github.com/luxfi/zapdb v1.10.0 h1:1lLHEmkyC0BucnA/zjQYsMkUVxuEo2vQkEaQGjYfuuc=
|
||||
github.com/luxfi/zapdb v1.10.0/go.mod h1:Qukh3hDRD0MnxA6z+a28JTnXhN85AiLLgp6TYr4QAMc=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
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-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY=
|
||||
github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
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.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||
@@ -166,8 +166,8 @@ github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.100 h1:ShkWi8Tyj9RtU57OQB2HIXKz4bFgtVib0bbT1sbtLI8=
|
||||
github.com/minio/minio-go/v7 v7.0.100/go.mod h1:EtGNKtlX20iL2yaYnxEigaIvj0G0GwSDnifnG8ClIdw=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/mr-tron/base58 v1.3.0 h1:K6Y13R2h+dku0wOqKtecgRnBUBPrZzLZy5aIj8lCcJI=
|
||||
github.com/mr-tron/base58 v1.3.0/go.mod h1:2BuubE67DCSWwVfx37JWNG8emOC0sHEU4/HpcYgCLX8=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@@ -202,16 +202,16 @@ github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD
|
||||
github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE=
|
||||
github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||
github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s=
|
||||
github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
|
||||
github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ=
|
||||
github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
|
||||
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/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/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.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
@@ -222,8 +222,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA=
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ=
|
||||
golang.org/x/exp v0.0.0-20260529124908-c761662dc8c9 h1:4d4PbuBNwaxMXkXI8yiIYjydtMU+04RHeuSxJdgKftM=
|
||||
golang.org/x/exp v0.0.0-20260529124908-c761662dc8c9/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
||||
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
||||
@@ -251,7 +251,6 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
||||
@@ -301,6 +301,14 @@ func (c *Client) secretPath(path, name string) string {
|
||||
)
|
||||
}
|
||||
|
||||
// secretURL is secretPath plus the ?env= query the server keys the record
|
||||
// on. c.env defaults to "default" (see New) and is sent explicitly so reads
|
||||
// and deletes target the configured env instead of relying on the server's
|
||||
// legacy default — the same one-way discipline the write path enforces.
|
||||
func (c *Client) secretURL(path, name string) string {
|
||||
return c.secretPath(path, name) + "?env=" + url.QueryEscape(c.env)
|
||||
}
|
||||
|
||||
// Get fetches a single secret value by path and name.
|
||||
//
|
||||
// On the HTTP path: GET /v1/kms/orgs/{org}/secrets/{path}/{name}.
|
||||
@@ -317,7 +325,7 @@ func (c *Client) httpGet(ctx context.Context, path, name string) (string, error)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("kmsclient: auth: %w", err)
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.secretPath(path, name), nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.secretURL(path, name), nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("kmsclient: build request: %w", err)
|
||||
}
|
||||
@@ -460,9 +468,16 @@ func (c *Client) httpPut(ctx context.Context, path, name, value string) error {
|
||||
c.endpoint,
|
||||
url.PathEscape(c.org),
|
||||
)
|
||||
// env is REQUIRED by the server on writes (no silent "default"): send
|
||||
// c.env explicitly so the write lands in the same env-keyed record that
|
||||
// project/env/path readers resolve. Historically this body omitted env
|
||||
// and the server defaulted it to "default" — silently diverting a client
|
||||
// configured for env=prod into the wrong bucket. That is the bug this
|
||||
// fixes end to end.
|
||||
payload, _ := json.Marshal(map[string]string{
|
||||
"path": path,
|
||||
"name": name,
|
||||
"env": c.env,
|
||||
"value": value,
|
||||
})
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, u, strings.NewReader(string(payload)))
|
||||
@@ -499,7 +514,7 @@ func (c *Client) httpDelete(ctx context.Context, path, name string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("kmsclient: auth: %w", err)
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, c.secretPath(path, name), nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, c.secretURL(path, name), nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("kmsclient: build request: %w", err)
|
||||
}
|
||||
@@ -588,7 +603,7 @@ func (c *Client) getToken(ctx context.Context) (string, error) {
|
||||
return c.accessToken, nil
|
||||
}
|
||||
|
||||
tokenURL := c.iamEndpoint + "/v1/iam/login/oauth/access_token"
|
||||
tokenURL := c.iamEndpoint + "/v1/iam/oauth/token"
|
||||
form := url.Values{
|
||||
"grant_type": {"client_credentials"},
|
||||
"client_id": {c.clientID},
|
||||
|
||||
@@ -223,6 +223,69 @@ func TestPut_SendsCanonicalPayload(t *testing.T) {
|
||||
if gotBody["path"] != "providers/square/dev" || gotBody["name"] != "access_token" || gotBody["value"] != "sq_abc" {
|
||||
t.Errorf("body = %+v, want path=providers/square/dev name=access_token value=sq_abc", gotBody)
|
||||
}
|
||||
// env MUST be on the write body — the server now rejects omitted env. With
|
||||
// no Config.Env set it defaults to "default" and is sent explicitly.
|
||||
if gotBody["env"] != "default" {
|
||||
t.Errorf("body env = %q, want default (sent explicitly, never omitted)", gotBody["env"])
|
||||
}
|
||||
}
|
||||
|
||||
// A client configured for a specific env sends it on the write body, so the
|
||||
// write lands in that env's record rather than the server's legacy default.
|
||||
func TestPut_SendsConfiguredEnv(t *testing.T) {
|
||||
iam := mockIAM(t, "tok")
|
||||
defer iam.Close()
|
||||
|
||||
var gotBody map[string]string
|
||||
kms := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_ = json.NewDecoder(r.Body).Decode(&gotBody)
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"ok": true})
|
||||
}))
|
||||
defer kms.Close()
|
||||
|
||||
c, _ := New(Config{Endpoint: kms.URL, IAMEndpoint: iam.URL, ClientID: "i", ClientSecret: "s", Org: "hanzo", Env: "prod"})
|
||||
if err := c.Put(context.Background(), "iam-passwords", "Z_PASSWORD", "s3cr3t"); err != nil {
|
||||
t.Fatalf("Put: %v", err)
|
||||
}
|
||||
if gotBody["env"] != "prod" {
|
||||
t.Errorf("body env = %q, want prod", gotBody["env"])
|
||||
}
|
||||
}
|
||||
|
||||
// Reads and deletes carry ?env= too, so they target the configured env
|
||||
// instead of relying on any server default.
|
||||
func TestGetDelete_SendEnvQuery(t *testing.T) {
|
||||
iam := mockIAM(t, "tok")
|
||||
defer iam.Close()
|
||||
|
||||
var gotQuery, gotMethod string
|
||||
kms := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotQuery = r.URL.RawQuery
|
||||
gotMethod = r.Method
|
||||
if r.Method == http.MethodGet {
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"secret": map[string]any{"value": "v"}})
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}))
|
||||
defer kms.Close()
|
||||
|
||||
c, _ := New(Config{Endpoint: kms.URL, IAMEndpoint: iam.URL, ClientID: "i", ClientSecret: "s", Org: "hanzo", Env: "prod"})
|
||||
|
||||
if _, err := c.Get(context.Background(), "iam-passwords", "Z_PASSWORD"); err != nil {
|
||||
t.Fatalf("Get: %v", err)
|
||||
}
|
||||
if gotMethod != http.MethodGet || gotQuery != "env=prod" {
|
||||
t.Errorf("GET query = %q (method %s), want env=prod", gotQuery, gotMethod)
|
||||
}
|
||||
|
||||
if err := c.Delete(context.Background(), "iam-passwords", "Z_PASSWORD"); err != nil {
|
||||
t.Fatalf("Delete: %v", err)
|
||||
}
|
||||
if gotMethod != http.MethodDelete || gotQuery != "env=prod" {
|
||||
t.Errorf("DELETE query = %q (method %s), want env=prod", gotQuery, gotMethod)
|
||||
}
|
||||
}
|
||||
|
||||
func TestList_UsesCanonicalPath(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user