zapwire: enforce Z-Wing PQ for all netrunner control RPC

Replace plain TCP zap.Dial / zap.Listen with zwing.DialZAP /
zwing.ListenZAP. zapwire.Dial and zapwire.NewServer now require a
*zwing.Config carrying a LocalIdentity — there is no cleartext mode.
Every netrunner control connection is X-Wing-encapsulated,
ChaCha20-Poly1305-encrypted, and identity-pinned via Ed25519 +
ML-DSA-65 hybrid signatures.

Wire format and opcode set are unchanged; only the dial/listen
seams are upgraded. The 14 e2e tests are updated to mint ephemeral
identities per test (newTestRig) and pin the server side, which
also proves the handshake actually authenticates.

Pairs with luxfi/zwing v0.2.1 (DialZAP / ListenZAP) and luxfi/api
v1.0.10 (zap.NewListener).
This commit is contained in:
Hanzo AI
2026-05-06 12:53:13 -07:00
parent 5e3f2779df
commit 7a2d1f0329
5 changed files with 129 additions and 67 deletions
+14 -13
View File
@@ -1,6 +1,6 @@
module github.com/luxfi/netrunner
go 1.26.1
go 1.26.2
exclude google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1
@@ -11,11 +11,11 @@ require (
github.com/btcsuite/btcd/btcutil v1.1.6
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0
github.com/luxfi/address v1.0.1
github.com/luxfi/api v1.0.3
github.com/luxfi/api v1.0.10
github.com/luxfi/atomic v1.0.0
github.com/luxfi/config v1.1.2
github.com/luxfi/constants v1.4.6
github.com/luxfi/crypto v1.17.44
github.com/luxfi/crypto v1.18.3
github.com/luxfi/genesis v1.7.1
github.com/luxfi/geth v1.16.76
github.com/luxfi/go-bip39 v1.1.2
@@ -25,7 +25,7 @@ require (
github.com/luxfi/math v1.4.0
github.com/luxfi/metric v1.5.0
github.com/luxfi/node v1.23.21
github.com/luxfi/p2p v1.19.2-zap
github.com/luxfi/p2p v1.19.2
github.com/luxfi/protocol v0.0.3
github.com/luxfi/sdk v1.16.46
github.com/luxfi/utxo v0.2.5
@@ -38,7 +38,7 @@ require (
github.com/spf13/cobra v1.10.2
github.com/stretchr/testify v1.11.1
go.uber.org/multierr v1.11.0
golang.org/x/crypto v0.49.0
golang.org/x/crypto v0.50.0
golang.org/x/mod v0.34.0
golang.org/x/sync v0.20.0
google.golang.org/genproto/googleapis/api v0.0.0-20260217215200-42d3e9bedb6d
@@ -77,12 +77,12 @@ require (
github.com/klauspost/crc32 v1.3.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/luxfi/accel v1.0.1 // indirect
github.com/luxfi/accel v1.0.7 // indirect
github.com/luxfi/age v1.4.0 // indirect
github.com/luxfi/codec v1.1.4 // indirect
github.com/luxfi/compress v0.0.5 // indirect
github.com/luxfi/concurrent v0.0.3 // indirect
github.com/luxfi/consensus v1.22.69 // indirect
github.com/luxfi/consensus v1.22.85 // indirect
github.com/luxfi/container v0.0.4 // indirect
github.com/luxfi/filesystem v0.0.1 // indirect
github.com/luxfi/formatting v1.0.1 // indirect
@@ -92,6 +92,7 @@ require (
github.com/luxfi/sys v0.0.0-20260110090042-50187ec5ffd8 // indirect
github.com/luxfi/timer v1.0.2 // indirect
github.com/luxfi/utils v1.1.4 // indirect
github.com/luxfi/zwing v0.2.1 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
@@ -108,7 +109,7 @@ require (
github.com/tinylib/msgp v1.6.1 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/oauth2 v0.35.0 // indirect
golang.org/x/term v0.41.0 // indirect
golang.org/x/term v0.42.0 // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
@@ -131,7 +132,7 @@ require (
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/consensys/gnark-crypto v0.19.2 // indirect
github.com/consensys/gnark-crypto v0.20.1 // indirect
github.com/crate-crypto/go-eth-kzg v1.5.0 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
@@ -181,7 +182,7 @@ require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.67.5 // indirect
github.com/prometheus/procfs v0.19.2 // indirect
github.com/prometheus/procfs v0.20.1 // indirect
github.com/rs/cors v1.11.1 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
@@ -207,12 +208,12 @@ require (
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
go.uber.org/mock v0.6.0 // indirect
go.uber.org/zap v1.27.1 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect
golang.org/x/net v0.52.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.35.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.43.0 // indirect
gonum.org/v1/gonum v0.17.0 // indirect
+28 -12
View File
@@ -68,8 +68,8 @@ github.com/cockroachdb/redact v1.1.6 h1:zXJBwDZ84xJNlHl1rMyCojqyIxv+7YUpQiJLQ7n4
github.com/cockroachdb/redact v1.1.6/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb h1:3bCgBvB8PbJVMX1ouCcSIxvsqKPYM7gs72o0zC76n9g=
github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
github.com/consensys/gnark-crypto v0.19.2 h1:qrEAIXq3T4egxqiliFFoNrepkIWVEeIYwt3UL0fvS80=
github.com/consensys/gnark-crypto v0.19.2/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0=
github.com/consensys/gnark-crypto v0.20.1 h1:PXDUBvk8AzhvWowHLWBEAfUQcV1/aZgWIqD6eMpXmDg=
github.com/consensys/gnark-crypto v0.20.1/go.mod h1:RBWrSgy+IDbGR69RRV313th3M/aZU1ubk2om+qHuTSc=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
@@ -252,12 +252,16 @@ github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzW
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
github.com/luxfi/accel v1.0.1 h1:JtkOS5tSjdrVRGDHiuXxR6AIBaiSN1NpQc4FobgSl5w=
github.com/luxfi/accel v1.0.1/go.mod h1:j5NCg8eKiQwhbRuJqa5G1oozcKp8HxJWMnrNA/CocWk=
github.com/luxfi/accel v1.0.7 h1:ksHieAp50umwqxqgyHk9WiOmXM54kia3IEDb5H7FsM8=
github.com/luxfi/accel v1.0.7/go.mod h1:iZD3oxffiMEIT/KvzD8bgwC/cBn4AYlMW3QJpbRa4RE=
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.4.0 h1:tU5q65RQSQdaVq64Z/DVUhiPQMoMPQT6pr41LsvG0AQ=
github.com/luxfi/age v1.4.0/go.mod h1:iAYAxgvrXxcy746+Ovh/eWWDuF9teJLNcCSSOX9RYW0=
github.com/luxfi/api v1.0.3 h1:5AfLa2rQMaBFAlPFTbUar4G4a0zFJe1laE6AXDTZz78=
github.com/luxfi/api v1.0.3/go.mod h1:a+SIsQW3gGdximHZEpmU/DaGPkm/Ypb6uyxOTmfXtyM=
github.com/luxfi/api v1.0.8 h1:9mi9PLmmXm8vHIGvAUvg59QmR63QbyVN1r9YmbYId+8=
github.com/luxfi/api v1.0.8/go.mod h1:5OFWvZF+PbyuvLCDyKKpXCA/xp+LxJ32yOhNEKwQFN0=
github.com/luxfi/api v1.0.10 h1:tqtiCX8DcBsFG0JEhKy7eUNI+42+m2DoPjLjEh5TE5E=
github.com/luxfi/api v1.0.10/go.mod h1:5OFWvZF+PbyuvLCDyKKpXCA/xp+LxJ32yOhNEKwQFN0=
github.com/luxfi/atomic v1.0.0 h1:xUV60MuzRvXngaQ1sM0yVC2v4TRoLlUGkkH7M9PS4yw=
github.com/luxfi/atomic v1.0.0/go.mod h1:0G2mTlQ6TXWHICUHrUUPu1/qAiIyR4gSZ2tva9ci/bI=
github.com/luxfi/cache v1.2.1 h1:kAzOS55/hmYeNKR+0HAKv4ma48Y6JjkI8UQeqdZ8bfI=
@@ -270,14 +274,16 @@ github.com/luxfi/concurrent v0.0.3 h1:eJyv1fhaC0jMLMw6+QS774cUmp7GK+ouMgvLCqnC7c
github.com/luxfi/concurrent v0.0.3/go.mod h1:Aj/FR5NpM0cB2P4Nt3+tz9+dV6V+LUW4HuMgSjwq5hw=
github.com/luxfi/config v1.1.2 h1:iCUewwm7oT7ckRNyQkrVHZ5Ge+l+FadciGX/zyaPo/k=
github.com/luxfi/config v1.1.2/go.mod h1:z6t0a5pGpQz2uDW2qJPLX5fZ/eWbpiNa51gBc63ebFk=
github.com/luxfi/consensus v1.22.69 h1:f9udx1mOj5lB4HiDju+Z+hoCkl+FoHJw/fjLVc8ePuw=
github.com/luxfi/consensus v1.22.69/go.mod h1:cYnIxzIuRR+gpNXMyI07HlgYBwwhgO1PCP6/XinU+2E=
github.com/luxfi/consensus v1.22.85 h1:S/XpPF+eDapbZb4AIu0Vr1GXzg/sAxjrAff2M2mkOx0=
github.com/luxfi/consensus v1.22.85/go.mod h1:y6y+bVjvpDLkkTBCCKLcch6R4PRW8EA+lTfr+6sB6F8=
github.com/luxfi/constants v1.4.6 h1:9a/ED2t/sRfeOfvNur2lssyjaw2I7tSyddTJHVSDYWg=
github.com/luxfi/constants v1.4.6/go.mod h1:hOszZ2NDQ8gMZKncfcZ67PXkb5OIbnwAzXC3oFbQwW0=
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.17.44 h1:N3uWiHs1hC0DGsr2jwIdTfsEcpVR1Da583Z1LcqOaH8=
github.com/luxfi/crypto v1.17.44/go.mod h1:3ahTd4kNZT5WtEiVWbzGtd05wUjjrlA7eSkqeDNL8U4=
github.com/luxfi/crypto v1.18.3 h1:f6mBUHNjZl9/9nXPSqjq+oJttrPl8slvcZ49Vi7jKw4=
github.com/luxfi/crypto v1.18.3/go.mod h1:qBtCJDT6p6aNa2hMtPxYwC688Bnsb3Amso8q09GsMls=
github.com/luxfi/database v1.18.3 h1:gg+xwhKUxXa7fDoOD8IS91E71QqoEtcDCl2nfS61Jgg=
github.com/luxfi/database v1.18.3/go.mod h1:sv0pYCGKlK1aNJTICxFUDpVWCJTigoLlshHmV/1pg7c=
github.com/luxfi/evm v0.8.30 h1:Z3YfZmcmnn8UghmRex3B6gVZ7zblqgEBNeNj7Tcvedw=
@@ -316,8 +322,8 @@ github.com/luxfi/net v0.0.4 h1:z1d6Q5c9/79jb4vF0XwBBjlF5swH5NsgfaXA+Pgojq8=
github.com/luxfi/net v0.0.4/go.mod h1:QvgHzCa767cVWtPpui0P7HW1IrA2+c++hhvaQ/t0yyw=
github.com/luxfi/node v1.23.21 h1:87c4aee2Tf9B89wlgy4em2icV8Kq+wWmq8Cr2oceivo=
github.com/luxfi/node v1.23.21/go.mod h1:agZkrIfw8OdX0A3EzHr9OqTwq6w3cTUBuR1W3l02rU0=
github.com/luxfi/p2p v1.19.2-zap h1:WKpbTonYxk4+m9BE4AD2X4g7kx0PmZ01zamncScDoUg=
github.com/luxfi/p2p v1.19.2-zap/go.mod h1:m7sRplOIXB8FRR7ScjMNLl7ioTEZhLKDJzs5Cc3Lmok=
github.com/luxfi/p2p v1.19.2 h1:uqZq7ofmEDbXlTkv1QThtci01Q+dmDkNmAPeORIyP8E=
github.com/luxfi/p2p v1.19.2/go.mod h1:tI9Bt1R0ouvVtJvXG4e20GlGeV4AR230k4mFF9Vglzk=
github.com/luxfi/precompile v0.4.7 h1:L47mOmWqCCQ9zkogUDjhvXXgBIJH9gx7ISGdZU4mHcM=
github.com/luxfi/precompile v0.4.7/go.mod h1:HrDssVg6EQjWhunwYEGUO5VngS8Veng6TRBQy0NuSMU=
github.com/luxfi/protocol v0.0.3 h1:Teytlu6Gbd0HqJXuDvV84ArqRabEFR40yDNNQUOCpVE=
@@ -358,6 +364,8 @@ github.com/luxfi/warp v1.18.5 h1:yXFCT+lnvzJHs8nVvfUCQ9wNvhtgbDGaXJkJeiwgKf4=
github.com/luxfi/warp v1.18.5/go.mod h1:SFyC529HDvbP/TWRAdYQSyJUliMa5JKFRtBrTLEElp4=
github.com/luxfi/zapdb v1.8.0 h1:E9hXMW1MTuS6kmDVs07j62gIq5qI5gZOdyzVfiBc1wg=
github.com/luxfi/zapdb v1.8.0/go.mod h1:Qukh3hDRD0MnxA6z+a28JTnXhN85AiLLgp6TYr4QAMc=
github.com/luxfi/zwing v0.2.1 h1:vWNhMerJiauiz3sAgBHS3vvRVw0lrVtbTimbhE/d03c=
github.com/luxfi/zwing v0.2.1/go.mod h1:8nixkEL3bhO2LrqVqhJ8WgT+QGUtnCPIQIhFQh9gQio=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
@@ -441,8 +449,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
@@ -551,8 +559,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -562,6 +570,8 @@ golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -616,10 +626,14 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -627,6 +641,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+14 -4
View File
@@ -20,19 +20,29 @@ import (
"github.com/luxfi/api/zap"
"github.com/luxfi/netrunner/zapwire/types"
"github.com/luxfi/zwing"
)
// Wire-protocol version. Bumped on breaking changes to encoding.
const ProtocolVersion uint32 = 1
// Client is the netrunner control RPC client over ZAP.
// Client is the netrunner control RPC client over a Z-Wing channel.
//
// All netrunner control traffic is post-quantum encrypted and mutually
// authenticated. There is no cleartext mode. If a control plane needs
// to talk to a netrunner server, it presents a Z-Wing identity, period.
type Client struct {
conn *zap.Conn
}
// Dial opens a ZAP control connection to a netrunner server.
func Dial(ctx context.Context, addr string) (*Client, error) {
conn, err := zap.Dial(ctx, addr, nil)
// Dial opens a ZAP control connection to a netrunner server over
// Z-Wing. cfg must carry a LocalIdentity; ExpectedRemote optionally
// pins the server's identity.
func Dial(ctx context.Context, addr string, cfg *zwing.Config) (*Client, error) {
if cfg == nil || cfg.LocalIdentity == nil {
return nil, errors.New("zapwire: zwing.Config with LocalIdentity required")
}
conn, err := zwing.DialZAP(ctx, addr, cfg)
if err != nil {
return nil, fmt.Errorf("zapwire: dial %s: %w", addr, err)
}
+64 -35
View File
@@ -9,15 +9,44 @@ import (
"time"
"github.com/luxfi/netrunner/zapwire/types"
"github.com/luxfi/zwing"
)
// runServer starts a zapwire server on 127.0.0.1:0 and returns a
// teardown that the test must call (via defer or t.Cleanup). It also
// gives a context whose cancel is wired into the teardown, so handlers
// see ctx.Done() before the listener closes.
func runServer(t *testing.T, be Backend) (*Server, func()) {
// testRig holds the ephemeral PQ identities used by an e2e test. The
// server identity is pinned by the client to prove that the Z-Wing
// handshake actually authenticated the server.
type testRig struct {
clientCfg *zwing.Config
serverCfg *zwing.Config
}
func newTestRig(t *testing.T) *testRig {
t.Helper()
srv, err := NewServer("127.0.0.1:0", be)
clientID, err := zwing.GenerateIdentity()
if err != nil {
t.Fatalf("client identity: %v", err)
}
serverID, err := zwing.GenerateIdentity()
if err != nil {
t.Fatalf("server identity: %v", err)
}
return &testRig{
clientCfg: &zwing.Config{
LocalIdentity: clientID,
ExpectedRemote: serverID.Public(),
},
serverCfg: &zwing.Config{LocalIdentity: serverID},
}
}
// runServer starts a zapwire server on 127.0.0.1:0 over a Z-Wing
// listener and returns a teardown that the test must call (via defer
// or t.Cleanup). It also gives a context whose cancel is wired into
// the teardown, so handlers see ctx.Done() before the listener closes.
func runServer(t *testing.T, be Backend) (*Server, *testRig, func()) {
t.Helper()
rig := newTestRig(t)
srv, err := NewServer("127.0.0.1:0", rig.serverCfg, be)
if err != nil {
t.Fatalf("NewServer: %v", err)
}
@@ -34,7 +63,7 @@ func runServer(t *testing.T, be Backend) (*Server, func()) {
_ = srv.Close()
<-done
}
return srv, teardown
return srv, rig, teardown
}
// stubBackend is a minimal Backend implementation for e2e tests.
@@ -240,13 +269,13 @@ func (b *stubBackend) Stop(ctx context.Context) (*types.StopResponse, error) {
}
func TestE2EPing(t *testing.T) {
srv, teardown := runServer(t, &stubBackend{pingPID: 42})
srv, rig, teardown := runServer(t, &stubBackend{pingPID: 42})
defer teardown()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := Dial(ctx, srv.Addr())
client, err := Dial(ctx, srv.Addr(), rig.clientCfg)
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -262,12 +291,12 @@ func TestE2EPing(t *testing.T) {
}
func TestE2ERPCVersion(t *testing.T) {
srv, teardown := runServer(t, &stubBackend{})
srv, rig, teardown := runServer(t, &stubBackend{})
defer teardown()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := Dial(ctx, srv.Addr())
client, err := Dial(ctx, srv.Addr(), rig.clientCfg)
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -283,12 +312,12 @@ func TestE2ERPCVersion(t *testing.T) {
}
func TestE2EHealth(t *testing.T) {
srv, teardown := runServer(t, &stubBackend{})
srv, rig, teardown := runServer(t, &stubBackend{})
defer teardown()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := Dial(ctx, srv.Addr())
client, err := Dial(ctx, srv.Addr(), rig.clientCfg)
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -313,12 +342,12 @@ func TestE2EHealth(t *testing.T) {
}
func TestE2EURIs(t *testing.T) {
srv, teardown := runServer(t, &stubBackend{})
srv, rig, teardown := runServer(t, &stubBackend{})
defer teardown()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := Dial(ctx, srv.Addr())
client, err := Dial(ctx, srv.Addr(), rig.clientCfg)
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -334,12 +363,12 @@ func TestE2EURIs(t *testing.T) {
}
func TestE2EStatus(t *testing.T) {
srv, teardown := runServer(t, &stubBackend{})
srv, rig, teardown := runServer(t, &stubBackend{})
defer teardown()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := Dial(ctx, srv.Addr())
client, err := Dial(ctx, srv.Addr(), rig.clientCfg)
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -370,12 +399,12 @@ func TestE2EStatus(t *testing.T) {
func TestE2EAddNode(t *testing.T) {
be := &stubBackend{}
srv, teardown := runServer(t, be)
srv, rig, teardown := runServer(t, be)
defer teardown()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := Dial(ctx, srv.Addr())
client, err := Dial(ctx, srv.Addr(), rig.clientCfg)
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -422,12 +451,12 @@ func TestE2EAddNode(t *testing.T) {
func TestE2ERemoveNode(t *testing.T) {
be := &stubBackend{}
srv, teardown := runServer(t, be)
srv, rig, teardown := runServer(t, be)
defer teardown()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := Dial(ctx, srv.Addr())
client, err := Dial(ctx, srv.Addr(), rig.clientCfg)
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -447,12 +476,12 @@ func TestE2ERemoveNode(t *testing.T) {
func TestE2ERestartNode(t *testing.T) {
be := &stubBackend{}
srv, teardown := runServer(t, be)
srv, rig, teardown := runServer(t, be)
defer teardown()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := Dial(ctx, srv.Addr())
client, err := Dial(ctx, srv.Addr(), rig.clientCfg)
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -493,12 +522,12 @@ func TestE2ERestartNode(t *testing.T) {
func TestE2EPauseNode(t *testing.T) {
be := &stubBackend{}
srv, teardown := runServer(t, be)
srv, rig, teardown := runServer(t, be)
defer teardown()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := Dial(ctx, srv.Addr())
client, err := Dial(ctx, srv.Addr(), rig.clientCfg)
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -518,12 +547,12 @@ func TestE2EPauseNode(t *testing.T) {
func TestE2EResumeNode(t *testing.T) {
be := &stubBackend{}
srv, teardown := runServer(t, be)
srv, rig, teardown := runServer(t, be)
defer teardown()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := Dial(ctx, srv.Addr())
client, err := Dial(ctx, srv.Addr(), rig.clientCfg)
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -543,12 +572,12 @@ func TestE2EResumeNode(t *testing.T) {
func TestE2EAttachPeer(t *testing.T) {
be := &stubBackend{}
srv, teardown := runServer(t, be)
srv, rig, teardown := runServer(t, be)
defer teardown()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := Dial(ctx, srv.Addr())
client, err := Dial(ctx, srv.Addr(), rig.clientCfg)
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -568,12 +597,12 @@ func TestE2EAttachPeer(t *testing.T) {
func TestE2ESendOutboundMessage(t *testing.T) {
be := &stubBackend{}
srv, teardown := runServer(t, be)
srv, rig, teardown := runServer(t, be)
defer teardown()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := Dial(ctx, srv.Addr())
client, err := Dial(ctx, srv.Addr(), rig.clientCfg)
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -605,12 +634,12 @@ func TestE2ESendOutboundMessage(t *testing.T) {
}
func TestE2EWaitForHealthy(t *testing.T) {
srv, teardown := runServer(t, &stubBackend{})
srv, rig, teardown := runServer(t, &stubBackend{})
defer teardown()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := Dial(ctx, srv.Addr())
client, err := Dial(ctx, srv.Addr(), rig.clientCfg)
if err != nil {
t.Fatalf("Dial: %v", err)
}
@@ -632,12 +661,12 @@ func TestE2EWaitForHealthy(t *testing.T) {
}
func TestE2EStop(t *testing.T) {
srv, teardown := runServer(t, &stubBackend{})
srv, rig, teardown := runServer(t, &stubBackend{})
defer teardown()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client, err := Dial(ctx, srv.Addr())
client, err := Dial(ctx, srv.Addr(), rig.clientCfg)
if err != nil {
t.Fatalf("Dial: %v", err)
}
+9 -3
View File
@@ -10,6 +10,7 @@ import (
"github.com/luxfi/api/zap"
"github.com/luxfi/netrunner/zapwire/types"
"github.com/luxfi/zwing"
)
// Backend is the interface a netrunner server implementation provides
@@ -40,12 +41,17 @@ type Server struct {
srv *zap.Server
}
// NewServer creates a netrunner ZAP server listening on addr.
func NewServer(addr string, be Backend) (*Server, error) {
// NewServer creates a netrunner ZAP server listening on addr behind a
// Z-Wing post-quantum handshake. cfg must carry a LocalIdentity. The
// server has no cleartext mode.
func NewServer(addr string, cfg *zwing.Config, be Backend) (*Server, error) {
if be == nil {
return nil, errors.New("zapwire: nil backend")
}
listener, err := zap.Listen(addr, nil)
if cfg == nil || cfg.LocalIdentity == nil {
return nil, errors.New("zapwire: zwing.Config with LocalIdentity required")
}
listener, err := zwing.ListenZAP(addr, cfg)
if err != nil {
return nil, fmt.Errorf("zapwire: listen %s: %w", addr, err)
}