Compare commits

...
24 Commits
Author SHA1 Message Date
z 8c64ef628e docs: modernize + LLM.md + cross-links (one-way SDK model) 2026-07-24 12:43:36 -07:00
z e7b64bfad8 docs: modernize + LLM.md + cross-links (one-way SDK model) 2026-07-24 12:43:34 -07:00
zandGitHub 53a4a5bb5d Merge decruft: drop dead Embedded.HTTPAddr accessor 2026-07-18 11:02:51 -07:00
Hanzo Dev c5d633bf3a refactor(embed): drop dead Embedded.HTTPAddr accessor
Provably-dead exported accessor: no in-repo callers (only cmd/kmsd and
mount.go consume the root package) and no external importer of
github.com/hanzoai/kms across the tree uses it. Its backing httpAddr
field was write-only and its comment was wrong (stored the raw config
addr, never a resolved listener address). Removes field, assignment,
and accessor. Build+vet green.
2026-07-18 11:02:07 -07:00
Hanzo Dev f021830ebf ci: resolve private luxfi modules in the kms release build
The binaries job built with GOPRIVATE='' so go verified the first-party
luxfi/* modules against the public sumdb — force-moved upstream tags never
match, so every release failed with 'checksum mismatch / SECURITY ERROR'
(luxfi/vm@v1.3.1). Set GOPRIVATE + GONOSUMDB for luxfi/* + hanzoai/* and add a
git-config token (GH_PAT, GITHUB_TOKEN fallback) so the modules resolve directly
via git and skip the sumdb. Dockerfile: GONOSUMCHECK (removed by Go) -> GONOSUMDB,
guard the token, x-access-token form; docker job passes GH_PAT so it can read
luxfi.
2026-07-18 09:36:19 -07:00
Hanzo Dev 90c5f81271 build(deps): bump luxfi/kms v1.11.8 -> v1.12.4
Latest v1.x of the canonical KMS server. luxfi/keys (v1.4.1) and
luxfi/crypto (v1.20.2) were already at latest v1.x via the module graph.
luxfi/mpc stays out of the graph by design: threshold signing is delegated
over ZAP to an external MPC daemon (cfg.MPCAddr / MPCVaultID) through
luxfi/kms's pkg/mpc client; this module never links the standalone daemon.

GOWORK=off go build/vet/test green; test delta vs HEAD = 0.
2026-07-18 01:15:58 -07:00
zandGitHub 963a9a2be5 Merge chore/debrand-o11y-comment: debrand SigNoz -> o11y in telemetry comment 2026-07-18 00:13:38 -07:00
Hanzo Dev fc69bb81f6 chore(o11y): debrand SigNoz -> o11y in telemetry comment 2026-07-18 00:13:25 -07:00
zandGitHub 006dda86c7 Merge chore/fork-hygiene-notice: Infisical provenance + deviations in NOTICE 2026-07-18 00:08:59 -07:00
23be765f2f feat: migrate SQLite driver to hanzoai/sqlite (#25)
Swap the audit-trail SQLite driver from modernc.org/sqlite to hanzoai/
sqlite, the dual-backend drop-in that registers the "sqlite" driver name
under both CGO (mattn+SQLCipher) and !cgo (modernc) builds.

- audit.go: blank import + storage doc comment rebranded.
- red_hardening_test.go: swap the test-side blank import too. Both files
  had to move together: under CGO=1 hanzoai/sqlite registers "sqlite" via
  mattn while a lingering direct modernc import ALSO registers it, causing
  `panic: sql: Register called twice for driver sqlite` at test init.
  Swapping both leaves a single registration under both build configs.

Security: SQLite in KMS backs ONLY the plaintext audit trail (KMS_AUDIT_DB);
secrets are MPC-rooted and never stored in SQLite. The audit DB is opened
with a nil key via sql.Open("sqlite", ...&_pragma=...), so it stays
plaintext under both backends — the swap does not change the at-rest path.
DSN is already modernc-style (_pragma=NAME(VALUE)); no conversion needed.

Co-authored-by: hanzo-dev <dev@hanzo.ai>
2026-07-17 22:46:12 -07:00
zeekayandClaude Opus 4.8 7ce41309a8 build(deps): bump kms/sdk/go luxfi/keys v1.2.0 -> v1.4.1 (ServiceIdentity consumer)
The kms Go SDK module directly imports luxfi/keys (NewServiceIdentity /
ServiceIdentity). Its v1.2.0 pin was force-moved; adopt the stable v1.4.1,
regenerate go.sum from the current remote. Builds clean (CGO_ENABLED=0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:00:15 -07:00
zeekayandClaude Opus 4.8 342e0126a4 build(deps): adopt unified luxfi stack via keys v1.4.1 (unbreak force-moved keys/zap-proto + vanished geth v1.16.98)
kms pinned luxfi/keys v1.4.0 whose remote tag was force-moved (content
5153d639→80a3745a → go.sum checksum mismatch), plus zap-proto/http v0.1.0
(also force-moved) and geth v1.16.98 (deleted tag). Only keys v1.4.1 is
byte-stable; adopting it floors the unified luxfi stack and regenerates
go.sum from the current remote. kmsd + kms (pure-Go CGO_ENABLED=0, ZapDB)
build clean; the ServiceIdentity surface (keys.NewServiceIdentity) is
unchanged (v1.4.1 = local v1.4.0 identity work + geth bump).

go.mod/go.sum only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 00:59:00 -07:00
Hanzo Dev ae84372e6c chore(notice): state Infisical derivation provenance and deviations; sweep product branding 2026-07-13 15:56:39 -07:00
hanzo-dev 4a32bd9802 ci: route linux/amd64 jobs to hanzo-build-linux-amd64 ARC scale set
Claude-Session: https://claude.ai/code/session_01RFrWpXc1BsqfrFYMbyDusJ
2026-07-10 21:52:38 -07:00
828da1b916 fix(kms): require explicit env on secret writes (no silent default) (#22)
env is a first-class component of the storage key
(kms/secrets/{path}/{env}/{name}); a POST/PATCH that omitted env used to
silently default to "default", committing the write to a bucket that
project/env/path readers (kms-operator, cluster syncs) never resolve. That
split landed an IAM z-password in env=default while prod kept serving the
stale value. POST/PATCH now fail loud with 400 when env is omitted;
GET/DELETE/LIST keep a compat default for legacy readers (a read/delete
can't plant a value another reader trusts).

Root cause on the client: sdk/go/kmsclient dropped env on every HTTP op
(c.env was used only on the ZAP path), so a client configured for env=prod
still wrote to env=default over HTTP. The HTTP path now sends c.env on the
write body and as ?env= on read/delete, matching the ZAP path. Built
in-tree via a local replace so cmd/kms and cmd/kms-fetch ship the fix; the
two Dockerfiles stage sdk/go/go.mod so `go mod download` resolves the
replaced module.

Regression tests: write without env -> 400; write env=prod is readable via
the operator's project/env/path resolution (sha256 round-trip, values never
printed) and is not visible in env=default.

Claude-Session: https://claude.ai/code/session_01D4FSvT3UfhrFJNQjrctjEj

Co-authored-by: hanzo-dev <dev@hanzo.ai>
2026-07-08 07:21:40 -07:00
d378702c23 build: base on ghcr.io/hanzoai/nodejs:v24.18.0 (Node 24 + sqlite3)
Adopt the canonical Hanzo Node base image. Node 24 uniform across the
fleet; sqlite3 bundled (node:sqlite builtin + native better-sqlite3
toolchain). One base, one way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 12:49:29 -07:00
2e8fc55a9b fix(kmsd): gate per-request access log behind KMS_LOG_LEVEL=debug (#21)
The zip access-log middleware logged one Info "request" line per call —
~4k lines/min, the entire KMS log volume in ClickHouse. Startup, shutdown,
authz-denials (kms_auth_reject), and handler errors are logged elsewhere and
stay at info. Default (info) is now quiet; opt back in with KMS_LOG_LEVEL=debug.

Co-authored-by: hanzo-dev <dev@hanzo.ai>
2026-07-04 04:32:01 -07:00
hanzo-dev 65af686c34 fix(auth): KMS login proxies to the current IAM token endpoint
IAM moved its OAuth token endpoint to /v1/iam/oauth/token; the login proxy
(and the Go SDK client) still called the retired /v1/iam/login/oauth/access_token,
which now 404s — so /v1/kms/auth/login returned 401 for every valid machine
identity. This broke fresh KMSSecret-operator logins and the hanzoai/ci deploy-
cred fetch. Point both at /v1/iam/oauth/token (form/body unchanged); update LLM.md.

Note: requires a KMS image release + redeploy (ghcr.io/luxfi/kms) to take effect.
2026-07-03 16:32:40 -07:00
Hanzo DevandGitHub ea90a0ebf0 feat(o11y): emit OTel traces via go.opentelemetry.io/otel (service.name=hanzo-kms) (#20)
Env-gated on OTEL_EXPORTER_OTLP_ENDPOINT; non-fatal; clean no-op when unset (safe to ship before the collector is live). Installs the global tracer provider with a service.name resource so the console Monitoring tab filters this product. Mirrors ai/object/telemetry.go. Traces-only; metrics/logs are a tracked follow-up.
2026-07-02 20:18:57 -07:00
zeekay 15e1cf9175 Merge remote-tracking branch 'origin/rip/api-to-v1' 2026-07-02 13:07:26 -07:00
Hanzo Dev 0de5ca4cbd chore: OSS attribution — verify/fix Infisical (remove phantom ee/ license claim) 2026-07-02 12:48:24 -07:00
zeekayandClaude Opus 4.8 e71712e1dd fix(kms): 404 any /api/ at the SPA catch-all — no /api/ surface exists
The kms.hanzo.ai "/api/v1/* returns 200" signal was a false positive: the
frontend catch-all (registerFrontend) served index.html for any unmatched
GET, including /api/v1/auth/universal-auth/login. No /api/ route is
registered anywhere in this repo — the server is already 100% /v1/kms/*
native (Go mux, both Go SDKs, and the React frontend all use /v1/kms).

The catch-all's defense-in-depth bail-out excluded /v1/, /healthz, /health
but not /api/, so an /api/-shaped GET fell through to the SPA fallback and
answered 200. This made a nonexistent legacy /api/ backend look live.

Add /api/ (and bare /api) to the bail-out so it 404s at the wire. The ONE
canonical prefix is /v1/kms; there is no /api/, and now the server says so
unambiguously. No alias, no rewrite, no backward compat.

frontend_spa_test.go locks the invariant: /api/* → 404, /v1/ and /health →
404, /healthz → 200 (real handler wins), client routes + assets → 200.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 12:45:50 -07:00
Hanzo Dev 5f3b1bd6cc chore: zip v1.1.0 — one-verb Listen + free MCP; app.ListenHTTP -> app.Listen("http://"...)
Bump zap-proto/zip v1.0.0 -> v1.1.0 (final API: single Listen, transport as
address scheme; free MCP tool surface). go.sum re-recorded vs immutable proxy.
2026-07-02 00:22:35 -07:00
Hanzo Dev 30eb88a821 chore: zip canonical home — hanzoai/zip -> zap-proto/zip@v1.0.0 + Listen->ListenHTTP
Pure move to the ZAP-family canonical framework home + the ListenHTTP rename
(verb-first, was Listen). go.sum re-recorded against the immutable public proxy.
Build green.
2026-07-01 23:23:10 -07:00
24 changed files with 937 additions and 622 deletions
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -8,7 +8,7 @@ on:
jobs:
test:
runs-on: [self-hosted, linux, amd64]
runs-on: hanzo-build-linux-amd64
steps:
- uses: actions/checkout@v4
+13 -1
View File
@@ -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
View File
@@ -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 . .
+4
View File
@@ -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/*" \
+78 -244
View File
@@ -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)
+16
View File
@@ -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).
+53 -156
View File
@@ -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.
[![Status](https://img.shields.io/badge/status-stable-green)]()
[![License](https://img.shields.io/badge/license-MIT-blue)]()
## Quick start
```bash
docker run -p 80:80 ghcr.io/hanzoai/kms:latest
```
Open `http://localhost:80`.
[![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
[![Go](https://img.shields.io/badge/go-1.26-00ADD8)]()
## 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)
+1 -1
View File
@@ -1 +1 @@
0.159.4
0.159.5
+2 -2
View File
@@ -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
View File
@@ -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)
+83
View File
@@ -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
View File
@@ -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")
+47 -18
View File
@@ -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
}
+165
View File
@@ -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)
}
}
+125
View File
@@ -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)
}
}
+57 -28
View File
@@ -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
+93 -66
View File
@@ -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=
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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=
+18 -3
View File
@@ -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},
+63
View File
@@ -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) {