the ZAP dial addresses its request to the session it goes out on

luxfi/kms v1.12.16 gives the hybrid handshake a second output beside the
session key: a binding that names the channel, computable only by the two
endpoints that ran it. Every envelope carries the binding of the session
it is sent on, and the server honours it only there. A request carried to
some other peer is inert — it cannot be re-signed, and no other channel
can produce the name it holds.

RequireSession goes away with it. A client without a session now has
nothing to address a request to and cannot speak at all, so the dial
agrees a hybrid session or fails; that is the shape of the code rather
than the value of a field.

List builds each answer from the coordinate the record came back with.
A path is a subtree root, so a result can sit below the one that was
asked for, and re-joining the query prefix would name it somewhere it is
not.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-08-15 04:51:55 -07:00
co-authored by Hanzo Dev
parent d7e864494f
commit c0486a09bc
3 changed files with 27 additions and 36 deletions
+2 -5
View File
@@ -3,10 +3,10 @@ module github.com/hanzoai/kms/sdk/go
go 1.26.5
require (
github.com/luxfi/crypto v1.20.2
github.com/luxfi/crypto v1.20.5
github.com/luxfi/ids v1.3.2
github.com/luxfi/keys v1.4.2
github.com/luxfi/kms v1.12.15
github.com/luxfi/kms v1.12.16
github.com/luxfi/log v1.4.3
github.com/luxfi/zap v1.2.6
github.com/luxfi/zapdb v1.10.6
@@ -42,7 +42,6 @@ require (
github.com/luxfi/cache v1.3.1 // indirect
github.com/luxfi/constants v1.6.2 // indirect
github.com/luxfi/container v0.2.2 // indirect
github.com/luxfi/formatting v1.1.1 // indirect
github.com/luxfi/geth v1.20.2 // indirect
github.com/luxfi/go-bip32 v1.1.0 // indirect
github.com/luxfi/go-bip39 v1.2.0 // indirect
@@ -51,10 +50,8 @@ require (
github.com/luxfi/mdns v0.1.1 // indirect
github.com/luxfi/metric v1.8.1 // indirect
github.com/luxfi/mock v0.1.1 // indirect
github.com/luxfi/protocol v0.0.2 // indirect
github.com/luxfi/sampler v1.1.0 // indirect
github.com/luxfi/tls v1.1.1 // indirect
github.com/luxfi/vm v1.3.7 // 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
+4 -14
View File
@@ -117,10 +117,8 @@ 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.2 h1:/IunVpvNuwsqwAYjJSibDskmH/39y4pRKQxrrMLF5Qk=
github.com/luxfi/container v0.2.2/go.mod h1:owTgLOeBnjPK9Ow+/M9Cy9ulLNit6O8M/tWn6rqq7C4=
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/crypto v1.20.5 h1:kL69Ewyr90A/wQsTnIK8Zj6cHUPYvM1WHhgSErc2Yh0=
github.com/luxfi/crypto v1.20.5/go.mod h1:gic3n9puSJhZFonBfmIwc5Cck/SMqetxpcfzrk9gna8=
github.com/luxfi/geth v1.20.2 h1:XwNVyIpb4ip4wy/SsQS8fHdVEVlmsvLNwGPykFDvutw=
github.com/luxfi/geth v1.20.2/go.mod h1:5vJMlOrSvz4ORC+fe16k+i0CUqTadmiFi9ldAHb5TE4=
github.com/luxfi/go-bip32 v1.1.0 h1:zjy19WKa1KJnGamRNyOZM3l/MPtV/sax7M4NMPwLHZQ=
@@ -129,14 +127,10 @@ 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/keys v1.4.2 h1:a9Wy1XwXezJn7+q4wwxSmJiG3RWffegeNX15yGYXldc=
github.com/luxfi/keys v1.4.2/go.mod h1:M+PbXq4/cU4KDXRMZ2SzuEjkB9NV4Y1pKLZbQehSHe4=
github.com/luxfi/kms v1.11.4 h1:mCGDItWs3MD6IsibbCZb5u5AlcySESGnKkyk6z6lrig=
github.com/luxfi/kms v1.11.4/go.mod h1:Ku4LHH6J1oO9MaZDbn1e2fMnkRsN4QqV89DGwRFP/Ak=
github.com/luxfi/kms v1.12.15 h1:wga2JOOfhdbPlomGKOTnG6WhKzR1mMMUrWENzTdIjTU=
github.com/luxfi/kms v1.12.15/go.mod h1:d1U0FVdCqHXTwspj4wbhhFAuD3cNnbYU6YzYlnrWbl4=
github.com/luxfi/kms v1.12.16 h1:klxVF0nfssjEGLxIG2AX4a6aiXIi1g2HoUCgQIYtw/k=
github.com/luxfi/kms v1.12.16/go.mod h1:hSrGkWAjg7VOW+ap4F3BiHQpBiKz8JVCi/h8nIy/OPs=
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=
@@ -151,14 +145,10 @@ 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.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.1.1 h1:BSZ0gHSp7U8vzlmzx7WSSCz+b7Ky4JtD9HDDhn7vrDg=
github.com/luxfi/tls v1.1.1/go.mod h1:+5TDy8UtLL+tz124brZzpUDBRj+sKrq0JFqdmpMUHgw=
github.com/luxfi/vm v1.3.7 h1:3FJOj9z4WrWb8Jupfo2avLQV25D5JoBUzYCcYTkwWMY=
github.com/luxfi/vm v1.3.7/go.mod h1:lxjv6dh/i8Cj82hZzKWvnbzOG+5wqFNlAeYfDBeCkGM=
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.6 h1:K62BsDk64Va72D1T7DHtvtFjfR0ExNl57ytCdPNMemk=
+21 -17
View File
@@ -217,13 +217,16 @@ func newHTTP(cfg Config, env string) (*Client, error) {
// We dial eagerly so callers see the dial failure at construction time
// rather than on first Get. The connection is reused for every call.
//
// The dial requires an AEAD session from the X25519 + ML-KEM-768 hybrid
// handshake, and requires that session to have run ML-KEM rather than
// X25519 alone. That is a constant here, not a setting: everything this
// client carries is a secret, so there is no caller for whom a plaintext
// or classical-only channel is the right answer, and a setting is a thing
// an on-path adversary gets to influence by making the handshake fail.
// A peer that will not agree a hybrid session does not get the request.
// The dial agrees an X25519 + ML-KEM-768 hybrid session or it fails, and
// a peer that answers classical-only is refused. Neither is a setting:
// everything this client carries is a secret, so there is no caller for
// whom a plaintext or classical-only channel is the right answer, and a
// setting is a thing an on-path adversary gets to influence by making
// the handshake fail.
//
// The session is also what the request is addressed to. Every envelope
// names the channel it goes out on and is honoured only there, so a
// request carried to some other peer buys that peer nothing.
func newZAP(cfg Config, env string) (*Client, error) {
if cfg.Identity == nil {
return nil, errors.New("kmsclient: identity is required (zap transport)")
@@ -239,7 +242,6 @@ func newZAP(cfg Config, env string) (*Client, error) {
DefaultPath: cfg.Org, // unused by GetAt/PutAt below; harmless
IdentityHeader: cfg.Identity.Header,
Signer: cfg.Identity.ServiceIdentity,
RequireSession: true,
}
if !mdns {
zcfg.PeerAddr = host
@@ -388,21 +390,23 @@ func (c *Client) GetJSON(ctx context.Context, path, name string, dst any) error
// caller under the given prefix.
//
// On the HTTP path: GET /v1/kms/secrets?prefix=…
// On the ZAP path: OpSecretList (0x0042) — names only, no path
// information is surfaced; we prepend the requested prefix so callers
// see identical output across transports.
// On the ZAP path: OpSecretList (0x0042), which answers with each
// record's own coordinate. A path prefix is a subtree root, so a result
// can sit below it; each answer is built from the path it came back
// with rather than the one that was asked for, or a record one segment
// deeper would be named somewhere it is not.
func (c *Client) List(ctx context.Context, pathPrefix string) ([]string, error) {
if c.transport == "zap" {
names, err := c.zap.ListAt(ctx, pathPrefix, c.env)
refs, err := c.zap.ListAt(ctx, pathPrefix, c.env)
if err != nil {
return nil, err
}
out := make([]string, 0, len(names))
for _, n := range names {
if pathPrefix != "" {
out = append(out, pathPrefix+"/"+n)
out := make([]string, 0, len(refs))
for _, ref := range refs {
if ref.Path != "" {
out = append(out, ref.Path+"/"+ref.Name)
} else {
out = append(out, n)
out = append(out, ref.Name)
}
}
return out, nil