diff --git a/cmd/pggat/pgbouncer.go b/cmd/pggat/pgbouncer.go
index 4fe2f957a4c5b74a0040857b730b2c0720245d11..52dcf980bd7379b09a877e5ff1371d542debc612 100644
--- a/cmd/pggat/pgbouncer.go
+++ b/cmd/pggat/pgbouncer.go
@@ -6,7 +6,8 @@ import (
 
 	"github.com/caddyserver/caddy/v2"
 	"github.com/caddyserver/caddy/v2/caddyconfig"
-	caddycmd "github.com/caddyserver/caddy/v2/cmd"
+
+	caddycmd "gfx.cafe/gfx/pggat/cmd"
 
 	"gfx.cafe/gfx/pggat/lib/gat"
 	"gfx.cafe/gfx/pggat/lib/gat/handlers/pgbouncer"
diff --git a/go.mod b/go.mod
index 706ee3ef0a02205ea95d65c8de93efd4799b8496..074e963d7513bedf790fdb2c1ad7bffd8ae2668c 100644
--- a/go.mod
+++ b/go.mod
@@ -17,6 +17,10 @@ require (
 require (
 	cloud.google.com/go/compute v1.20.1 // indirect
 	cloud.google.com/go/compute/metadata v0.2.3 // indirect
+	gfx.cafe/util/temple v0.0.0-20230312041217-0882540e78eb // indirect
+	github.com/Masterminds/goutils v1.1.1 // indirect
+	github.com/Masterminds/semver/v3 v3.2.0 // indirect
+	github.com/Masterminds/sprig/v3 v3.2.3 // indirect
 	github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/caddyserver/certmagic v0.19.2 // indirect
@@ -43,6 +47,8 @@ require (
 	github.com/googleapis/gax-go/v2 v2.11.0 // indirect
 	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
 	github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
+	github.com/huandu/xstrings v1.3.3 // indirect
+	github.com/iancoleman/strcase v0.2.0 // indirect
 	github.com/imdario/mergo v0.3.12 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
@@ -54,6 +60,8 @@ require (
 	github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
 	github.com/mholt/acmez v1.2.0 // indirect
 	github.com/miekg/dns v1.1.55 // indirect
+	github.com/mitchellh/copystructure v1.2.0 // indirect
+	github.com/mitchellh/reflectwalk v1.0.2 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d // indirect
@@ -67,7 +75,10 @@ require (
 	github.com/quic-go/qtls-go1-20 v0.3.1 // indirect
 	github.com/quic-go/quic-go v0.37.5 // indirect
 	github.com/russross/blackfriday/v2 v2.1.0 // indirect
+	github.com/shopspring/decimal v1.2.0 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
+	github.com/spf13/afero v1.9.3 // indirect
+	github.com/spf13/cast v1.4.1 // indirect
 	github.com/spf13/cobra v1.7.0 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/xdg-go/stringprep v1.0.4 // indirect
diff --git a/go.sum b/go.sum
index 91e67319bb9289efbb9802a3f740a5306fbb6e00..d055e4f6894991cb9266b8f653fdc34f6c1e2e62 100644
--- a/go.sum
+++ b/go.sum
@@ -3,6 +3,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
 cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
 cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
 cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
 cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
 cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
 cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
@@ -13,6 +14,9 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
 cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
 cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
 cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
+cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
+cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
+cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
 cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
 cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@@ -34,11 +38,20 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
 cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
 cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 gfx.cafe/ghalliday1/scram v0.0.2 h1:uuScaL7DUEP/lKWJnA5kKHq5RJev26q8DMbP3gKviAg=
 gfx.cafe/ghalliday1/scram v0.0.2/go.mod h1:qt6+WJoFcX2id67G5w+/dktBJ56Se0sZAa7WjqfNNu0=
+gfx.cafe/util/temple v0.0.0-20230312041217-0882540e78eb h1:E6gfSdAD5eBWHD5cLktd/umyivQlFTvEzoadoM33g3M=
+gfx.cafe/util/temple v0.0.0-20230312041217-0882540e78eb/go.mod h1:rDv4JeaN6NFCUXiNiewrmQnvm7npR7XbJ1jsturNDhc=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
+github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
+github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
+github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
+github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
+github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -66,6 +79,7 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
 github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
@@ -85,6 +99,7 @@ github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
 github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
@@ -172,6 +187,7 @@ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
@@ -179,11 +195,15 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
 github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
 github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
 github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -193,6 +213,7 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
 github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
+github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
 github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
@@ -202,8 +223,13 @@ github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZn
 github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
+github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
+github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
 github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -227,6 +253,7 @@ github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/q
 github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -246,6 +273,12 @@ github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
 github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
 github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
 github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
+github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
+github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
+github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
+github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
+github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -265,6 +298,7 @@ github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -304,11 +338,18 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
 github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
 github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
+github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
+github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
+github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
+github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
 github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -349,6 +390,7 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
 go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
@@ -363,8 +405,11 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
 golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
 golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -391,6 +436,7 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
 golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
 golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@@ -399,6 +445,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
 golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
@@ -432,7 +480,10 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
 golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -440,6 +491,7 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
 golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -447,6 +499,10 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
 golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
@@ -495,11 +551,17 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -508,11 +570,13 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
 golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
 golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
 golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -520,9 +584,11 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -571,7 +637,14 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
 golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
+golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
@@ -596,6 +669,9 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
 google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
 google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
 google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
+google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
+google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
+google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
 google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o=
 google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
@@ -636,7 +712,14 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
 google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 h1:Au6te5hbKUV8pIYWHqOUZ1pva5qK/rwbIhoXEUB9Lu8=
 google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 h1:XVeBY8d/FaK4848myy41HBqnDwvxeV3zMZhwN1TvAMU=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
@@ -653,8 +736,11 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
 google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
 google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
 google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
+google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
+google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
 google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI=
diff --git a/hack/packetgen/main.go b/hack/packetgen/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..c6d4f14eb0b353b75bc067c5acce5997a5cea701
--- /dev/null
+++ b/hack/packetgen/main.go
@@ -0,0 +1,29 @@
+package main
+
+import (
+	_ "embed"
+	"fmt"
+
+	"gfx.cafe/util/temple"
+	"gfx.cafe/util/temple/lib/prayer"
+)
+
+func main() {
+	var idx int
+	var obj any
+	temple.RegisterTemplateDir("templates")
+	temple.ReadObjectFile(&obj, "protocol.yaml")
+	temple.RegisterFunc("temp", func() string {
+		idx++
+		return fmt.Sprintf("temp%d", idx)
+	})
+	temple.Prepare(&prayer.Go{
+		Input:   "packets",
+		Obj:     obj,
+		Package: "packets",
+		Output:  "out/packets.go",
+	})
+	if err := temple.Pray(); err != nil {
+		panic(err)
+	}
+}
diff --git a/hack/packetgen/protocol.yaml b/hack/packetgen/protocol.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..85ccc00cec583acfbe6cc213fed2bb7f0560d142
--- /dev/null
+++ b/hack/packetgen/protocol.yaml
@@ -0,0 +1,382 @@
+Packets:
+  Startup:
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: Mode
+          Map:
+            Name: Mode
+            Prefix:
+              Basic: int16
+            Items:
+              Version3:
+                Type: 3
+                Struct:
+                  Name: Payload
+                  Fields:
+                    - Name: MinorVersion
+                      Basic: int16
+                    - Name: Parameters
+                      ZeroTerminatedSlice:
+                        Name: Parameter
+                        Fields:
+                          - Name: Key
+                            Basic: string
+                          - Name: Value
+                            Basic: string
+              Control:
+                Type: 1234
+                Struct:
+                  Name: Payload
+                  Fields:
+                    - Name: Mode
+                      Map:
+                        Name: Mode
+                        Prefix:
+                          Basic: int16
+                        Items:
+                          Cancel:
+                            Type: 5678
+                            Struct:
+                              Name: Key
+                              Fields:
+                                - Name: ProcessID
+                                  Basic: int32
+                                - Name: SecretKey
+                                  Basic: int32
+                          SSL:
+                            Type: 5679
+                          GSSAPI:
+                            Type: 5680
+  Authentication:
+    Type: 'R'
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: Mode
+          Map:
+            Name: Mode
+            Prefix:
+              Basic: int32
+            Items:
+              Ok:
+                Type: 0
+              KerberosV5:
+                Type: 2
+              CleartextPassword:
+                Type: 3
+              MD5Password:
+                Type: 5
+                Array:
+                  Length: 4
+                  Basic: uint8
+              GSS:
+                Type: 7
+              GSSContinue:
+                Type: 8
+                Remaining:
+                  Basic: uint8
+              SSPI:
+                Type: 9
+              SASL:
+                Type: 10
+                ZeroTerminatedSlice:
+                  Name: Method
+                  Fields:
+                    - Name: Method
+                      Basic: string
+              SASLContinue:
+                Type: 11
+                Remaining:
+                  Basic: uint8
+              SASLFinal:
+                Type: 12
+                Remaining:
+                  Basic: uint8
+  GSSResponse:
+    Type: 'p'
+    Remaining:
+      Basic: uint8
+  PasswordMessage:
+    Type: 'p'
+    Basic: string
+  SASLInitialResponse:
+    Type: 'p'
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: Mechanism
+          Basic: string
+        - Name: InitialClientResponse
+          NullableLengthPrefixedSlice:
+            Prefix:
+              Basic: int32
+            Basic: uint8
+  SASLResponse:
+    Type: 'p'
+    Remaining:
+      Basic: uint8
+  BackendKeyData:
+    Type: 'K'
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: ProcessID
+          Basic: int32
+        - Name: SecretKey
+          Basic: int32
+  Bind:
+    Type: 'B'
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: Destination
+          Basic: string
+        - Name: Source
+          Basic: string
+        - Name: FormatCodes
+          LengthPrefixedSlice:
+            Prefix:
+              Basic: uint16
+            Basic: int16
+        - Name: Parameters
+          LengthPrefixedSlice:
+            Prefix:
+              Basic: uint16
+            NullableLengthPrefixedSlice:
+              Prefix:
+                Basic: int32
+              Basic: uint8
+        - Name: ResultFormatCodes
+          LengthPrefixedSlice:
+            Prefix:
+              Basic: uint16
+            Basic: int16
+  BindComplete:
+    Type: '2'
+  Close:
+    Type: 'C'
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: Which
+          Basic: uint8
+        - Name: Name
+          Basic: string
+  CloseComplete:
+    Type: '3'
+  CommandComplete:
+    Type: 'C'
+    Basic: string
+  CopyData:
+    Type: 'd'
+    Remaining:
+      Basic: uint8
+  CopyDone:
+    Type: 'c'
+  CopyFail:
+    Type: 'f'
+    Basic: string
+  CopyInResponse:
+    Type: 'G'
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: Mode
+          Basic: int8
+        - Name: ColumnFormatCodes
+          LengthPrefixedSlice:
+            Prefix:
+              Basic: uint16
+            Basic: int16
+  CopyOutResponse:
+    Type: 'H'
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: Mode
+          Basic: int8
+        - Name: ColumnFormatCodes
+          LengthPrefixedSlice:
+            Prefix:
+              Basic: uint16
+            Basic: int16
+  CopyBothResponse:
+    Type: 'W'
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: Mode
+          Basic: int8
+        - Name: ColumnFormatCodes
+          LengthPrefixedSlice:
+            Prefix:
+              Basic: uint16
+            Basic: int16
+  DataRow:
+    Type: 'D'
+    LengthPrefixedSlice:
+      Prefix:
+        Basic: uint16
+      NullableLengthPrefixedSlice:
+        Prefix:
+          Basic: int32
+        Basic: uint8
+  Describe:
+    Type: 'D'
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: Which
+          Basic: uint8
+        - Name: Name
+          Basic: string
+  EmptyQueryResponse:
+    Type: 'I'
+  ErrorResponse:
+    Type: 'E'
+    ZeroTerminatedSlice:
+      Name: Field
+      Fields:
+        - Name: Code
+          Basic: uint8
+        - Name: Value
+          Basic: string
+  Execute:
+    Type: 'E'
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: Target
+          Basic: string
+        - Name: MaxRows
+          Basic: uint32
+  Flush:
+    Type: 'H'
+  FunctionCall:
+    Type: 'F'
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: ObjectID
+          Basic: int32
+        - Name: ArgumentFormatCodes
+          LengthPrefixedSlice:
+            Prefix:
+              Basic: uint16
+            Basic: int16
+        - Name: Arguments
+          LengthPrefixedSlice:
+            Prefix:
+              Basic: uint16
+            NullableLengthPrefixedSlice:
+              Prefix:
+                Basic: int32
+              Basic: uint8
+        - Name: ResultFormatCode
+          Basic: int16
+  FunctionCallResponse:
+    Type: 'V'
+    NullableLengthPrefixedSlice:
+      Prefix:
+        Basic: int32
+      Basic: uint8
+  NegotiateProtocolVersion:
+    Type: 'v'
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: MinorProtocolVersion
+          Basic: int32
+        - Name: UnrecognizedProtocolOptions
+          LengthPrefixedSlice:
+            Prefix:
+              Basic: uint32
+            Basic: string
+  NoData:
+    Type: 'n'
+  NoticeResponse:
+    Type: 'N'
+    ZeroTerminatedSlice:
+      Name: Field
+      Fields:
+        - Name: Code
+          Basic: uint8
+        - Name: Value
+          Basic: string
+  NotificationResponse:
+    Type: 'A'
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: ProcessID
+          Basic: int32
+        - Name: Channel
+          Basic: string
+        - Name: Payload
+          Basic: string
+  ParameterDescription:
+    Type: 't'
+    LengthPrefixedSlice:
+      Prefix:
+        Basic: uint16
+      Basic: int32
+  ParameterStatus:
+    Type: 'S'
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: Key
+          Basic: string
+        - Name: Value
+          Basic: string
+  Parse:
+    Type: 'P'
+    Struct:
+      Name: Payload
+      Fields:
+        - Name: Destination
+          Basic: string
+        - Name: Query
+          Basic: string
+        - Name: ParameterDataTypes
+          LengthPrefixedSlice:
+            Prefix:
+              Basic: uint16
+            Basic: int32
+  ParseComplete:
+    Type: '1'
+  PortalSuspended:
+    Type: 's'
+  Query:
+    Type: 'Q'
+    Basic: string
+  ReadyForQuery:
+    Type: 'Z'
+    Basic: uint8
+  RowDescription:
+    Type: 'T'
+    LengthPrefixedSlice:
+      Prefix:
+        Basic: uint16
+      Struct:
+        Name: Row
+        Fields:
+          - Name: Name
+            Basic: string
+          - Name: TableID
+            Basic: int32
+          - Name: ColumnAttributeNumber
+            Basic: int16
+          - Name: FieldDataType
+            Basic: int32
+          - Name: DataTypeSize
+            Basic: int16
+          - Name: TypeModifier
+            Basic: int32
+          - Name: FormatCode
+            Basic: int16
+  Sync:
+    Type: 'S'
+  Terminate:
+    Type: 'X'
diff --git a/hack/packetgen/templates/decode.tmpl b/hack/packetgen/templates/decode.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..cdfd7d424ba769ed18a39595cf2e968adad21917
--- /dev/null
+++ b/hack/packetgen/templates/decode.tmpl
@@ -0,0 +1,126 @@
+{{$name := index . 0 -}}
+{{$pointer := index . 1 -}}
+{{$value := index . 2 -}}
+
+{{if some $value.Map -}}
+	{{$prefixPointer := temp -}}
+
+	var {{$prefixPointer}} {{template "type" (list $name $value.Map.Prefix)}}
+
+	{{template "decode" (list $name $prefixPointer $value.Map.Prefix)}}
+
+	switch {{$prefixPointer}} {
+	{{range $n, $item := $value.Map.Items -}}
+        {{$itemName := printf "%s%s" $name $n -}}
+
+		case {{$item.Type}}:
+			{{$pointer}} = new({{$itemName}})
+	{{end -}}
+		default:
+			err = ErrInvalidFormat
+			return
+	}
+
+	err = {{$pointer}}.ReadFrom(decoder)
+	if err != nil {
+		return
+	}
+{{else if some $value.Remaining -}}
+	{{$pointer}} = {{$pointer}}[:0]
+
+	for {
+		if decoder.Position() >= decoder.Length() {
+			break
+		}
+
+		{{$pointer}} = slices.Resize({{$pointer}}, len({{$pointer}})+1)
+
+		{{$targetPointer := printf "%s[len(%s)-1]" $pointer $pointer -}}
+
+		{{template "decode" (list $name $targetPointer $value.Remaining)}}
+	}
+{{else if some $value.Basic -}}
+	*(*{{$value.Basic}})(&({{$pointer}})), err = decoder.{{upperCamel $value.Basic}}()
+	if err != nil {
+		return
+	}
+{{else if some $value.Array -}}
+	{{$indexPointer := temp -}}
+
+	for {{$indexPointer}} := 0; {{$indexPointer}} < {{$value.Array.Length}}; {{$indexPointer}}++ {
+		{{$targetPointer := printf "%s[%s]" $pointer $indexPointer -}}
+
+		{{template "decode" (list $name $targetPointer $value.Array)}}
+	}
+{{else if some $value.Struct -}}
+	{{$structName := printf "%s%s" $name $value.Struct.Name -}}
+
+	{{range $field := $value.Struct.Fields -}}
+        {{$fieldPointer := printf "%s.%s" $pointer $field.Name -}}
+
+		{{template "decode" (list $structName $fieldPointer $field) -}}
+	{{end -}}
+{{else if some $value.LengthPrefixedSlice -}}
+	{{$lengthPointer := temp -}}
+
+	var {{$lengthPointer}} {{template "type" (list $name $value.LengthPrefixedSlice.Prefix)}}
+	{{template "decode" (list $name $lengthPointer $value.LengthPrefixedSlice.Prefix)}}
+
+	{{$pointer}} = slices.Resize({{$pointer}}, int({{$lengthPointer}}))
+
+	{{$indexPointer := temp -}}
+
+	for {{$indexPointer}} := 0; {{$indexPointer}} < int({{$lengthPointer}}); {{$indexPointer}}++ {
+		{{$targetPointer := printf "%s[%s]" $pointer $indexPointer -}}
+
+		{{template "decode" (list $name $targetPointer $value.LengthPrefixedSlice)}}
+	}
+
+{{else if some $value.NullableLengthPrefixedSlice -}}
+    {{$lengthPointer := temp -}}
+
+	var {{$lengthPointer}} {{template "type" (list $name $value.NullableLengthPrefixedSlice.Prefix)}}
+    {{template "decode" (list $name $lengthPointer $value.NullableLengthPrefixedSlice.Prefix)}}
+
+	if {{$lengthPointer}} == -1 {
+		{{$pointer}} = nil
+	} else {
+		if {{$pointer}} == nil {
+			{{$pointer}} = make([]{{template "type" (list $name $value.NullableLengthPrefixedSlice)}}, int({{$lengthPointer}}))
+		} else {
+			{{$pointer}} = slices.Resize({{$pointer}}, int({{$lengthPointer}}))
+		}
+
+		{{$indexPointer := temp -}}
+
+		for {{$indexPointer}} := 0; {{$indexPointer}} < int({{$lengthPointer}}); {{$indexPointer}}++ {
+			{{$targetPointer := printf "%s[%s]" $pointer $indexPointer -}}
+
+			{{template "decode" (list $name $targetPointer $value.NullableLengthPrefixedSlice)}}
+		}
+	}
+
+{{else if some $value.ZeroTerminatedSlice -}}
+    {{$structName := printf "%s%s" $name $value.ZeroTerminatedSlice.Name -}}
+
+	{{$pointer}} = {{$pointer}}[:0]
+
+	for {
+		{{$pointer}} = slices.Resize({{$pointer}}, len({{$pointer}})+1)
+
+		{{$targetPointer := printf "%s[len(%s)-1]" $pointer $pointer -}}
+
+		{{range $i, $field := $value.ZeroTerminatedSlice.Fields -}}
+			{{$fieldPointer := printf "%s.%s" $targetPointer $field.Name -}}
+
+			{{template "decode" (list $structName $fieldPointer $field) -}}
+
+			{{if eq $i 0 -}}
+				if {{$fieldPointer}} == *new({{template "type" (list $structName $field)}}) {
+                	{{$pointer}} = {{$pointer}}[:len({{$pointer}})-1]
+					break
+				}
+			{{end -}}
+		{{end -}}
+	}
+{{end -}}
diff --git a/hack/packetgen/templates/encode.tmpl b/hack/packetgen/templates/encode.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..9d6532f2583e5cf2a0267a9561156b2d4265bffc
--- /dev/null
+++ b/hack/packetgen/templates/encode.tmpl
@@ -0,0 +1,87 @@
+{{$name := index . 0 -}}
+{{$pointer := index . 1 -}}
+{{$value := index . 2 -}}
+
+{{if some $value.Map -}}
+    {{$ifaceName := printf "%s%s" $name $value.Map.Name -}}
+
+    {{$prefixPointer := printf "%s.%s()" $pointer $ifaceName -}}
+
+	{{template "encode" (list $name $prefixPointer $value.Map.Prefix)}}
+
+	err = {{$pointer}}.WriteTo(encoder)
+	if err != nil {
+		return
+	}
+{{else if some $value.Remaining -}}
+	{{$itemPointer := temp -}}
+
+	for _, {{$itemPointer}} := range {{$pointer}} {
+		{{template "encode" (list $name $itemPointer $value.Remaining)}}
+	}
+{{else if some $value.Basic -}}
+	err = encoder.{{upperCamel $value.Basic}}({{$value.Basic}}({{$pointer}}))
+	if err != nil {
+		return
+	}
+{{else if some $value.Array -}}
+	{{$itemPointer := temp -}}
+
+	for _, {{$itemPointer}} := range {{$pointer}} {
+		{{template "encode" (list $name $itemPointer $value.Array)}}
+	}
+{{else if some $value.Struct -}}
+    {{$structName := printf "%s%s" $name $value.Struct.Name -}}
+
+    {{range $field := $value.Struct.Fields -}}
+		{{$fieldPointer := printf "%s.%s" $pointer $field.Name -}}
+
+		{{template "encode" (list $structName $fieldPointer $field)}}
+	{{end -}}
+{{else if some $value.LengthPrefixedSlice -}}
+	{{$lengthPointer := temp -}}
+
+	{{$lengthPointer}} := {{template "type" (list $name $value.LengthPrefixedSlice.Prefix)}}(len({{$pointer}}))
+
+	{{template "encode" (list $name $lengthPointer $value.LengthPrefixedSlice.Prefix)}}
+
+	{{$itemPointer := temp -}}
+
+	for _, {{$itemPointer}} := range {{$pointer}} {
+		{{template "encode" (list $name $itemPointer $value.LengthPrefixedSlice)}}
+	}
+{{else if some $value.NullableLengthPrefixedSlice -}}
+    {{$lengthPointer := temp -}}
+
+    {{$lengthPointer}} := {{template "type" (list $name $value.NullableLengthPrefixedSlice.Prefix)}}(len({{$pointer}}))
+
+	if {{$pointer}} == nil {
+		{{$lengthPointer}} = -1
+	}
+
+    {{template "encode" (list $name $lengthPointer $value.NullableLengthPrefixedSlice.Prefix)}}
+
+    {{$itemPointer := temp -}}
+
+	for _, {{$itemPointer}} := range {{$pointer}} {
+    	{{template "encode" (list $name $itemPointer $value.NullableLengthPrefixedSlice)}}
+	}
+{{else if some $value.ZeroTerminatedSlice -}}
+    {{$structName := printf "%s%s" $name $value.ZeroTerminatedSlice.Name -}}
+
+	{{$itemPointer := temp -}}
+
+	for _, {{$itemPointer}} := range {{$pointer}} {
+		{{range $field := $value.ZeroTerminatedSlice.Fields -}}
+			{{$fieldPointer := printf "%s.%s" $itemPointer $field.Name -}}
+
+			{{template "encode" (list $structName $fieldPointer $field)}}
+		{{end -}}
+	}
+
+	{{$donePointer := temp -}}
+
+	var {{$donePointer}} {{template "type" (list $structName (index $value.ZeroTerminatedSlice.Fields 0))}}
+
+	{{template "encode" (list $structName $donePointer (index $value.ZeroTerminatedSlice.Fields 0))}}
+{{end -}}
diff --git a/hack/packetgen/templates/length.tmpl b/hack/packetgen/templates/length.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..49bdf45401d17f50f4d731cd102a1f546c9151f0
--- /dev/null
+++ b/hack/packetgen/templates/length.tmpl
@@ -0,0 +1,112 @@
+{{$name := index . 0 -}}
+{{$pointer := index . 1 -}}
+{{$value := index . 2 -}}
+
+{{if some $value.Map -}}
+    {{$ifaceName := printf "%s%s" $name $value.Map.Name -}}
+
+    {{$prefixPointer := printf "%s.%s()" $pointer $ifaceName -}}
+
+    {{template "length" (list $name $prefixPointer $value.Map.Prefix)}}
+
+	length += {{$pointer}}.Length()
+{{else if some $value.Remaining -}}
+    {{$itemPointer := temp -}}
+
+    for _, {{$itemPointer}} := range {{$pointer}} {
+        _ = {{$itemPointer}}
+
+        {{template "length" (list $name $itemPointer $value.Remaining)}}
+    }
+{{else if some $value.Basic -}}
+    {{if eq $value.Basic "uint8" -}}
+        length += 1
+    {{else if eq $value.Basic "uint16" -}}
+        length += 2
+    {{else if eq $value.Basic "uint32" -}}
+        length += 4
+    {{else if eq $value.Basic "uint64" -}}
+        length += 8
+    {{else if eq $value.Basic "int8" -}}
+        length += 1
+    {{else if eq $value.Basic "int16" -}}
+        length += 2
+    {{else if eq $value.Basic "int32" -}}
+        length += 4
+    {{else if eq $value.Basic "int64" -}}
+        length += 8
+    {{else if eq $value.Basic "float32" -}}
+        length += 4
+    {{else if eq $value.Basic "float64" -}}
+        length += 8
+    {{else if eq $value.Basic "string" -}}
+        length += len({{$pointer}}) + 1
+    {{end -}}
+{{else if some $value.Array -}}
+    {{$itemPointer := temp -}}
+
+    for _, {{$itemPointer}} := range {{$pointer}} {
+        _ = {{$itemPointer}}
+
+        {{template "length" (list $name $itemPointer $value.Array)}}
+    }
+{{else if some $value.Struct -}}
+    {{$structName := printf "%s%s" $name $value.Struct.Name -}}
+
+    {{range $field := $value.Struct.Fields -}}
+        {{$fieldPointer := printf "%s.%s" $pointer $field.Name -}}
+
+        {{template "length" (list $structName $fieldPointer $field)}}
+    {{end -}}
+{{else if some $value.LengthPrefixedSlice -}}
+    {{$lengthPointer := temp -}}
+
+    {{$lengthPointer}} := {{template "type" (list $name $value.LengthPrefixedSlice.Prefix)}}(len({{$pointer}}))
+    _ = {{$lengthPointer}}
+
+    {{template "length" (list $name $lengthPointer $value.LengthPrefixedSlice.Prefix)}}
+
+    {{$itemPointer := temp -}}
+
+    for _, {{$itemPointer}} := range {{$pointer}} {
+        _ = {{$itemPointer}}
+
+        {{template "length" (list $name $itemPointer $value.LengthPrefixedSlice)}}
+    }
+{{else if some $value.NullableLengthPrefixedSlice -}}
+    {{$lengthPointer := temp -}}
+
+    {{$lengthPointer}} := {{template "type" (list $name $value.NullableLengthPrefixedSlice.Prefix)}}(len({{$pointer}}))
+    _ = {{$lengthPointer}}
+
+    {{template "length" (list $name $lengthPointer $value.NullableLengthPrefixedSlice.Prefix)}}
+
+    {{$itemPointer := temp -}}
+
+    for _, {{$itemPointer}} := range {{$pointer}} {
+        _ = {{$itemPointer}}
+
+        {{template "length" (list $name $itemPointer $value.NullableLengthPrefixedSlice)}}
+    }
+{{else if some $value.ZeroTerminatedSlice -}}
+    {{$structName := printf "%s%s" $name $value.ZeroTerminatedSlice.Name -}}
+
+    {{$itemPointer := temp -}}
+
+    for _, {{$itemPointer}} := range {{$pointer}} {
+        _ = {{$itemPointer}}
+
+        {{range $field := $value.ZeroTerminatedSlice.Fields -}}
+            {{$fieldPointer := printf "%s.%s" $itemPointer $field.Name -}}
+
+            {{template "length" (list $structName $fieldPointer $field)}}
+        {{end -}}
+    }
+
+    {{$donePointer := temp -}}
+
+    var {{$donePointer}} {{template "type" (list $structName (index $value.ZeroTerminatedSlice.Fields 0))}}
+    _ = {{$donePointer}}
+
+    {{template "length" (list $name $donePointer (index $value.ZeroTerminatedSlice.Fields 0))}}
+{{end -}}
diff --git a/hack/packetgen/templates/packets.tmpl b/hack/packetgen/templates/packets.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..3b095d6fdd37cd543c6f5223f23462b43c8a9d88
--- /dev/null
+++ b/hack/packetgen/templates/packets.tmpl
@@ -0,0 +1,60 @@
+// automatically generated. do not edit
+
+import (
+	"gfx.cafe/gfx/pggat/lib/fed"
+	"gfx.cafe/gfx/pggat/lib/util/slices"
+
+	"errors"
+)
+
+var (
+	ErrUnexpectedPacket = errors.New("unexpected packet")
+	ErrInvalidFormat = errors.New("invalid packet format")
+)
+
+const (
+	{{range $name, $packet := .Packets -}}
+		{{if some $packet.Type -}}
+			Type{{$name}} = '{{$packet.Type}}'
+		{{end -}}
+	{{end -}}
+)
+
+{{range $name, $packet := .Packets -}}
+	{{template "preType" (list $name $packet)}}
+
+	type {{$name}} {{template "type" (list $name $packet)}}
+
+	func (T *{{$name}}) Type() fed.Type {
+		{{if some $packet.Type -}}
+			return Type{{$name}}
+		{{else -}}
+			return 0
+		{{end -}}
+	}
+
+	func (T *{{$name}}) Length() (length int) {
+		{{template "length" (list $name "(*T)" $packet)}}
+
+		return
+	}
+
+	func (T *{{$name}}) ReadFrom(decoder *fed.Decoder) (err error) {
+		if decoder.Type() != T.Type() {
+			return ErrUnexpectedPacket
+		}
+
+		{{template "decode" (list $name "(*T)" $packet)}}
+
+		return
+	}
+
+	func (T *{{$name}}) WriteTo(encoder *fed.Encoder) (err error) {
+		{{template "encode" (list $name "(*T)" $packet)}}
+
+		return
+	}
+
+	var _ fed.Packet = (*{{$name}})(nil)
+
+{{end}}
diff --git a/hack/packetgen/templates/preType.tmpl b/hack/packetgen/templates/preType.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..5bd8ba06118c6d4c0079f1621b593b849e859464
--- /dev/null
+++ b/hack/packetgen/templates/preType.tmpl
@@ -0,0 +1,75 @@
+{{$name := index . 0 -}}
+{{$value := index . 1 -}}
+
+{{if some $value.Map -}}
+	{{$ifaceName := printf "%s%s" $name $value.Map.Name -}}
+
+	{{range $n, $item := $value.Map.Items -}}
+		{{$itemName := printf "%s%s" $name $n -}}
+
+		{{template "preType" (list $itemName $item) -}}
+
+		type {{$itemName}} {{template "type" (list $itemName $item)}}
+
+		func (*{{$itemName}}) {{$ifaceName}}() {{template "type" (list $name $value.Map.Prefix)}} {
+			return {{$item.Type}}
+		}
+
+		func (T *{{$itemName}}) Length() (length int) {
+			{{template "length" (list $name "(*T)" $item)}}
+
+			return
+		}
+
+		func (T *{{$itemName}}) ReadFrom(decoder *fed.Decoder) (err error) {
+			{{template "decode" (list $itemName "(*T)" $item)}}
+
+			return
+		}
+
+		func (T *{{$itemName}}) WriteTo(encoder *fed.Encoder) (err error) {
+			{{template "encode" (list $itemName "(*T)" $item)}}
+
+			return
+		}
+
+	{{end -}}
+
+	type {{$ifaceName}} interface{
+		{{$ifaceName}}() {{template "type" (list $name $value.Map.Prefix)}}
+
+		Length() int
+		ReadFrom(decoder *fed.Decoder) error
+		WriteTo(encoder *fed.Encoder) error
+	}
+{{else if some $value.Struct -}}
+	{{$structName := printf "%s%s" $name $value.Struct.Name -}}
+
+	{{range $field := $value.Struct.Fields -}}
+		{{template "preType" (list $structName $field) -}}
+	{{end -}}
+
+	type {{$structName}} struct{
+		{{range $field := $value.Struct.Fields -}}
+			{{$field.Name}} {{template "type" (list $structName $field)}}
+		{{end -}}
+	}
+
+{{else if some $value.LengthPrefixedSlice -}}
+	{{template "preType" (list $name $value.LengthPrefixedSlice) -}}
+{{else if some $value.NullableLengthPrefixedSlice -}}
+    {{template "preType" (list $name $value.NullableLengthPrefixedSlice) -}}
+{{else if some $value.ZeroTerminatedSlice -}}
+	{{$structName := printf "%s%s" $name $value.ZeroTerminatedSlice.Name -}}
+
+	{{range $field := $value.ZeroTerminatedSlice.Fields -}}
+		{{template "preType" (list $structName $field) -}}
+	{{end -}}
+
+	type {{$structName}} struct{
+		{{range $field := $value.ZeroTerminatedSlice.Fields -}}
+			{{$field.Name}} {{template "type" (list $structName $field)}}
+		{{end -}}
+	}
+
+{{end -}}
\ No newline at end of file
diff --git a/hack/packetgen/templates/type.tmpl b/hack/packetgen/templates/type.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..22f154a085e5cbf67941e419f72fc495ecea1d5d
--- /dev/null
+++ b/hack/packetgen/templates/type.tmpl
@@ -0,0 +1,22 @@
+{{- $name := index . 0 -}}
+{{- $value := index . 1 -}}
+
+{{- if some $value.Map -}}
+	{{printf "%s%s" $name $value.Map.Name}}
+{{- else if some $value.Remaining -}}
+	[]{{template "type" (list $name $value.Remaining)}}
+{{- else if some $value.Basic -}}
+    {{$value.Basic}}
+{{- else if some $value.Array -}}
+	[{{$value.Array.Length}}]{{template "type" (list $name $value.Array)}}
+{{- else if some $value.Struct -}}
+	{{printf "%s%s" $name $value.Struct.Name}}
+{{- else if some $value.LengthPrefixedSlice -}}
+	[]{{template "type" (list $name $value.LengthPrefixedSlice)}}
+{{- else if some $value.NullableLengthPrefixedSlice -}}
+	[]{{template "type" (list $name $value.NullableLengthPrefixedSlice)}}
+{{- else if some $value.ZeroTerminatedSlice -}}
+	[]{{printf "%s%s" $name $value.ZeroTerminatedSlice.Name}}
+{{- else -}}
+	struct{}
+{{- end -}}
\ No newline at end of file
diff --git a/lib/bouncer/backends/v0/accept.go b/lib/bouncer/backends/v0/accept.go
index d094f2f19f5f39bf5b38ca68f71a7a589ba7c5c9..6fe9254b8cc8184cc7889f48cf61c2a2757884e8 100644
--- a/lib/bouncer/backends/v0/accept.go
+++ b/lib/bouncer/backends/v0/accept.go
@@ -9,39 +9,43 @@ import (
 	"gfx.cafe/gfx/pggat/lib/bouncer"
 	"gfx.cafe/gfx/pggat/lib/fed"
 	packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
+	"gfx.cafe/gfx/pggat/lib/perror"
 	"gfx.cafe/gfx/pggat/lib/util/strutil"
 )
 
 func authenticationSASLChallenge(ctx *acceptContext, encoder auth.SASLEncoder) (done bool, err error) {
-	ctx.Packet, err = ctx.Conn.ReadPacket(true, ctx.Packet)
+	var packet fed.Packet
+	packet, err = ctx.Conn.ReadPacket(true)
 	if err != nil {
 		return
 	}
 
-	if ctx.Packet.Type() != packets.TypeAuthentication {
+	if packet.Type() != packets.TypeAuthentication {
 		err = ErrUnexpectedPacket
 		return
 	}
 
-	var method int32
-	p := ctx.Packet.ReadInt32(&method)
+	var p packets.Authentication
+	err = fed.ToConcrete(&p, packet)
+	if err != nil {
+		return
+	}
 
-	switch method {
-	case 11:
+	switch mode := p.Mode.(type) {
+	case *packets.AuthenticationPayloadSASLContinue:
 		// challenge
 		var response []byte
-		response, err = encoder.Write(p)
+		response, err = encoder.Write(*mode)
 		if err != nil {
 			return
 		}
 
-		resp := packets.AuthenticationResponse(response)
-		ctx.Packet = resp.IntoPacket(ctx.Packet)
-		err = ctx.Conn.WritePacket(ctx.Packet)
+		resp := packets.SASLResponse(response)
+		err = ctx.Conn.WritePacket(&resp)
 		return
-	case 12:
+	case *packets.AuthenticationPayloadSASLFinal:
 		// finish
-		_, err = encoder.Write(p)
+		_, err = encoder.Write(*mode)
 		if err != io.EOF {
 			if err == nil {
 				err = errors.New("expected EOF")
@@ -67,11 +71,10 @@ func authenticationSASL(ctx *acceptContext, mechanisms []string, creds auth.SASL
 	}
 
 	saslInitialResponse := packets.SASLInitialResponse{
-		Mechanism:       mechanism,
-		InitialResponse: initialResponse,
+		Mechanism:             mechanism,
+		InitialClientResponse: initialResponse,
 	}
-	ctx.Packet = saslInitialResponse.IntoPacket(ctx.Packet)
-	err = ctx.Conn.WritePacket(ctx.Packet)
+	err = ctx.Conn.WritePacket(&saslInitialResponse)
 	if err != nil {
 		return err
 	}
@@ -92,11 +95,8 @@ func authenticationSASL(ctx *acceptContext, mechanisms []string, creds auth.SASL
 }
 
 func authenticationMD5(ctx *acceptContext, salt [4]byte, creds auth.MD5Client) error {
-	pw := packets.PasswordMessage{
-		Password: creds.EncodeMD5(salt),
-	}
-	ctx.Packet = pw.IntoPacket(ctx.Packet)
-	err := ctx.Conn.WritePacket(ctx.Packet)
+	pw := packets.PasswordMessage(creds.EncodeMD5(salt))
+	err := ctx.Conn.WritePacket(&pw)
 	if err != nil {
 		return err
 	}
@@ -104,69 +104,54 @@ func authenticationMD5(ctx *acceptContext, salt [4]byte, creds auth.MD5Client) e
 }
 
 func authenticationCleartext(ctx *acceptContext, creds auth.CleartextClient) error {
-	pw := packets.PasswordMessage{
-		Password: creds.EncodeCleartext(),
-	}
-	ctx.Packet = pw.IntoPacket(ctx.Packet)
-	err := ctx.Conn.WritePacket(ctx.Packet)
+	pw := packets.PasswordMessage(creds.EncodeCleartext())
+	err := ctx.Conn.WritePacket(&pw)
 	if err != nil {
 		return err
 	}
 	return nil
 }
 
-func authentication(ctx *acceptContext) (done bool, err error) {
-	var method int32
-	ctx.Packet.ReadInt32(&method)
+func authentication(ctx *acceptContext, p *packets.Authentication) (done bool, err error) {
 	// they have more authentication methods than there are pokemon
-	switch method {
-	case 0:
+	switch mode := p.Mode.(type) {
+	case *packets.AuthenticationPayloadOk:
 		// we're good to go, that was easy
 		ctx.Conn.Authenticated = true
 		return true, nil
-	case 2:
+	case *packets.AuthenticationPayloadKerberosV5:
 		err = errors.New("kerberos v5 is not supported")
 		return
-	case 3:
+	case *packets.AuthenticationPayloadCleartextPassword:
 		c, ok := ctx.Options.Credentials.(auth.CleartextClient)
 		if !ok {
 			return false, auth.ErrMethodNotSupported
 		}
 		return false, authenticationCleartext(ctx, c)
-	case 5:
-		var md5 packets.AuthenticationMD5
-		if !md5.ReadFromPacket(ctx.Packet) {
-			err = ErrBadFormat
-			return
-		}
-
+	case *packets.AuthenticationPayloadMD5Password:
 		c, ok := ctx.Options.Credentials.(auth.MD5Client)
 		if !ok {
 			return false, auth.ErrMethodNotSupported
 		}
-		return false, authenticationMD5(ctx, md5.Salt, c)
-	case 6:
-		err = errors.New("scm credential is not supported")
-		return
-	case 7:
+		return false, authenticationMD5(ctx, *mode, c)
+	case *packets.AuthenticationPayloadGSS:
 		err = errors.New("gss is not supported")
 		return
-	case 9:
+	case *packets.AuthenticationPayloadSSPI:
 		err = errors.New("sspi is not supported")
 		return
-	case 10:
-		// read list of mechanisms
-		var sasl packets.AuthenticationSASL
-		if !sasl.ReadFromPacket(ctx.Packet) {
-			err = ErrBadFormat
-			return
-		}
-
+	case *packets.AuthenticationPayloadSASL:
 		c, ok := ctx.Options.Credentials.(auth.SASLClient)
 		if !ok {
 			return false, auth.ErrMethodNotSupported
 		}
-		return false, authenticationSASL(ctx, sasl.Mechanisms, c)
+
+		var mechanisms = make([]string, 0, len(*mode))
+		for _, m := range *mode {
+			mechanisms = append(mechanisms, m.Method)
+		}
+
+		return false, authenticationSASL(ctx, mechanisms, c)
 	default:
 		err = errors.New("unknown authentication method")
 		return
@@ -174,22 +159,28 @@ func authentication(ctx *acceptContext) (done bool, err error) {
 }
 
 func startup0(ctx *acceptContext) (done bool, err error) {
-	ctx.Packet, err = ctx.Conn.ReadPacket(true, ctx.Packet)
+	var packet fed.Packet
+	packet, err = ctx.Conn.ReadPacket(true)
 	if err != nil {
 		return
 	}
 
-	switch ctx.Packet.Type() {
+	switch packet.Type() {
 	case packets.TypeErrorResponse:
-		var err2 packets.ErrorResponse
-		if !err2.ReadFromPacket(ctx.Packet) {
-			err = ErrBadFormat
-		} else {
-			err = errors.New(err2.Error.String())
+		var p packets.ErrorResponse
+		err = fed.ToConcrete(&p, packet)
+		if err != nil {
+			return
 		}
+		err = perror.FromPacket(&p)
 		return
 	case packets.TypeAuthentication:
-		return authentication(ctx)
+		var p packets.Authentication
+		err = fed.ToConcrete(&p, packet)
+		if err != nil {
+			return
+		}
+		return authentication(ctx, &p)
 	case packets.TypeNegotiateProtocolVersion:
 		// we only support protocol 3.0 for now
 		err = errors.New("server wanted to negotiate protocol version")
@@ -201,36 +192,44 @@ func startup0(ctx *acceptContext) (done bool, err error) {
 }
 
 func startup1(ctx *acceptContext) (done bool, err error) {
-	ctx.Packet, err = ctx.Conn.ReadPacket(true, ctx.Packet)
+	var packet fed.Packet
+	packet, err = ctx.Conn.ReadPacket(true)
 	if err != nil {
 		return
 	}
 
-	switch ctx.Packet.Type() {
+	switch packet.Type() {
 	case packets.TypeBackendKeyData:
-		ctx.Packet.ReadBytes(ctx.Conn.BackendKey[:])
+		var p packets.BackendKeyData
+		err = fed.ToConcrete(&p, packet)
+		if err != nil {
+			return
+		}
+		ctx.Conn.BackendKey.SecretKey = p.SecretKey
+		ctx.Conn.BackendKey.ProcessID = p.ProcessID
+
 		return false, nil
 	case packets.TypeParameterStatus:
-		var ps packets.ParameterStatus
-		if !ps.ReadFromPacket(ctx.Packet) {
-			err = ErrBadFormat
+		var p packets.ParameterStatus
+		err = fed.ToConcrete(&p, packet)
+		if err != nil {
 			return
 		}
-		ikey := strutil.MakeCIString(ps.Key)
+		ikey := strutil.MakeCIString(p.Key)
 		if ctx.Conn.InitialParameters == nil {
 			ctx.Conn.InitialParameters = make(map[strutil.CIString]string)
 		}
-		ctx.Conn.InitialParameters[ikey] = ps.Value
+		ctx.Conn.InitialParameters[ikey] = p.Value
 		return false, nil
 	case packets.TypeReadyForQuery:
 		return true, nil
 	case packets.TypeErrorResponse:
-		var err2 packets.ErrorResponse
-		if !err2.ReadFromPacket(ctx.Packet) {
-			err = ErrBadFormat
-		} else {
-			err = errors.New(err2.Error.String())
+		var p packets.ErrorResponse
+		err = fed.ToConcrete(&p, packet)
+		if err != nil {
+			return
 		}
+		err = perror.FromPacket(&p)
 		return
 	case packets.TypeNoticeResponse:
 		// TODO(garet) do something with notice
@@ -242,20 +241,18 @@ func startup1(ctx *acceptContext) (done bool, err error) {
 }
 
 func enableSSL(ctx *acceptContext) (bool, error) {
-	ctx.Packet = ctx.Packet.Reset(0, 4)
-	ctx.Packet = ctx.Packet.AppendUint16(1234)
-	ctx.Packet = ctx.Packet.AppendUint16(5679)
-	if err := ctx.Conn.WritePacket(ctx.Packet); err != nil {
-		return false, err
+	p := packets.Startup{
+		Mode: &packets.StartupPayloadControl{
+			Mode: &packets.StartupPayloadControlPayloadSSL{},
+		},
 	}
 
-	byteReader, ok := ctx.Conn.ReadWriteCloser.(io.ByteReader)
-	if !ok {
-		return false, errors.New("server must be io.ByteReader to enable ssl")
+	if err := ctx.Conn.WritePacket(&p); err != nil {
+		return false, err
 	}
 
 	// read byte to see if ssl is allowed
-	yn, err := byteReader.ReadByte()
+	yn, err := ctx.Conn.ReadByte()
 	if err != nil {
 		return false, err
 	}
@@ -265,12 +262,7 @@ func enableSSL(ctx *acceptContext) (bool, error) {
 		return false, nil
 	}
 
-	sslClient, ok := ctx.Conn.ReadWriteCloser.(fed.SSLClient)
-	if !ok {
-		return false, errors.New("server must be fed.SSLClient to enable ssl")
-	}
-
-	if err = sslClient.EnableSSLClient(ctx.Options.SSLConfig); err != nil {
+	if err = ctx.Conn.EnableSSL(ctx.Options.SSLConfig, true); err != nil {
 		return false, err
 	}
 
@@ -294,26 +286,32 @@ func accept(ctx *acceptContext) error {
 		}
 	}
 
-	size := 4 + len("user") + 1 + len(username) + 1 + len("database") + 1 + len(ctx.Options.Database) + 1
-	for key, value := range ctx.Options.StartupParameters {
-		size += len(key.String()) + len(value) + 2
+	m := packets.StartupPayloadVersion3{
+		MinorVersion: 0,
+		Parameters: []packets.StartupPayloadVersion3PayloadParameter{
+			{
+				Key:   "user",
+				Value: username,
+			},
+			{
+				Key:   "database",
+				Value: ctx.Options.Database,
+			},
+		},
 	}
-	size += 1
-
-	ctx.Packet = ctx.Packet.Reset(0, size)
-	ctx.Packet = ctx.Packet.AppendUint16(3)
-	ctx.Packet = ctx.Packet.AppendUint16(0)
-	ctx.Packet = ctx.Packet.AppendString("user")
-	ctx.Packet = ctx.Packet.AppendString(username)
-	ctx.Packet = ctx.Packet.AppendString("database")
-	ctx.Packet = ctx.Packet.AppendString(ctx.Options.Database)
+
 	for key, value := range ctx.Options.StartupParameters {
-		ctx.Packet = ctx.Packet.AppendString(key.String())
-		ctx.Packet = ctx.Packet.AppendString(value)
+		m.Parameters = append(m.Parameters, packets.StartupPayloadVersion3PayloadParameter{
+			Key:   key.String(),
+			Value: value,
+		})
+	}
+
+	p := packets.Startup{
+		Mode: &m,
 	}
-	ctx.Packet = ctx.Packet.AppendUint8(0)
 
-	err := ctx.Conn.WritePacket(ctx.Packet)
+	err := ctx.Conn.WritePacket(&p)
 	if err != nil {
 		return err
 	}
diff --git a/lib/bouncer/backends/v0/cancel.go b/lib/bouncer/backends/v0/cancel.go
index 23769b8dbed161c791b0e64ee450820a24d8bb9d..48ee2b66461df144c76de97208c8b00bd642d2bf 100644
--- a/lib/bouncer/backends/v0/cancel.go
+++ b/lib/bouncer/backends/v0/cancel.go
@@ -1,11 +1,18 @@
 package backends
 
-import "gfx.cafe/gfx/pggat/lib/fed"
+import (
+	"gfx.cafe/gfx/pggat/lib/fed"
+	packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
+)
 
-func Cancel(server *fed.Conn, key [8]byte) error {
-	packet := fed.NewPacket(0, 12)
-	packet = packet.AppendUint16(1234)
-	packet = packet.AppendUint16(5678)
-	packet = packet.AppendBytes(key[:])
-	return server.WritePacket(packet)
+func Cancel(server *fed.Conn, key fed.BackendKey) error {
+	p := packets.Startup{
+		Mode: &packets.StartupPayloadControl{
+			Mode: &packets.StartupPayloadControlPayloadCancel{
+				ProcessID: key.ProcessID,
+				SecretKey: key.SecretKey,
+			},
+		},
+	}
+	return server.WritePacket(&p)
 }
diff --git a/lib/bouncer/backends/v0/context.go b/lib/bouncer/backends/v0/context.go
index 01945b65e47630a4bdfa299abf446d1bbdc7f05c..3554fc2bbbb9be1721fd4a233611dc9de63f0807 100644
--- a/lib/bouncer/backends/v0/context.go
+++ b/lib/bouncer/backends/v0/context.go
@@ -5,7 +5,6 @@ import (
 )
 
 type acceptContext struct {
-	Packet  fed.Packet
 	Conn    *fed.Conn
 	Options acceptOptions
 }
@@ -20,7 +19,7 @@ type context struct {
 
 func (T *context) ServerRead() error {
 	var err error
-	T.Packet, err = T.Server.ReadPacket(true, T.Packet)
+	T.Packet, err = T.Server.ReadPacket(true)
 	return err
 }
 
@@ -51,7 +50,7 @@ func (T *context) PeerRead() bool {
 		return false
 	}
 	var err error
-	T.Packet, err = T.Peer.ReadPacket(true, T.Packet)
+	T.Packet, err = T.Peer.ReadPacket(true)
 	if err != nil {
 		T.PeerFail(err)
 		return false
diff --git a/lib/bouncer/backends/v0/query.go b/lib/bouncer/backends/v0/query.go
index 89a8bd3f82d758a7f924f14d7944e0285e4376ef..d86b46c19faec8c64321c1dc02b44849f6e9ab66 100644
--- a/lib/bouncer/backends/v0/query.go
+++ b/lib/bouncer/backends/v0/query.go
@@ -1,7 +1,7 @@
 package backends
 
 import (
-	"fmt"
+	"strings"
 
 	"gfx.cafe/gfx/pggat/lib/fed"
 	packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
@@ -13,10 +13,8 @@ func copyIn(ctx *context) error {
 
 	for {
 		if !ctx.PeerRead() {
-			copyFail := packets.CopyFail{
-				Reason: "peer failed",
-			}
-			ctx.Packet = copyFail.IntoPacket(ctx.Packet)
+			copyFail := packets.CopyFail("peer failed")
+			ctx.Packet = &copyFail
 			return ctx.ServerWrite()
 		}
 
@@ -87,11 +85,13 @@ func query(ctx *context) error {
 				return err
 			}
 		case packets.TypeReadyForQuery:
-			var txState packets.ReadyForQuery
-			if !txState.ReadFromPacket(ctx.Packet) {
-				return ErrBadFormat
+			var p packets.ReadyForQuery
+			err = fed.ToConcrete(&p, ctx.Packet)
+			if err != nil {
+				return err
 			}
-			ctx.TxState = byte(txState)
+			ctx.Packet = &p
+			ctx.TxState = byte(p)
 			ctx.PeerWrite()
 			return nil
 		default:
@@ -102,28 +102,35 @@ func query(ctx *context) error {
 
 func queryString(ctx *context, q string) error {
 	qq := packets.Query(q)
-	ctx.Packet = qq.IntoPacket(ctx.Packet)
+	ctx.Packet = &qq
 	return query(ctx)
 }
 
-func QueryString(server, peer *fed.Conn, buffer fed.Packet, query string) (err, peerError error, packet fed.Packet) {
+func QueryString(server, peer *fed.Conn, query string) (err, peerError error) {
 	ctx := context{
 		Server: server,
 		Peer:   peer,
-		Packet: buffer,
 	}
 	err = queryString(&ctx, query)
 	peerError = ctx.PeerError
-	packet = ctx.Packet
 	return
 }
 
-func SetParameter(server, peer *fed.Conn, buffer fed.Packet, name strutil.CIString, value string) (err, peerError error, packet fed.Packet) {
+func SetParameter(server, peer *fed.Conn, name strutil.CIString, value string) (err, peerError error) {
+	var q strings.Builder
+	escapedName := strutil.Escape(name.String(), '"')
+	escapedValue := strutil.Escape(value, '\'')
+	q.Grow(len(`SET "" = ''`) + len(escapedName) + len(escapedValue))
+	q.WriteString(`SET "`)
+	q.WriteString(escapedName)
+	q.WriteString(`" = '`)
+	q.WriteString(escapedValue)
+	q.WriteString(`'`)
+
 	return QueryString(
 		server,
 		peer,
-		buffer,
-		fmt.Sprintf(`SET "%s" = '%s'`, strutil.Escape(name.String(), '"'), strutil.Escape(value, '\'')),
+		q.String(),
 	)
 }
 
@@ -146,11 +153,13 @@ func functionCall(ctx *context) error {
 			packets.TypeNotificationResponse:
 			ctx.PeerWrite()
 		case packets.TypeReadyForQuery:
-			var txState packets.ReadyForQuery
-			if !txState.ReadFromPacket(ctx.Packet) {
-				return ErrBadFormat
+			var p packets.ReadyForQuery
+			err = fed.ToConcrete(&p, ctx.Packet)
+			if err != nil {
+				return err
 			}
-			ctx.TxState = byte(txState)
+			ctx.Packet = &p
+			ctx.TxState = byte(p)
 			ctx.PeerWrite()
 			return nil
 		default:
@@ -160,7 +169,6 @@ func functionCall(ctx *context) error {
 }
 
 func sync(ctx *context) (bool, error) {
-	ctx.Packet = ctx.Packet.Reset(packets.TypeSync)
 	if err := ctx.ServerWrite(); err != nil {
 		return false, err
 	}
@@ -200,11 +208,13 @@ func sync(ctx *context) (bool, error) {
 				return false, err
 			}
 		case packets.TypeReadyForQuery:
-			var txState packets.ReadyForQuery
-			if !txState.ReadFromPacket(ctx.Packet) {
-				return false, ErrBadFormat
+			var p packets.ReadyForQuery
+			err = fed.ToConcrete(&p, ctx.Packet)
+			if err != nil {
+				return false, err
 			}
-			ctx.TxState = byte(txState)
+			ctx.Packet = &p
+			ctx.TxState = byte(p)
 			ctx.PeerWrite()
 			return true, nil
 		default:
@@ -213,15 +223,14 @@ func sync(ctx *context) (bool, error) {
 	}
 }
 
-func Sync(server, peer *fed.Conn, buffer fed.Packet) (err, peerErr error, packet fed.Packet) {
+func Sync(server, peer *fed.Conn) (err, peerErr error) {
 	ctx := context{
 		Server: server,
 		Peer:   peer,
-		Packet: buffer,
+		Packet: &packets.Sync{},
 	}
 	_, err = sync(&ctx)
 	peerErr = ctx.PeerError
-	packet = ctx.Packet
 	return
 }
 
@@ -233,6 +242,7 @@ func eqp(ctx *context) error {
 	for {
 		if !ctx.PeerRead() {
 			for {
+				ctx.Packet = &packets.Sync{}
 				ok, err := sync(ctx)
 				if err != nil {
 					return err
@@ -276,7 +286,7 @@ func transaction(ctx *context) error {
 		case packets.TypeSync:
 			// phony sync call, we can just reply with a fake ReadyForQuery(TxState)
 			rfq := packets.ReadyForQuery(ctx.TxState)
-			ctx.Packet = rfq.IntoPacket(ctx.Packet)
+			ctx.Packet = &rfq
 			ctx.PeerWrite()
 		case packets.TypeParse, packets.TypeBind, packets.TypeClose, packets.TypeDescribe, packets.TypeExecute, packets.TypeFlush:
 			if err := eqp(ctx); err != nil {
@@ -305,7 +315,7 @@ func transaction(ctx *context) error {
 	}
 }
 
-func Transaction(server, peer *fed.Conn, initialPacket fed.Packet) (err, peerError error, packet fed.Packet) {
+func Transaction(server, peer *fed.Conn, initialPacket fed.Packet) (err, peerError error) {
 	ctx := context{
 		Server: server,
 		Peer:   peer,
@@ -313,6 +323,5 @@ func Transaction(server, peer *fed.Conn, initialPacket fed.Packet) (err, peerErr
 	}
 	err = transaction(&ctx)
 	peerError = ctx.PeerError
-	packet = ctx.Packet
 	return
 }
diff --git a/lib/bouncer/bouncers/v2/bouncer.go b/lib/bouncer/bouncers/v2/bouncer.go
index 573b8cc28a3ac92de6dcf1c5a03cfa5ace827877..946c66b29e11f4739a51a662743d28f37a1c1228 100644
--- a/lib/bouncer/bouncers/v2/bouncer.go
+++ b/lib/bouncer/bouncers/v2/bouncer.go
@@ -3,27 +3,22 @@ package bouncers
 import (
 	"gfx.cafe/gfx/pggat/lib/bouncer/backends/v0"
 	"gfx.cafe/gfx/pggat/lib/fed"
-	packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
 	"gfx.cafe/gfx/pggat/lib/perror"
 )
 
-func clientFail(packet fed.Packet, client *fed.Conn, err perror.Error) fed.Packet {
+func clientFail(client *fed.Conn, err perror.Error) {
 	// send fatal error to client
-	resp := packets.ErrorResponse{
-		Error: err,
-	}
-	packet = resp.IntoPacket(packet)
-	_ = client.WritePacket(packet)
-	return packet
+	resp := perror.ToPacket(err)
+	_ = client.WritePacket(resp)
 }
 
-func Bounce(client, server *fed.Conn, initialPacket fed.Packet) (packet fed.Packet, clientError error, serverError error) {
-	serverError, clientError, packet = backends.Transaction(server, client, initialPacket)
+func Bounce(client, server *fed.Conn, initialPacket fed.Packet) (clientError error, serverError error) {
+	serverError, clientError = backends.Transaction(server, client, initialPacket)
 
 	if clientError != nil {
-		packet = clientFail(packet, client, perror.Wrap(clientError))
+		clientFail(client, perror.Wrap(clientError))
 	} else if serverError != nil {
-		packet = clientFail(packet, client, perror.Wrap(serverError))
+		clientFail(client, perror.Wrap(serverError))
 	}
 
 	return
diff --git a/lib/bouncer/frontends/v0/accept.go b/lib/bouncer/frontends/v0/accept.go
index 5fadb9e3e51ba004a9e06fa86f103944bb7b09fd..00036e1abc5a8672eac16afe582f83d69b29b431 100644
--- a/lib/bouncer/frontends/v0/accept.go
+++ b/lib/bouncer/frontends/v0/accept.go
@@ -2,7 +2,6 @@ package frontends
 
 import (
 	"crypto/tls"
-	"io"
 	"strings"
 
 	"gfx.cafe/gfx/pggat/lib/fed"
@@ -14,72 +13,47 @@ import (
 func startup0(
 	ctx *acceptContext,
 	params *acceptParams,
-) (cancelling bool, done bool, err perror.Error) {
-	var err2 error
-	ctx.Packet, err2 = ctx.Conn.ReadPacket(false, ctx.Packet)
-	if err2 != nil {
-		err = perror.Wrap(err2)
+) (cancelling bool, done bool, err error) {
+	var packet fed.Packet
+	packet, err = ctx.Conn.ReadPacket(false)
+	if err != nil {
 		return
 	}
 
-	var majorVersion uint16
-	var minorVersion uint16
-	p := ctx.Packet.ReadUint16(&majorVersion)
-	p = p.ReadUint16(&minorVersion)
+	var p packets.Startup
+	err = fed.ToConcrete(&p, packet)
+	if err != nil {
+		return
+	}
 
-	if majorVersion == 1234 {
-		// Cancel or SSL
-		switch minorVersion {
-		case 5678:
+	switch mode := p.Mode.(type) {
+	case *packets.StartupPayloadControl:
+		switch control := mode.Mode.(type) {
+		case *packets.StartupPayloadControlPayloadCancel:
 			// Cancel
-			p.ReadBytes(params.CancelKey[:])
+			params.CancelKey.ProcessID = control.ProcessID
+			params.CancelKey.SecretKey = control.SecretKey
 			cancelling = true
 			done = true
 			return
-		case 5679:
-			byteWriter, ok := ctx.Conn.ReadWriteCloser.(io.ByteWriter)
-			if !ok {
-				err = perror.New(
-					perror.FATAL,
-					perror.FeatureNotSupported,
-					"SSL is not supported",
-				)
-				return
-			}
-
+		case *packets.StartupPayloadControlPayloadSSL:
 			// ssl is not enabled
 			if ctx.Options.SSLConfig == nil {
-				err = perror.Wrap(byteWriter.WriteByte('N'))
-				return
-			}
-
-			sslServer, ok := ctx.Conn.ReadWriteCloser.(fed.SSLServer)
-			if !ok {
-				err = perror.Wrap(byteWriter.WriteByte('N'))
+				err = ctx.Conn.WriteByte('N')
 				return
 			}
 
 			// do ssl
-			if err = perror.Wrap(byteWriter.WriteByte('S')); err != nil {
+			if err = ctx.Conn.WriteByte('S'); err != nil {
 				return
 			}
-			if err = perror.Wrap(sslServer.EnableSSLServer(ctx.Options.SSLConfig)); err != nil {
+			if err = ctx.Conn.EnableSSL(ctx.Options.SSLConfig, false); err != nil {
 				return
 			}
 			return
-		case 5680:
-			byteWriter, ok := ctx.Conn.ReadWriteCloser.(io.ByteWriter)
-			if !ok {
-				err = perror.New(
-					perror.FATAL,
-					perror.FeatureNotSupported,
-					"GSSAPI is not supported",
-				)
-				return
-			}
-
+		case *packets.StartupPayloadControlPayloadGSSAPI:
 			// GSSAPI is not supported yet
-			err = perror.Wrap(byteWriter.WriteByte('N'))
+			err = ctx.Conn.WriteByte('N')
 			return
 		default:
 			err = perror.New(
@@ -89,121 +63,108 @@ func startup0(
 			)
 			return
 		}
-	}
-
-	if majorVersion != 3 {
-		err = perror.New(
-			perror.FATAL,
-			perror.ProtocolViolation,
-			"Unsupported protocol version",
-		)
-		return
-	}
-
-	var unsupportedOptions []string
-
-	for {
-		var key string
-		p = p.ReadString(&key)
-		if key == "" {
-			break
-		}
-
-		var value string
-		p = p.ReadString(&value)
-
-		switch key {
-		case "user":
-			ctx.Conn.User = value
-		case "database":
-			ctx.Conn.Database = value
-		case "options":
-			fields := strings.Fields(value)
-			for i := 0; i < len(fields); i++ {
-				switch fields[i] {
-				case "-c":
-					i++
-					set := fields[i]
-					var ok bool
-					key, value, ok = strings.Cut(set, "=")
-					if !ok {
+	case *packets.StartupPayloadVersion3:
+		var unsupportedOptions []string
+
+		for _, parameter := range mode.Parameters {
+			switch parameter.Key {
+			case "user":
+				ctx.Conn.User = parameter.Value
+			case "database":
+				ctx.Conn.Database = parameter.Value
+			case "options":
+				fields := strings.Fields(parameter.Value)
+				for i := 0; i < len(fields); i++ {
+					switch fields[i] {
+					case "-c":
+						i++
+						set := fields[i]
+						key, value, ok := strings.Cut(set, "=")
+						if !ok {
+							err = perror.New(
+								perror.FATAL,
+								perror.ProtocolViolation,
+								"Expected key=value",
+							)
+							return
+						}
+
+						ikey := strutil.MakeCIString(key)
+
+						if ctx.Conn.InitialParameters == nil {
+							ctx.Conn.InitialParameters = make(map[strutil.CIString]string)
+						}
+						ctx.Conn.InitialParameters[ikey] = value
+					default:
 						err = perror.New(
 							perror.FATAL,
-							perror.ProtocolViolation,
-							"Expected key=value",
+							perror.FeatureNotSupported,
+							"Flag not supported, sorry",
 						)
 						return
 					}
-
-					ikey := strutil.MakeCIString(key)
+				}
+			case "replication":
+				err = perror.New(
+					perror.FATAL,
+					perror.FeatureNotSupported,
+					"Replication mode is not supported yet",
+				)
+				return
+			default:
+				if strings.HasPrefix(parameter.Key, "_pq_.") {
+					// we don't support protocol extensions at the moment
+					unsupportedOptions = append(unsupportedOptions, parameter.Key)
+				} else {
+					ikey := strutil.MakeCIString(parameter.Key)
 
 					if ctx.Conn.InitialParameters == nil {
 						ctx.Conn.InitialParameters = make(map[strutil.CIString]string)
 					}
-					ctx.Conn.InitialParameters[ikey] = value
-				default:
-					err = perror.New(
-						perror.FATAL,
-						perror.FeatureNotSupported,
-						"Flag not supported, sorry",
-					)
-					return
+					ctx.Conn.InitialParameters[ikey] = parameter.Value
 				}
 			}
-		case "replication":
+		}
+
+		if mode.MinorVersion != 0 || len(unsupportedOptions) > 0 {
+			// negotiate protocol
+			uopts := packets.NegotiateProtocolVersion{
+				MinorProtocolVersion:        0,
+				UnrecognizedProtocolOptions: unsupportedOptions,
+			}
+			err = ctx.Conn.WritePacket(&uopts)
+			if err != nil {
+				return
+			}
+		}
+
+		if ctx.Conn.User == "" {
 			err = perror.New(
 				perror.FATAL,
-				perror.FeatureNotSupported,
-				"Replication mode is not supported yet",
+				perror.InvalidAuthorizationSpecification,
+				"User is required",
 			)
 			return
-		default:
-			if strings.HasPrefix(key, "_pq_.") {
-				// we don't support protocol extensions at the moment
-				unsupportedOptions = append(unsupportedOptions, key)
-			} else {
-				ikey := strutil.MakeCIString(key)
-
-				if ctx.Conn.InitialParameters == nil {
-					ctx.Conn.InitialParameters = make(map[strutil.CIString]string)
-				}
-				ctx.Conn.InitialParameters[ikey] = value
-			}
-		}
-	}
-
-	if minorVersion != 0 || len(unsupportedOptions) > 0 {
-		// negotiate protocol
-		uopts := packets.NegotiateProtocolVersion{
-			MinorProtocolVersion: 0,
-			UnrecognizedOptions:  unsupportedOptions,
 		}
-		ctx.Packet = uopts.IntoPacket(ctx.Packet)
-		err = perror.Wrap(ctx.Conn.WritePacket(ctx.Packet))
-		if err != nil {
-			return
+		if ctx.Conn.Database == "" {
+			ctx.Conn.Database = ctx.Conn.User
 		}
-	}
 
-	if ctx.Conn.User == "" {
+		done = true
+		return
+	default:
 		err = perror.New(
 			perror.FATAL,
-			perror.InvalidAuthorizationSpecification,
-			"User is required",
+			perror.ProtocolViolation,
+			"Unsupported protocol version",
 		)
 		return
 	}
-	if ctx.Conn.Database == "" {
-		ctx.Conn.Database = ctx.Conn.User
-	}
-
-	done = true
-	return
 }
 
 func accept0(
 	ctx *acceptContext,
-) (params acceptParams, err perror.Error) {
+) (params acceptParams, err error) {
 	for {
 		var done bool
 		params.IsCanceling, done, err = startup0(ctx, &params)
@@ -218,27 +179,24 @@ func accept0(
 	return
 }
 
-func fail(packet fed.Packet, client fed.ReadWriter, err perror.Error) {
-	resp := packets.ErrorResponse{
-		Error: err,
-	}
-	packet = resp.IntoPacket(packet)
-	_ = client.WritePacket(packet)
+func fail(client *fed.Conn, err error) {
+	resp := perror.ToPacket(perror.Wrap(err))
+	_ = client.WritePacket(resp)
 }
 
-func accept(ctx *acceptContext) (acceptParams, perror.Error) {
+func accept(ctx *acceptContext) (acceptParams, error) {
 	params, err := accept0(ctx)
 	if err != nil {
-		fail(ctx.Packet, ctx.Conn, err)
+		fail(ctx.Conn, err)
 		return acceptParams{}, err
 	}
 	return params, nil
 }
 
 func Accept(conn *fed.Conn, tlsConfig *tls.Config) (
-	cancelKey [8]byte,
+	cancelKey fed.BackendKey,
 	isCanceling bool,
-	err perror.Error,
+	err error,
 ) {
 	ctx := acceptContext{
 		Conn: conn,
diff --git a/lib/bouncer/frontends/v0/authenticate.go b/lib/bouncer/frontends/v0/authenticate.go
index c22f4e3920af9877f2bceb6ca3d56dc49e4532ac..4c534ee24599dae1267b793998d9b20e25a5a95c 100644
--- a/lib/bouncer/frontends/v0/authenticate.go
+++ b/lib/bouncer/frontends/v0/authenticate.go
@@ -2,6 +2,7 @@ package frontends
 
 import (
 	"crypto/rand"
+	"encoding/binary"
 	"errors"
 	"io"
 
@@ -11,69 +12,73 @@ import (
 	"gfx.cafe/gfx/pggat/lib/perror"
 )
 
-func authenticationSASLInitial(ctx *authenticateContext, creds auth.SASLServer) (tool auth.SASLVerifier, resp []byte, done bool, err perror.Error) {
+func authenticationSASLInitial(ctx *authenticateContext, creds auth.SASLServer) (tool auth.SASLVerifier, resp []byte, done bool, err error) {
 	// check which authentication method the client wants
-	var err2 error
-	ctx.Packet, err2 = ctx.Conn.ReadPacket(true, ctx.Packet)
-	if err2 != nil {
-		err = perror.Wrap(err2)
+	var packet fed.Packet
+	packet, err = ctx.Conn.ReadPacket(true)
+	if err != nil {
 		return
 	}
-	var initialResponse packets.SASLInitialResponse
-	if !initialResponse.ReadFromPacket(ctx.Packet) {
-		err = packets.ErrBadFormat
+	var p packets.SASLInitialResponse
+	err = fed.ToConcrete(&p, packet)
+	if err != nil {
 		return
 	}
 
-	tool, err2 = creds.VerifySASL(initialResponse.Mechanism)
-	if err2 != nil {
-		err = perror.Wrap(err2)
+	tool, err = creds.VerifySASL(p.Mechanism)
+	if err != nil {
 		return
 	}
 
-	resp, err2 = tool.Write(initialResponse.InitialResponse)
-	if err2 != nil {
-		if errors.Is(err2, io.EOF) {
+	resp, err = tool.Write(p.InitialClientResponse)
+	if err != nil {
+		if errors.Is(err, io.EOF) {
 			done = true
+			err = nil
 			return
 		}
-		err = perror.Wrap(err2)
 		return
 	}
 	return
 }
 
-func authenticationSASLContinue(ctx *authenticateContext, tool auth.SASLVerifier) (resp []byte, done bool, err perror.Error) {
-	var err2 error
-	ctx.Packet, err2 = ctx.Conn.ReadPacket(true, ctx.Packet)
-	if err2 != nil {
-		err = perror.Wrap(err2)
+func authenticationSASLContinue(ctx *authenticateContext, tool auth.SASLVerifier) (resp []byte, done bool, err error) {
+	var packet fed.Packet
+	packet, err = ctx.Conn.ReadPacket(true)
+	if err != nil {
 		return
 	}
-	var authResp packets.AuthenticationResponse
-	if !authResp.ReadFromPacket(ctx.Packet) {
-		err = packets.ErrBadFormat
+	var p packets.SASLResponse
+	err = fed.ToConcrete(&p, packet)
+	if err != nil {
 		return
 	}
 
-	resp, err2 = tool.Write(authResp)
-	if err2 != nil {
-		if errors.Is(err2, io.EOF) {
+	resp, err = tool.Write(p)
+	if err != nil {
+		if errors.Is(err, io.EOF) {
 			done = true
+			err = nil
 			return
 		}
-		err = perror.Wrap(err2)
 		return
 	}
 	return
 }
 
-func authenticationSASL(ctx *authenticateContext, creds auth.SASLServer) perror.Error {
-	saslInitial := packets.AuthenticationSASL{
-		Mechanisms: creds.SupportedSASLMechanisms(),
+func authenticationSASL(ctx *authenticateContext, creds auth.SASLServer) error {
+	var mode packets.AuthenticationPayloadSASL
+	mechanisms := creds.SupportedSASLMechanisms()
+	for _, mechanism := range mechanisms {
+		mode = append(mode, packets.AuthenticationPayloadSASLMethod{
+			Method: mechanism,
+		})
+	}
+
+	saslInitial := packets.Authentication{
+		Mode: &mode,
 	}
-	ctx.Packet = saslInitial.IntoPacket(ctx.Packet)
-	err := perror.Wrap(ctx.Conn.WritePacket(ctx.Packet))
+	err := ctx.Conn.WritePacket(&saslInitial)
 	if err != nil {
 		return err
 	}
@@ -85,17 +90,21 @@ func authenticationSASL(ctx *authenticateContext, creds auth.SASLServer) perror.
 
 	for {
 		if done {
-			final := packets.AuthenticationSASLFinal(resp)
-			ctx.Packet = final.IntoPacket(ctx.Packet)
-			err = perror.Wrap(ctx.Conn.WritePacket(ctx.Packet))
+			m := packets.AuthenticationPayloadSASLFinal(resp)
+			final := packets.Authentication{
+				Mode: &m,
+			}
+			err = ctx.Conn.WritePacket(&final)
 			if err != nil {
 				return err
 			}
 			break
 		} else {
-			cont := packets.AuthenticationSASLContinue(resp)
-			ctx.Packet = cont.IntoPacket(ctx.Packet)
-			err = perror.Wrap(ctx.Conn.WritePacket(ctx.Packet))
+			m := packets.AuthenticationPayloadSASLContinue(resp)
+			cont := packets.Authentication{
+				Mode: &m,
+			}
+			err = ctx.Conn.WritePacket(&cont)
 			if err != nil {
 				return err
 			}
@@ -110,39 +119,41 @@ func authenticationSASL(ctx *authenticateContext, creds auth.SASLServer) perror.
 	return nil
 }
 
-func authenticationMD5(ctx *authenticateContext, creds auth.MD5Server) perror.Error {
+func authenticationMD5(ctx *authenticateContext, creds auth.MD5Server) error {
 	var salt [4]byte
 	_, err := rand.Read(salt[:])
 	if err != nil {
-		return perror.Wrap(err)
+		return err
 	}
-	md5Initial := packets.AuthenticationMD5{
-		Salt: salt,
+	mode := packets.AuthenticationPayloadMD5Password(salt)
+	md5Initial := packets.Authentication{
+		Mode: &mode,
 	}
-	ctx.Packet = md5Initial.IntoPacket(ctx.Packet)
-	err = ctx.Conn.WritePacket(ctx.Packet)
+	err = ctx.Conn.WritePacket(&md5Initial)
 	if err != nil {
-		return perror.Wrap(err)
+		return err
 	}
 
-	ctx.Packet, err = ctx.Conn.ReadPacket(true, ctx.Packet)
+	var packet fed.Packet
+	packet, err = ctx.Conn.ReadPacket(true)
 	if err != nil {
-		return perror.Wrap(err)
+		return err
 	}
 
 	var pw packets.PasswordMessage
-	if !pw.ReadFromPacket(ctx.Packet) {
-		return packets.ErrUnexpectedPacket
+	err = fed.ToConcrete(&pw, packet)
+	if err != nil {
+		return err
 	}
 
-	if err = creds.VerifyMD5(salt, pw.Password); err != nil {
-		return perror.Wrap(err)
+	if err = creds.VerifyMD5(salt, string(pw)); err != nil {
+		return err
 	}
 
 	return nil
 }
 
-func authenticate(ctx *authenticateContext) (err perror.Error) {
+func authenticate(ctx *authenticateContext) (err error) {
 	if ctx.Options.Credentials != nil {
 		if credsSASL, ok := ctx.Options.Credentials.(auth.SASLServer); ok {
 			err = authenticationSASL(ctx, credsSASL)
@@ -161,32 +172,40 @@ func authenticate(ctx *authenticateContext) (err perror.Error) {
 	}
 
 	// send auth Ok
-	authOk := packets.AuthenticationOk{}
-	ctx.Packet = authOk.IntoPacket(ctx.Packet)
-	if err = perror.Wrap(ctx.Conn.WritePacket(ctx.Packet)); err != nil {
+	authOk := packets.Authentication{
+		Mode: &packets.AuthenticationPayloadOk{},
+	}
+	if err = ctx.Conn.WritePacket(&authOk); err != nil {
 		return
 	}
 	ctx.Conn.Authenticated = true
 
 	// send backend key data
-	_, err2 := rand.Read(ctx.Conn.BackendKey[:])
-	if err2 != nil {
-		err = perror.Wrap(err2)
+	var processID [4]byte
+	if _, err = rand.Reader.Read(processID[:]); err != nil {
 		return
 	}
+	var backendKey [4]byte
+	if _, err = rand.Reader.Read(backendKey[:]); err != nil {
+		return
+	}
+	ctx.Conn.BackendKey = fed.BackendKey{
+		ProcessID: int32(binary.BigEndian.Uint32(processID[:])),
+		SecretKey: int32(binary.BigEndian.Uint32(backendKey[:])),
+	}
 
 	keyData := packets.BackendKeyData{
-		CancellationKey: ctx.Conn.BackendKey,
+		ProcessID: ctx.Conn.BackendKey.ProcessID,
+		SecretKey: ctx.Conn.BackendKey.SecretKey,
 	}
-	ctx.Packet = keyData.IntoPacket(ctx.Packet)
-	if err = perror.Wrap(ctx.Conn.WritePacket(ctx.Packet)); err != nil {
+	if err = ctx.Conn.WritePacket(&keyData); err != nil {
 		return
 	}
 
 	return
 }
 
-func Authenticate(conn *fed.Conn, creds auth.Credentials) (err perror.Error) {
+func Authenticate(conn *fed.Conn, creds auth.Credentials) (err error) {
 	if conn.Authenticated {
 		// already authenticated
 		return
diff --git a/lib/bouncer/frontends/v0/context.go b/lib/bouncer/frontends/v0/context.go
index e0668cef9a24a5c7287da9562012e47f27cda543..993073ffab07b6d371176eff956b7627fa814a75 100644
--- a/lib/bouncer/frontends/v0/context.go
+++ b/lib/bouncer/frontends/v0/context.go
@@ -3,13 +3,11 @@ package frontends
 import "gfx.cafe/gfx/pggat/lib/fed"
 
 type acceptContext struct {
-	Packet  fed.Packet
 	Conn    *fed.Conn
 	Options acceptOptions
 }
 
 type authenticateContext struct {
-	Packet  fed.Packet
 	Conn    *fed.Conn
 	Options authenticateOptions
 }
diff --git a/lib/bouncer/frontends/v0/params.go b/lib/bouncer/frontends/v0/params.go
index 0d960dbf3edbad11e492c22e899dd71e08824172..4f28f8347cb111f03ade898b28fde91364420d3b 100644
--- a/lib/bouncer/frontends/v0/params.go
+++ b/lib/bouncer/frontends/v0/params.go
@@ -1,6 +1,8 @@
 package frontends
 
+import "gfx.cafe/gfx/pggat/lib/fed"
+
 type acceptParams struct {
-	CancelKey   [8]byte
+	CancelKey   fed.BackendKey
 	IsCanceling bool
 }
diff --git a/lib/fed/backendkey.go b/lib/fed/backendkey.go
new file mode 100644
index 0000000000000000000000000000000000000000..e611366bbcf9db31d64ddf330c0a0d455f9ec5ca
--- /dev/null
+++ b/lib/fed/backendkey.go
@@ -0,0 +1,6 @@
+package fed
+
+type BackendKey struct {
+	ProcessID int32
+	SecretKey int32
+}
diff --git a/lib/fed/conn.go b/lib/fed/conn.go
index c0b8e990d29292906f42356c127a0b1e7cce87db..547981adff48d7d564ff4b653fd9b05504b75b6d 100644
--- a/lib/fed/conn.go
+++ b/lib/fed/conn.go
@@ -1,6 +1,10 @@
 package fed
 
 import (
+	"crypto/tls"
+	"errors"
+	"net"
+
 	"gfx.cafe/gfx/pggat/lib/util/decorator"
 	"gfx.cafe/gfx/pggat/lib/util/strutil"
 )
@@ -8,54 +12,128 @@ import (
 type Conn struct {
 	noCopy decorator.NoCopy
 
-	ReadWriteCloser
+	encoder Encoder
+	decoder Decoder
+
+	NetConn net.Conn
 
 	Middleware []Middleware
+	SSL        bool
 
 	User              string
 	Database          string
 	InitialParameters map[strutil.CIString]string
 	Authenticated     bool
-	BackendKey        [8]byte
+	BackendKey        BackendKey
 }
 
-func NewConn(rw ReadWriteCloser) *Conn {
-	return &Conn{
-		ReadWriteCloser: rw,
+func NewConn(rw net.Conn) *Conn {
+	c := &Conn{
+		NetConn: rw,
 	}
+	c.encoder.Writer.Reset(rw)
+	c.decoder.Reader.Reset(rw)
+	return c
+}
+
+func (T *Conn) Flush() error {
+	return T.encoder.Flush()
 }
 
-func (T *Conn) ReadPacket(typed bool, buffer Packet) (packet Packet, err error) {
-	packet = buffer
+func (T *Conn) ReadPacket(typed bool) (Packet, error) {
+	if err := T.Flush(); err != nil {
+		return nil, err
+	}
+
 	for {
-		packet, err = T.ReadWriteCloser.ReadPacket(typed, buffer)
-		if err != nil {
-			return
+		if err := T.decoder.Next(typed); err != nil {
+			return nil, err
+		}
+		var packet Packet
+		packet = PendingPacket{
+			Decoder: &T.decoder,
 		}
 		for _, middleware := range T.Middleware {
+			var err error
 			packet, err = middleware.ReadPacket(packet)
 			if err != nil {
-				return
+				return nil, err
 			}
-			if len(packet) == 0 {
+			if packet == nil {
 				break
 			}
 		}
-		if len(packet) != 0 {
-			return
+		if packet != nil {
+			return packet, nil
 		}
 	}
 }
 
-func (T *Conn) WritePacket(packet Packet) (err error) {
+func (T *Conn) WritePacket(packet Packet) error {
 	for _, middleware := range T.Middleware {
+		var err error
 		packet, err = middleware.WritePacket(packet)
-		if err != nil || len(packet) == 0 {
-			return
+		if err != nil {
+			return err
+		}
+		if packet == nil {
+			break
 		}
 	}
-	err = T.ReadWriteCloser.WritePacket(packet)
-	return
+	if packet == nil {
+		return nil
+	}
+
+	err := T.encoder.Next(packet.Type(), packet.Length())
+	if err != nil {
+		return err
+	}
+
+	return packet.WriteTo(&T.encoder)
+}
+
+func (T *Conn) WriteByte(b byte) error {
+	return T.encoder.WriteByte(b)
 }
 
-var _ ReadWriteCloser = (*Conn)(nil)
+func (T *Conn) ReadByte() (byte, error) {
+	if err := T.Flush(); err != nil {
+		return 0, err
+	}
+
+	return T.decoder.ReadByte()
+}
+
+func (T *Conn) EnableSSL(config *tls.Config, isClient bool) error {
+	if T.SSL {
+		return errors.New("SSL is already enabled")
+	}
+	T.SSL = true
+
+	// Flush buffers
+	if err := T.Flush(); err != nil {
+		return err
+	}
+	if T.decoder.Reader.Buffered() > 0 {
+		return errors.New("expected empty read buffer")
+	}
+
+	var sslConn *tls.Conn
+	if isClient {
+		sslConn = tls.Client(T.NetConn, config)
+	} else {
+		sslConn = tls.Server(T.NetConn, config)
+	}
+	T.encoder.Writer.Reset(sslConn)
+	T.decoder.Reader.Reset(sslConn)
+	T.NetConn = sslConn
+	return sslConn.Handshake()
+}
+
+func (T *Conn) Close() error {
+	if err := T.encoder.Flush(); err != nil {
+		return err
+	}
+
+	return T.NetConn.Close()
+}
diff --git a/lib/fed/decoder.go b/lib/fed/decoder.go
new file mode 100644
index 0000000000000000000000000000000000000000..df53acd30208adae54cedf22da2e6b99edbe73e0
--- /dev/null
+++ b/lib/fed/decoder.go
@@ -0,0 +1,156 @@
+package fed
+
+import (
+	"bufio"
+	"encoding/binary"
+	"io"
+	"math"
+
+	"gfx.cafe/gfx/pggat/lib/util/decorator"
+)
+
+type Decoder struct {
+	noCopy decorator.NoCopy
+
+	Reader bufio.Reader
+
+	typ Type
+	len int
+	pos int
+
+	buf [8]byte
+}
+
+func NewDecoder(r io.Reader) *Decoder {
+	d := &Decoder{}
+	d.Reader.Reset(r)
+	return d
+}
+
+func (T *Decoder) Read(b []byte) (n int, err error) {
+	rem := T.len - T.pos
+	if rem == 0 {
+		err = io.EOF
+		return
+	}
+	if len(b) > rem {
+		n, err = T.Reader.Read(b[:rem])
+	} else {
+		n, err = T.Reader.Read(b)
+	}
+	T.pos += n
+	return
+}
+
+func (T *Decoder) ReadByte() (byte, error) {
+	if T.pos != T.len {
+		_, err := T.Reader.Discard(T.len - T.pos)
+		if err != nil {
+			return 0, err
+		}
+	}
+
+	T.typ = 0
+	T.len = 0
+	T.pos = 0
+	return T.Reader.ReadByte()
+}
+
+func (T *Decoder) Next(typed bool) error {
+	if T.pos != T.len {
+		_, err := T.Reader.Discard(T.len - T.pos)
+		if err != nil {
+			return err
+		}
+	}
+
+	var err error
+	if typed {
+		_, err = io.ReadFull(&T.Reader, T.buf[:5])
+	} else {
+		T.buf[0] = 0
+		_, err = io.ReadFull(&T.Reader, T.buf[1:5])
+	}
+	if err != nil {
+		return err
+	}
+	T.typ = Type(T.buf[0])
+	T.len = int(binary.BigEndian.Uint32(T.buf[1:5])) - 4
+	T.pos = 0
+	return nil
+}
+
+func (T *Decoder) Type() Type {
+	return T.typ
+}
+
+func (T *Decoder) Length() int {
+	return T.len
+}
+
+func (T *Decoder) Position() int {
+	return T.pos
+}
+
+func (T *Decoder) Uint8() (uint8, error) {
+	v, err := T.Reader.ReadByte()
+	T.pos += 1
+	return v, err
+}
+
+func (T *Decoder) Uint16() (uint16, error) {
+	_, err := io.ReadFull(&T.Reader, T.buf[:2])
+	T.pos += 2
+	return binary.BigEndian.Uint16(T.buf[:2]), err
+}
+
+func (T *Decoder) Uint32() (uint32, error) {
+	_, err := io.ReadFull(&T.Reader, T.buf[:4])
+	T.pos += 4
+	return binary.BigEndian.Uint32(T.buf[:4]), err
+}
+
+func (T *Decoder) Uint64() (uint64, error) {
+	_, err := io.ReadFull(&T.Reader, T.buf[:8])
+	T.pos += 8
+	return binary.BigEndian.Uint64(T.buf[:8]), err
+}
+
+func (T *Decoder) Int8() (int8, error) {
+	v, err := T.Uint8()
+	return int8(v), err
+}
+
+func (T *Decoder) Int16() (int16, error) {
+	v, err := T.Uint16()
+	return int16(v), err
+}
+
+func (T *Decoder) Int32() (int32, error) {
+	v, err := T.Uint32()
+	return int32(v), err
+}
+
+func (T *Decoder) Int64() (int64, error) {
+	v, err := T.Uint64()
+	return int64(v), err
+}
+
+func (T *Decoder) Float32() (float32, error) {
+	v, err := T.Uint32()
+	return math.Float32frombits(v), err
+}
+
+func (T *Decoder) Float64() (float64, error) {
+	v, err := T.Uint64()
+	return math.Float64frombits(v), err
+}
+
+func (T *Decoder) String() (string, error) {
+	s, err := T.Reader.ReadString(0)
+	if err != nil {
+		return "", err
+	}
+	T.pos += len(s)
+	return s[:len(s)-1], nil
+}
diff --git a/lib/fed/encoder.go b/lib/fed/encoder.go
new file mode 100644
index 0000000000000000000000000000000000000000..0acf75053a46b3df37c806571d144e817dda13e8
--- /dev/null
+++ b/lib/fed/encoder.go
@@ -0,0 +1,137 @@
+package fed
+
+import (
+	"bufio"
+	"encoding/binary"
+	"io"
+	"math"
+
+	"gfx.cafe/gfx/pggat/lib/util/decorator"
+)
+
+type Encoder struct {
+	noCopy decorator.NoCopy
+
+	Writer bufio.Writer
+
+	typ Type
+	len int
+	pos int
+
+	buf [8]byte
+}
+
+func NewEncoder(w io.Writer) *Encoder {
+	e := &Encoder{}
+	e.Writer.Reset(w)
+	return e
+}
+
+func (T *Encoder) Flush() error {
+	return T.Writer.Flush()
+}
+
+func (T *Encoder) WriteByte(b byte) error {
+	if T.pos != T.len {
+		panic("wrong number of bytes written")
+	}
+
+	T.typ = 0
+	T.len = 0
+	T.pos = 0
+	return T.Writer.WriteByte(b)
+}
+
+func (T *Encoder) Next(typ Type, length int) error {
+	if T.pos != T.len {
+		panic("wrong number of bytes written")
+	}
+
+	if typ != 0 {
+		if err := T.Writer.WriteByte(byte(typ)); err != nil {
+			return err
+		}
+	}
+
+	binary.BigEndian.PutUint32(T.buf[:4], uint32(length+4))
+	_, err := T.Writer.Write(T.buf[:4])
+
+	T.typ = typ
+	T.len = length
+	T.pos = 0
+
+	return err
+}
+
+func (T *Encoder) Type() Type {
+	return T.typ
+}
+
+func (T *Encoder) Length() int {
+	return T.len
+}
+
+func (T *Encoder) Position() int {
+	return T.pos
+}
+
+func (T *Encoder) Uint8(v uint8) error {
+	err := T.Writer.WriteByte(v)
+	T.pos += 1
+	return err
+}
+
+func (T *Encoder) Uint16(v uint16) error {
+	binary.BigEndian.PutUint16(T.buf[:2], v)
+	_, err := T.Writer.Write(T.buf[:2])
+	T.pos += 2
+	return err
+}
+
+func (T *Encoder) Uint32(v uint32) error {
+	binary.BigEndian.PutUint32(T.buf[:4], v)
+	_, err := T.Writer.Write(T.buf[:4])
+	T.pos += 4
+	return err
+}
+
+func (T *Encoder) Uint64(v uint64) error {
+	binary.BigEndian.PutUint64(T.buf[:8], v)
+	_, err := T.Writer.Write(T.buf[:8])
+	T.pos += 8
+	return err
+}
+
+func (T *Encoder) Int8(v int8) error {
+	return T.Uint8(uint8(v))
+}
+
+func (T *Encoder) Int16(v int16) error {
+	return T.Uint16(uint16(v))
+}
+
+func (T *Encoder) Int32(v int32) error {
+	return T.Uint32(uint32(v))
+}
+
+func (T *Encoder) Int64(v int64) error {
+	return T.Uint64(uint64(v))
+}
+
+func (T *Encoder) Float32(v float32) error {
+	return T.Uint32(math.Float32bits(v))
+}
+
+func (T *Encoder) Float64(v float64) error {
+	return T.Uint64(math.Float64bits(v))
+}
+
+func (T *Encoder) String(v string) error {
+	n, err := T.Writer.WriteString(v)
+	if err != nil {
+		return err
+	}
+	err = T.Writer.WriteByte(0)
+	T.pos += n + 1
+	return err
+}
diff --git a/lib/fed/middlewares/eqp/client.go b/lib/fed/middlewares/eqp/client.go
index 47417442094ac0b94e8304be45c5fb1af67a8cbd..aeaab9e81db6b8ff2815286c581ab4177de5d103 100644
--- a/lib/fed/middlewares/eqp/client.go
+++ b/lib/fed/middlewares/eqp/client.go
@@ -13,13 +13,11 @@ func NewClient() *Client {
 }
 
 func (T *Client) ReadPacket(packet fed.Packet) (fed.Packet, error) {
-	T.state.C2S(packet)
-	return packet, nil
+	return T.state.C2S(packet)
 }
 
 func (T *Client) WritePacket(packet fed.Packet) (fed.Packet, error) {
-	T.state.S2C(packet)
-	return packet, nil
+	return T.state.S2C(packet)
 }
 
 var _ fed.Middleware = (*Client)(nil)
diff --git a/lib/fed/middlewares/eqp/server.go b/lib/fed/middlewares/eqp/server.go
index f1b9e7889ac2b5bf3fae74e07edf4e4c73080bcc..9b070d971dde21c1292e36e2788d64cf02d4087c 100644
--- a/lib/fed/middlewares/eqp/server.go
+++ b/lib/fed/middlewares/eqp/server.go
@@ -13,13 +13,11 @@ func NewServer() *Server {
 }
 
 func (T *Server) ReadPacket(packet fed.Packet) (fed.Packet, error) {
-	T.state.S2C(packet)
-	return packet, nil
+	return T.state.S2C(packet)
 }
 
 func (T *Server) WritePacket(packet fed.Packet) (fed.Packet, error) {
-	T.state.C2S(packet)
-	return packet, nil
+	return T.state.C2S(packet)
 }
 
 var _ fed.Middleware = (*Server)(nil)
diff --git a/lib/fed/middlewares/eqp/state.go b/lib/fed/middlewares/eqp/state.go
index a8e7507c655cce83282dc4d8f44eb75b3c989d87..f029e01d0bb26ebc510df7012404b891accad86d 100644
--- a/lib/fed/middlewares/eqp/state.go
+++ b/lib/fed/middlewares/eqp/state.go
@@ -1,53 +1,12 @@
 package eqp
 
 import (
-	"bytes"
-	"hash/maphash"
-
 	"gfx.cafe/gfx/pggat/lib/fed"
 	packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
 	"gfx.cafe/gfx/pggat/lib/util/maps"
 	"gfx.cafe/gfx/pggat/lib/util/ring"
 )
 
-var seed = maphash.MakeSeed()
-
-type PreparedStatement struct {
-	Packet fed.Packet
-	Target string
-	Hash   uint64
-}
-
-func MakePreparedStatement(packet fed.Packet) PreparedStatement {
-	if packet.Type() != packets.TypeParse {
-		panic("unreachable")
-	}
-
-	var res PreparedStatement
-	packet.ReadString(&res.Target)
-	res.Packet = bytes.Clone(packet)
-	res.Hash = maphash.Bytes(seed, packet.Payload())
-
-	return res
-}
-
-type Portal struct {
-	Packet fed.Packet
-	Target string
-}
-
-func MakePortal(packet fed.Packet) Portal {
-	if packet.Type() != packets.TypeBind {
-		panic("unreachable")
-	}
-
-	var res Portal
-	packet.ReadString(&res.Target)
-	res.Packet = bytes.Clone(packet)
-
-	return res
-}
-
 type CloseVariant int
 
 const (
@@ -61,65 +20,76 @@ type Close struct {
 }
 
 type State struct {
-	preparedStatements map[string]PreparedStatement
-	portals            map[string]Portal
+	preparedStatements map[string]*packets.Parse
+	portals            map[string]*packets.Bind
 
-	pendingPreparedStatements ring.Ring[PreparedStatement]
-	pendingPortals            ring.Ring[Portal]
+	pendingPreparedStatements ring.Ring[*packets.Parse]
+	pendingPortals            ring.Ring[*packets.Bind]
 	pendingCloses             ring.Ring[Close]
 }
 
 // C2S is client to server packets
-func (T *State) C2S(packet fed.Packet) {
+func (T *State) C2S(packet fed.Packet) (fed.Packet, error) {
 	switch packet.Type() {
 	case packets.TypeClose:
-		T.Close(packet)
+		return T.Close(packet)
 	case packets.TypeParse:
-		T.Parse(packet)
+		return T.Parse(packet)
 	case packets.TypeBind:
-		T.Bind(packet)
+		return T.Bind(packet)
 	case packets.TypeQuery:
 		T.Query()
+		return packet, nil
+	default:
+		return packet, nil
 	}
 }
 
 // S2C is server to client packets
-func (T *State) S2C(packet fed.Packet) {
+func (T *State) S2C(packet fed.Packet) (fed.Packet, error) {
 	switch packet.Type() {
 	case packets.TypeCloseComplete:
 		T.CloseComplete()
+		return packet, nil
 	case packets.TypeParseComplete:
 		T.ParseComplete()
+		return packet, nil
 	case packets.TypeBindComplete:
 		T.BindComplete()
+		return packet, nil
 	case packets.TypeCommandComplete:
-		T.CommandComplete(packet)
+		return T.CommandComplete(packet)
 	case packets.TypeReadyForQuery:
-		T.ReadyForQuery(packet)
+		return T.ReadyForQuery(packet)
+	default:
+		return packet, nil
 	}
 }
 
 // Close is a pending close. Execute on Close C->S
-func (T *State) Close(packet fed.Packet) {
-	var which byte
-	p := packet.ReadUint8(&which)
-	var target string
-	p.ReadString(&target)
+func (T *State) Close(packet fed.Packet) (fed.Packet, error) {
+	var p packets.Close
+	err := fed.ToConcrete(&p, packet)
+	if err != nil {
+		return nil, err
+	}
 
 	var variant CloseVariant
-	switch which {
+	switch p.Which {
 	case 'S':
 		variant = CloseVariantPreparedStatement
 	case 'P':
 		variant = CloseVariantPortal
 	default:
-		return
+		return nil, packets.ErrInvalidFormat
 	}
 
 	T.pendingCloses.PushBack(Close{
 		Variant: variant,
-		Target:  target,
+		Target:  p.Name,
 	})
+
+	return &p, nil
 }
 
 // CloseComplete notifies that a close was successful. Execute on CloseComplete S->C
@@ -140,9 +110,14 @@ func (T *State) CloseComplete() {
 }
 
 // Parse is a pending prepared statement. Execute on Parse C->S
-func (T *State) Parse(packet fed.Packet) {
-	preparedStatement := MakePreparedStatement(packet)
-	T.pendingPreparedStatements.PushBack(preparedStatement)
+func (T *State) Parse(packet fed.Packet) (fed.Packet, error) {
+	var p packets.Parse
+	err := fed.ToConcrete(&p, packet)
+	if err != nil {
+		return nil, err
+	}
+	T.pendingPreparedStatements.PushBack(&p)
+	return &p, nil
 }
 
 // ParseComplete notifies that a parse was successful. Execute on ParseComplete S->C
@@ -153,15 +128,20 @@ func (T *State) ParseComplete() {
 	}
 
 	if T.preparedStatements == nil {
-		T.preparedStatements = make(map[string]PreparedStatement)
+		T.preparedStatements = make(map[string]*packets.Parse)
 	}
-	T.preparedStatements[preparedStatement.Target] = preparedStatement
+	T.preparedStatements[preparedStatement.Destination] = preparedStatement
 }
 
 // Bind is a pending portal. Execute on Bind C->S
-func (T *State) Bind(packet fed.Packet) {
-	portal := MakePortal(packet)
-	T.pendingPortals.PushBack(portal)
+func (T *State) Bind(packet fed.Packet) (fed.Packet, error) {
+	var p packets.Bind
+	err := fed.ToConcrete(&p, packet)
+	if err != nil {
+		return nil, err
+	}
+	T.pendingPortals.PushBack(&p)
+	return &p, nil
 }
 
 // BindComplete notifies that a bind was successful. Execute on BindComplete S->C
@@ -172,9 +152,9 @@ func (T *State) BindComplete() {
 	}
 
 	if T.portals == nil {
-		T.portals = make(map[string]Portal)
+		T.portals = make(map[string]*packets.Bind)
 	}
-	T.portals[portal.Target] = portal
+	T.portals[portal.Destination] = portal
 }
 
 // Query clobbers the unnamed portal and unnamed prepared statement. Execute on Query C->S
@@ -184,27 +164,33 @@ func (T *State) Query() {
 }
 
 // CommandComplete clobbers everything if DISCARD ALL | DEALLOCATE | CLOSE
-func (T *State) CommandComplete(packet fed.Packet) {
-	var commandComplete packets.CommandComplete
-	if !commandComplete.ReadFromPacket(packet) {
-		return
+func (T *State) CommandComplete(packet fed.Packet) (fed.Packet, error) {
+	var p packets.CommandComplete
+	err := fed.ToConcrete(&p, packet)
+	if err != nil {
+		return nil, err
 	}
 
-	if commandComplete == "DISCARD ALL" {
+	if p == "DISCARD ALL" {
 		maps.Clear(T.preparedStatements)
 		maps.Clear(T.portals)
 		T.pendingPreparedStatements.Clear()
 		T.pendingPortals.Clear()
 		T.pendingCloses.Clear()
 	}
+
+	return &p, nil
 }
 
 // ReadyForQuery clobbers portals if state == 'I' and pending. Execute on ReadyForQuery S->C
-func (T *State) ReadyForQuery(packet fed.Packet) {
-	var state byte
-	packet.ReadUint8(&state)
+func (T *State) ReadyForQuery(packet fed.Packet) (fed.Packet, error) {
+	var p packets.ReadyForQuery
+	err := fed.ToConcrete(&p, packet)
+	if err != nil {
+		return nil, err
+	}
 
-	if state == 'I' {
+	if p == 'I' {
 		// clobber all portals
 		for name := range T.portals {
 			delete(T.portals, name)
@@ -215,4 +201,6 @@ func (T *State) ReadyForQuery(packet fed.Packet) {
 	T.pendingPreparedStatements.Clear()
 	T.pendingPortals.Clear()
 	T.pendingCloses.Clear()
+
+	return &p, nil
 }
diff --git a/lib/fed/middlewares/eqp/sync.go b/lib/fed/middlewares/eqp/sync.go
index 150d8e0d9ef2824a7cbbdc4f9029dc92d29abf7d..05fc56e53b2860c246faf1e5549f87a8ce9715dc 100644
--- a/lib/fed/middlewares/eqp/sync.go
+++ b/lib/fed/middlewares/eqp/sync.go
@@ -1,11 +1,25 @@
 package eqp
 
 import (
+	"slices"
+
 	"gfx.cafe/gfx/pggat/lib/bouncer/backends/v0"
 	"gfx.cafe/gfx/pggat/lib/fed"
 	packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
 )
 
+func preparedStatementsEqual(a, b *packets.Parse) bool {
+	if a.Query != b.Query {
+		return false
+	}
+
+	if !slices.Equal(a.ParameterDataTypes, b.ParameterDataTypes) {
+		return false
+	}
+
+	return true
+}
+
 func Sync(c *Client, server *fed.Conn, s *Server) error {
 	var needsBackendSync bool
 
@@ -16,15 +30,12 @@ func Sync(c *Client, server *fed.Conn, s *Server) error {
 		needsBackendSync = true
 	}
 
-	var packet fed.Packet
-
 	for name := range s.state.portals {
 		p := packets.Close{
-			Which:  'P',
-			Target: name,
+			Which: 'P',
+			Name:  name,
 		}
-		packet = p.IntoPacket(packet)
-		if err := server.WritePacket(packet); err != nil {
+		if err := server.WritePacket(&p); err != nil {
 			return err
 		}
 	}
@@ -32,8 +43,7 @@ func Sync(c *Client, server *fed.Conn, s *Server) error {
 	// close all prepared statements that don't match client
 	for name, preparedStatement := range s.state.preparedStatements {
 		if clientPreparedStatement, ok := c.state.preparedStatements[name]; ok {
-			if preparedStatement.Hash == clientPreparedStatement.Hash {
-				// the same
+			if preparedStatementsEqual(preparedStatement, clientPreparedStatement) {
 				continue
 			}
 
@@ -44,11 +54,10 @@ func Sync(c *Client, server *fed.Conn, s *Server) error {
 		}
 
 		p := packets.Close{
-			Which:  'S',
-			Target: name,
+			Which: 'S',
+			Name:  name,
 		}
-		packet = p.IntoPacket(packet)
-		if err := server.WritePacket(packet); err != nil {
+		if err := server.WritePacket(&p); err != nil {
 			return err
 		}
 
@@ -58,13 +67,12 @@ func Sync(c *Client, server *fed.Conn, s *Server) error {
 	// parse all prepared statements that aren't on server
 	for name, preparedStatement := range c.state.preparedStatements {
 		if serverPreparedStatement, ok := s.state.preparedStatements[name]; ok {
-			if preparedStatement.Hash == serverPreparedStatement.Hash {
-				// the same
+			if preparedStatementsEqual(preparedStatement, serverPreparedStatement) {
 				continue
 			}
 		}
 
-		if err := server.WritePacket(preparedStatement.Packet); err != nil {
+		if err := server.WritePacket(preparedStatement); err != nil {
 			return err
 		}
 
@@ -77,14 +85,14 @@ func Sync(c *Client, server *fed.Conn, s *Server) error {
 	}
 
 	for _, portal := range c.state.portals {
-		if err := server.WritePacket(portal.Packet); err != nil {
+		if err := server.WritePacket(portal); err != nil {
 			return err
 		}
 	}
 
 	if needsBackendSync {
 		var err error
-		err, _, packet = backends.Sync(server, nil, packet)
+		err, _ = backends.Sync(server, nil)
 		return err
 	}
 
diff --git a/lib/fed/middlewares/ps/client.go b/lib/fed/middlewares/ps/client.go
index ecb819f992b19c47dbf313604db87df7e0f7b2d9..a0ca2278d4a028bdbf646b32e06384c1ea17ccb8 100644
--- a/lib/fed/middlewares/ps/client.go
+++ b/lib/fed/middlewares/ps/client.go
@@ -1,8 +1,6 @@
 package ps
 
 import (
-	"errors"
-
 	"gfx.cafe/gfx/pggat/lib/fed"
 	packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
 	"gfx.cafe/gfx/pggat/lib/util/strutil"
@@ -26,21 +24,24 @@ func (T *Client) ReadPacket(packet fed.Packet) (fed.Packet, error) {
 func (T *Client) WritePacket(packet fed.Packet) (fed.Packet, error) {
 	switch packet.Type() {
 	case packets.TypeParameterStatus:
-		var ps packets.ParameterStatus
-		if !ps.ReadFromPacket(packet) {
-			return packet, errors.New("bad packet format i")
+		var p packets.ParameterStatus
+		err := fed.ToConcrete(&p, packet)
+		if err != nil {
+			return nil, err
 		}
-		ikey := strutil.MakeCIString(ps.Key)
-		if T.synced && T.parameters[ikey] == ps.Value {
+		ikey := strutil.MakeCIString(p.Key)
+		if T.synced && T.parameters[ikey] == p.Value {
 			// already set
-			return packet[:0], nil
+			return nil, nil
 		}
 		if T.parameters == nil {
 			T.parameters = make(map[strutil.CIString]string)
 		}
-		T.parameters[ikey] = ps.Value
+		T.parameters[ikey] = p.Value
+		return &p, nil
+	default:
+		return packet, nil
 	}
-	return packet, nil
 }
 
 var _ fed.Middleware = (*Client)(nil)
diff --git a/lib/fed/middlewares/ps/server.go b/lib/fed/middlewares/ps/server.go
index f74f4f0c8c2c15414a34524984b83d313cc53159..dcc8515662a8dcf94d568b55523c44e182237ef5 100644
--- a/lib/fed/middlewares/ps/server.go
+++ b/lib/fed/middlewares/ps/server.go
@@ -1,8 +1,6 @@
 package ps
 
 import (
-	"errors"
-
 	"gfx.cafe/gfx/pggat/lib/fed"
 	packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
 	"gfx.cafe/gfx/pggat/lib/util/strutil"
@@ -21,17 +19,20 @@ func NewServer(parameters map[strutil.CIString]string) *Server {
 func (T *Server) ReadPacket(packet fed.Packet) (fed.Packet, error) {
 	switch packet.Type() {
 	case packets.TypeParameterStatus:
-		var ps packets.ParameterStatus
-		if !ps.ReadFromPacket(packet) {
-			return packet, errors.New("bad packet format j")
+		var p packets.ParameterStatus
+		err := fed.ToConcrete(&p, packet)
+		if err != nil {
+			return nil, err
 		}
-		ikey := strutil.MakeCIString(ps.Key)
+		ikey := strutil.MakeCIString(p.Key)
 		if T.parameters == nil {
 			T.parameters = make(map[strutil.CIString]string)
 		}
-		T.parameters[ikey] = ps.Value
+		T.parameters[ikey] = p.Value
+		return &p, nil
+	default:
+		return packet, nil
 	}
-	return packet, nil
 }
 
 func (T *Server) WritePacket(packet fed.Packet) (fed.Packet, error) {
diff --git a/lib/fed/middlewares/ps/sync.go b/lib/fed/middlewares/ps/sync.go
index be296b9c3421570d64d5d46460fdf623419c5325..8f9f19267e23c7918527a56c781338c06742a2be 100644
--- a/lib/fed/middlewares/ps/sync.go
+++ b/lib/fed/middlewares/ps/sync.go
@@ -12,16 +12,13 @@ func sync(tracking []strutil.CIString, client *fed.Conn, c *Client, server *fed.
 	value, hasValue := c.parameters[name]
 	expected, hasExpected := s.parameters[name]
 
-	var packet fed.Packet
-
 	if value == expected {
 		if !c.synced {
 			ps := packets.ParameterStatus{
 				Key:   name.String(),
 				Value: expected,
 			}
-			packet = ps.IntoPacket(packet)
-			if err := client.WritePacket(packet); err != nil {
+			if err := client.WritePacket(&ps); err != nil {
 				return err
 			}
 		}
@@ -32,7 +29,7 @@ func sync(tracking []strutil.CIString, client *fed.Conn, c *Client, server *fed.
 
 	if hasValue && slices.Contains(tracking, name) {
 		var err error
-		if err, _, packet = backends.SetParameter(server, nil, packet, name, value); err != nil {
+		if err, _ = backends.SetParameter(server, nil, name, value); err != nil {
 			return err
 		}
 		if s.parameters == nil {
@@ -50,8 +47,7 @@ func sync(tracking []strutil.CIString, client *fed.Conn, c *Client, server *fed.
 			Key:   name.String(),
 			Value: expected,
 		}
-		packet = ps.IntoPacket(packet)
-		if err := client.WritePacket(packet); err != nil {
+		if err := client.WritePacket(&ps); err != nil {
 			return err
 		}
 	}
diff --git a/lib/fed/netconn.go b/lib/fed/netconn.go
deleted file mode 100644
index 480edb12b2c66cca2eea13888daac31ca226c404..0000000000000000000000000000000000000000
--- a/lib/fed/netconn.go
+++ /dev/null
@@ -1,144 +0,0 @@
-package fed
-
-import (
-	"bufio"
-	"crypto/tls"
-	"encoding/binary"
-	"errors"
-	"io"
-	"net"
-
-	"gfx.cafe/gfx/pggat/lib/util/slices"
-)
-
-type NetConn struct {
-	conn       net.Conn
-	writer     bufio.Writer
-	reader     bufio.Reader
-	sslEnabled bool
-
-	headerBuf [5]byte
-}
-
-func NewNetConn(conn net.Conn) *NetConn {
-	c := &NetConn{
-		conn: conn,
-	}
-	c.writer.Reset(conn)
-	c.reader.Reset(conn)
-	return c
-}
-
-func (T *NetConn) LocalAddr() net.Addr {
-	return T.conn.LocalAddr()
-}
-
-func (T *NetConn) RemoteAddr() net.Addr {
-	return T.conn.RemoteAddr()
-}
-
-// SSL
-
-var errSSLAlreadyEnabled = errors.New("ssl is already enabled")
-
-func (T *NetConn) SSL() bool {
-	return T.sslEnabled
-}
-
-func (T *NetConn) EnableSSLClient(config *tls.Config) error {
-	if T.sslEnabled {
-		return errSSLAlreadyEnabled
-	}
-	T.sslEnabled = true
-
-	if err := T.writer.Flush(); err != nil {
-		return err
-	}
-	if T.reader.Buffered() > 0 {
-		return errors.New("expected empty read buffer")
-	}
-	sslConn := tls.Client(T.conn, config)
-	T.writer.Reset(sslConn)
-	T.reader.Reset(sslConn)
-	T.conn = sslConn
-	return sslConn.Handshake()
-}
-
-func (T *NetConn) EnableSSLServer(config *tls.Config) error {
-	if T.sslEnabled {
-		return errSSLAlreadyEnabled
-	}
-	T.sslEnabled = true
-
-	if err := T.writer.Flush(); err != nil {
-		return err
-	}
-	if T.reader.Buffered() > 0 {
-		return errors.New("expected empty read buffer")
-	}
-	sslConn := tls.Server(T.conn, config)
-	T.writer.Reset(sslConn)
-	T.reader.Reset(sslConn)
-	T.conn = sslConn
-	return sslConn.Handshake()
-}
-
-func (T *NetConn) ReadByte() (byte, error) {
-	if err := T.writer.Flush(); err != nil {
-		return 0, err
-	}
-	return T.reader.ReadByte()
-}
-
-func (T *NetConn) ReadPacket(typed bool, buffer Packet) (packet Packet, err error) {
-	packet = buffer
-
-	if err = T.writer.Flush(); err != nil {
-		return
-	}
-
-	if typed {
-		_, err = io.ReadFull(&T.reader, T.headerBuf[:])
-		if err != nil {
-			return
-		}
-	} else {
-		_, err = io.ReadFull(&T.reader, T.headerBuf[1:])
-		if err != nil {
-			return
-		}
-	}
-
-	length := binary.BigEndian.Uint32(T.headerBuf[1:])
-
-	packet = slices.Resize(buffer, int(length)+1)
-	copy(packet, T.headerBuf[:])
-
-	_, err = io.ReadFull(&T.reader, packet.Payload())
-	if err != nil {
-		return
-	}
-	return
-}
-
-func (T *NetConn) WriteByte(b byte) error {
-	return T.writer.WriteByte(b)
-}
-
-func (T *NetConn) WritePacket(packet Packet) error {
-	_, err := T.writer.Write(packet.Bytes())
-	return err
-}
-
-func (T *NetConn) Close() error {
-	if err := T.writer.Flush(); err != nil {
-		return err
-	}
-	return T.conn.Close()
-}
-
-var _ ReadWriteCloser = (*NetConn)(nil)
-var _ SSLServer = (*NetConn)(nil)
-var _ SSLClient = (*NetConn)(nil)
-var _ io.ByteReader = (*NetConn)(nil)
-var _ io.ByteWriter = (*NetConn)(nil)
diff --git a/lib/fed/packet.go b/lib/fed/packet.go
index 2a87a27731df8216bf80f37cf462eb33095d1b9d..6da6f2bf682c0ad77fe192ae2fc078275d728519 100644
--- a/lib/fed/packet.go
+++ b/lib/fed/packet.go
@@ -1,247 +1,54 @@
 package fed
 
-import (
-	"encoding/binary"
-	"math"
+type Packet interface {
+	Type() Type
+	Length() int
 
-	"gfx.cafe/gfx/pggat/lib/util/slices"
-)
-
-type Packet []byte
-
-func NewPacket(typ Type, size ...int) Packet {
-	return Packet(nil).Reset(typ, size...)
-}
-
-func (T Packet) Reset(typ Type, size ...int) Packet {
-	packet := T
-	c := 5
-	if len(size) > 0 {
-		c += size[0]
-	}
-
-	if cap(packet) < c {
-		packet = make([]byte, 5, c)
-	} else {
-		packet = slices.Resize(packet, 5)
-	}
-	packet[0] = byte(typ)
-	packet[1] = 0
-	packet[2] = 0
-	packet[3] = 0
-	packet[4] = 0
-	return packet
-}
-
-func (T Packet) Payload() PacketFragment {
-	return PacketFragment(T[5:])
-}
-
-func (T Packet) Bytes() []byte {
-	binary.BigEndian.PutUint32(T[1:], uint32(len(T)-1))
-
-	if T.Type() == 0 {
-		return T[1:]
-	}
-	return T
-}
-
-func (T Packet) Type() Type {
-	return Type(T[0])
-}
-
-func (T Packet) AppendUint8(v uint8) Packet {
-	return append(T, v)
+	WriteTo(encoder *Encoder) error
 }
 
-func (T Packet) AppendUint16(v uint16) Packet {
-	return binary.BigEndian.AppendUint16(T, v)
-}
-
-func (T Packet) AppendUint32(v uint32) Packet {
-	return binary.BigEndian.AppendUint32(T, v)
-}
-
-func (T Packet) AppendUint64(v uint64) Packet {
-	return binary.BigEndian.AppendUint64(T, v)
-}
+type ReadablePacket interface {
+	Packet
 
-func (T Packet) AppendInt8(v int8) Packet {
-	return T.AppendUint8(uint8(v))
+	ReadFrom(decoder *Decoder) error
 }
 
-func (T Packet) AppendInt16(v int16) Packet {
-	return T.AppendUint16(uint16(v))
+type PendingPacket struct {
+	Decoder *Decoder
 }
 
-func (T Packet) AppendInt32(v int32) Packet {
-	return T.AppendUint32(uint32(v))
+func (T PendingPacket) Type() Type {
+	return T.Decoder.Type()
 }
 
-func (T Packet) AppendInt64(v int64) Packet {
-	return T.AppendUint64(uint64(v))
+func (T PendingPacket) Length() int {
+	return T.Decoder.Length()
 }
 
-func (T Packet) AppendFloat32(v float32) Packet {
-	return T.AppendUint32(math.Float32bits(v))
-}
-
-func (T Packet) AppendFloat64(v float64) Packet {
-	return T.AppendUint64(math.Float64bits(v))
-}
-
-func (T Packet) AppendString(v string) Packet {
-	return append(append(T, v...), 0)
-}
-
-func (T Packet) AppendBytes(v []byte) Packet {
-	return append(T, v...)
-}
-
-func (T Packet) ReadUint8(v *uint8) PacketFragment {
-	return T.Payload().ReadUint8(v)
-}
-
-func (T Packet) ReadUint16(v *uint16) PacketFragment {
-	return T.Payload().ReadUint16(v)
-}
-
-func (T Packet) ReadUint32(v *uint32) PacketFragment {
-	return T.Payload().ReadUint32(v)
-}
-
-func (T Packet) ReadUint64(v *uint64) PacketFragment {
-	return T.Payload().ReadUint64(v)
-}
-
-func (T Packet) ReadInt8(v *int8) PacketFragment {
-	return T.Payload().ReadInt8(v)
-}
-
-func (T Packet) ReadInt16(v *int16) PacketFragment {
-	return T.Payload().ReadInt16(v)
-}
-
-func (T Packet) ReadInt32(v *int32) PacketFragment {
-	return T.Payload().ReadInt32(v)
-}
-
-func (T Packet) ReadInt64(v *int64) PacketFragment {
-	return T.Payload().ReadInt64(v)
-}
-
-func (T Packet) ReadFloat32(v *float32) PacketFragment {
-	return T.Payload().ReadFloat32(v)
-}
-
-func (T Packet) ReadFloat64(v *float64) PacketFragment {
-	return T.Payload().ReadFloat64(v)
-}
-
-func (T Packet) ReadString(v *string) PacketFragment {
-	return T.Payload().ReadString(v)
-}
-
-func (T Packet) ReadBytes(v []byte) PacketFragment {
-	return T.Payload().ReadBytes(v)
-}
-
-type PacketFragment []byte
-
-func (T PacketFragment) ReadUint8(v *uint8) PacketFragment {
-	if len(T) < 1 {
-		return T
-	}
-
-	*v = T[0]
-	return T[1:]
-}
-
-func (T PacketFragment) ReadUint16(v *uint16) PacketFragment {
-	if len(T) < 2 {
-		return T
-	}
-
-	*v = binary.BigEndian.Uint16(T)
-	return T[2:]
-}
-
-func (T PacketFragment) ReadUint32(v *uint32) PacketFragment {
-	if len(T) < 4 {
-		return T
-	}
-
-	*v = binary.BigEndian.Uint32(T)
-	return T[4:]
-}
-
-func (T PacketFragment) ReadUint64(v *uint64) PacketFragment {
-	if len(T) < 8 {
-		return T
-	}
-
-	*v = binary.BigEndian.Uint64(T)
-	return T[8:]
-}
-
-func (T PacketFragment) ReadInt8(v *int8) PacketFragment {
-	var vv uint8
-	n := T.ReadUint8(&vv)
-	*v = int8(vv)
-	return n
-}
-
-func (T PacketFragment) ReadInt16(v *int16) PacketFragment {
-	var vv uint16
-	n := T.ReadUint16(&vv)
-	*v = int16(vv)
-	return n
-}
-
-func (T PacketFragment) ReadInt32(v *int32) PacketFragment {
-	var vv uint32
-	n := T.ReadUint32(&vv)
-	*v = int32(vv)
-	return n
-}
-
-func (T PacketFragment) ReadInt64(v *int64) PacketFragment {
-	var vv uint64
-	n := T.ReadUint64(&vv)
-	*v = int64(vv)
-	return n
-}
-
-func (T PacketFragment) ReadFloat32(v *float32) PacketFragment {
-	var vv uint32
-	n := T.ReadUint32(&vv)
-	*v = math.Float32frombits(vv)
-	return n
-}
-
-func (T PacketFragment) ReadFloat64(v *float64) PacketFragment {
-	var vv uint64
-	n := T.ReadUint64(&vv)
-	*v = math.Float64frombits(vv)
-	return n
-}
-
-func (T PacketFragment) ReadString(v *string) PacketFragment {
-	for i, b := range T {
-		if b != '\x00' {
-			continue
+func (T PendingPacket) WriteTo(encoder *Encoder) error {
+	count := T.Decoder.Length() - T.Decoder.Position()
+	for T.Decoder.Position() < T.Decoder.Length() {
+		if _, err := encoder.Writer.ReadFrom(T.Decoder); err != nil {
+			return err
 		}
-		*v = string(T[:i])
-		return T[i+1:]
 	}
-
-	return T
+	encoder.pos += count
+	return nil
 }
 
-func (T PacketFragment) ReadBytes(v []byte) PacketFragment {
-	if len(T) < len(v) {
-		return T
+var _ Packet = PendingPacket{}
+
+func ToConcrete[T any, PT interface {
+	ReadFrom(decoder *Decoder) error
+	*T
+}](value PT, packet Packet) error {
+	switch p := packet.(type) {
+	case PT:
+		*value = *p
+		return nil
+	case PendingPacket:
+		return value.ReadFrom(p.Decoder)
+	default:
+		panic("incompatible packet types")
 	}
-	copy(v, T)
-	return T[len(v):]
 }
diff --git a/lib/fed/packets/v3.0/authenticationcleartext.go b/lib/fed/packets/v3.0/authenticationcleartext.go
deleted file mode 100644
index 36cd0413b43e7e914207d645ddf75183e6ad397c..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/authenticationcleartext.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package packets
-
-import "gfx.cafe/gfx/pggat/lib/fed"
-
-type AuthenticationCleartext struct{}
-
-func (T *AuthenticationCleartext) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeAuthentication {
-		return false
-	}
-	var method int32
-	packet.ReadInt32(&method)
-	if method != 3 {
-		return false
-	}
-	return true
-}
-
-func (T *AuthenticationCleartext) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = packet.Reset(TypeAuthentication, 4)
-	packet = packet.AppendUint32(3)
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/authenticationmd5.go b/lib/fed/packets/v3.0/authenticationmd5.go
deleted file mode 100644
index 9a88902b653d111992e7ef1a750e3584129f1b44..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/authenticationmd5.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package packets
-
-import "gfx.cafe/gfx/pggat/lib/fed"
-
-type AuthenticationMD5 struct {
-	Salt [4]byte
-}
-
-func (T *AuthenticationMD5) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeAuthentication {
-		return false
-	}
-	var method int32
-	p := packet.ReadInt32(&method)
-	if method != 5 {
-		return false
-	}
-	p = p.ReadBytes(T.Salt[:])
-	return true
-}
-
-func (T *AuthenticationMD5) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = packet.Reset(TypeAuthentication, 8)
-	packet = packet.AppendUint32(5)
-	packet = packet.AppendBytes(T.Salt[:])
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/authenticationok.go b/lib/fed/packets/v3.0/authenticationok.go
deleted file mode 100644
index 16dee6a21e8c8e411c8d48b2def9920e4ce2c705..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/authenticationok.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package packets
-
-import "gfx.cafe/gfx/pggat/lib/fed"
-
-type AuthenticationOk struct{}
-
-func (T *AuthenticationOk) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeAuthentication {
-		return false
-	}
-	var method int32
-	packet.ReadInt32(&method)
-	if method != 0 {
-		return false
-	}
-	return true
-}
-
-func (T *AuthenticationOk) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = packet.Reset(TypeAuthentication, 4)
-	packet = packet.AppendUint32(0)
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/authenticationresponse.go b/lib/fed/packets/v3.0/authenticationresponse.go
deleted file mode 100644
index 9c1be35fad8e41e1f0b21b6cdb8fcf2b511a3864..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/authenticationresponse.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package packets
-
-import (
-	"gfx.cafe/gfx/pggat/lib/fed"
-	"gfx.cafe/gfx/pggat/lib/util/slices"
-)
-
-type AuthenticationResponse []byte
-
-func (T *AuthenticationResponse) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeAuthenticationResponse {
-		return false
-	}
-	*T = slices.Resize(*T, len(packet.Payload()))
-	packet.ReadBytes(*T)
-	return true
-}
-
-func (T *AuthenticationResponse) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = fed.NewPacket(TypeAuthenticationResponse, len(*T))
-	packet = packet.AppendBytes(*T)
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/authenticationsasl.go b/lib/fed/packets/v3.0/authenticationsasl.go
deleted file mode 100644
index 0382b65ad1da6cdf90001b11fa492ea1255e6833..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/authenticationsasl.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package packets
-
-import "gfx.cafe/gfx/pggat/lib/fed"
-
-type AuthenticationSASL struct {
-	Mechanisms []string
-}
-
-func (T *AuthenticationSASL) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeAuthentication {
-		return false
-	}
-	var method int32
-	p := packet.ReadInt32(&method)
-	if method != 10 {
-		return false
-	}
-	T.Mechanisms = T.Mechanisms[:0]
-	for {
-		var mechanism string
-		p = p.ReadString(&mechanism)
-		if mechanism == "" {
-			break
-		}
-		T.Mechanisms = append(T.Mechanisms, mechanism)
-	}
-	return true
-}
-
-func (T *AuthenticationSASL) IntoPacket(packet fed.Packet) fed.Packet {
-	size := 5
-	for _, mechanism := range T.Mechanisms {
-		size += len(mechanism) + 1
-	}
-
-	packet = packet.Reset(TypeAuthentication, size)
-
-	packet = packet.AppendInt32(10)
-	for _, mechanism := range T.Mechanisms {
-		packet = packet.AppendString(mechanism)
-	}
-	packet = packet.AppendUint8(0)
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/authenticationsaslcontinue.go b/lib/fed/packets/v3.0/authenticationsaslcontinue.go
deleted file mode 100644
index 135d87ab8dc020e453b879dc689d520b436aeca8..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/authenticationsaslcontinue.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package packets
-
-import (
-	"gfx.cafe/gfx/pggat/lib/fed"
-	"gfx.cafe/gfx/pggat/lib/util/slices"
-)
-
-type AuthenticationSASLContinue []byte
-
-func (T *AuthenticationSASLContinue) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeAuthentication {
-		return false
-	}
-	var method int32
-	p := packet.ReadInt32(&method)
-	if method != 11 {
-		return false
-	}
-	*T = slices.Resize(*T, len(p))
-	p.ReadBytes(*T)
-	return true
-}
-
-func (T *AuthenticationSASLContinue) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = packet.Reset(TypeAuthentication, 4+len(*T))
-	packet = packet.AppendUint32(11)
-	packet = packet.AppendBytes(*T)
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/authenticationsaslfinal.go b/lib/fed/packets/v3.0/authenticationsaslfinal.go
deleted file mode 100644
index b5a191cafe8cfd3f9a39dcc763fecfea084d8d5f..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/authenticationsaslfinal.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package packets
-
-import (
-	"gfx.cafe/gfx/pggat/lib/fed"
-	"gfx.cafe/gfx/pggat/lib/util/slices"
-)
-
-type AuthenticationSASLFinal []byte
-
-func (T *AuthenticationSASLFinal) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeAuthentication {
-		return false
-	}
-	var method int32
-	p := packet.ReadInt32(&method)
-	if method != 12 {
-		return false
-	}
-	*T = slices.Resize(*T, len(p))
-	p.ReadBytes(*T)
-	return true
-}
-
-func (T *AuthenticationSASLFinal) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = packet.Reset(TypeAuthentication, 4+len(*T))
-	packet = packet.AppendUint32(12)
-	packet = packet.AppendBytes(*T)
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/backendkeydata.go b/lib/fed/packets/v3.0/backendkeydata.go
deleted file mode 100644
index e144b3ad19e42c814dc661ad3542d544ac875c86..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/backendkeydata.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package packets
-
-import "gfx.cafe/gfx/pggat/lib/fed"
-
-type BackendKeyData struct {
-	CancellationKey [8]byte
-}
-
-func (T *BackendKeyData) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeBackendKeyData {
-		return false
-	}
-	packet.ReadBytes(T.CancellationKey[:])
-	return true
-}
-
-func (T *BackendKeyData) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = fed.NewPacket(TypeBackendKeyData, 8)
-	packet = packet.AppendBytes(T.CancellationKey[:])
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/bind.go b/lib/fed/packets/v3.0/bind.go
deleted file mode 100644
index de1755e519369e5e57c2d3936007387be134f8df..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/bind.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package packets
-
-import (
-	"gfx.cafe/gfx/pggat/lib/fed"
-	"gfx.cafe/gfx/pggat/lib/util/slices"
-)
-
-type Bind struct {
-	Destination          string
-	Source               string
-	ParameterFormatCodes []int16
-	ParameterValues      [][]byte
-	ResultFormatCodes    []int16
-}
-
-func (T *Bind) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeBind {
-		return false
-	}
-	p := packet.ReadString(&T.Destination)
-	p = p.ReadString(&T.Source)
-
-	var parameterFormatCodesLength uint16
-	p = p.ReadUint16(&parameterFormatCodesLength)
-	T.ParameterFormatCodes = slices.Resize(T.ParameterFormatCodes, int(parameterFormatCodesLength))
-	for i := 0; i < int(parameterFormatCodesLength); i++ {
-		p = p.ReadInt16(&T.ParameterFormatCodes[i])
-	}
-
-	var parameterValuesLength uint16
-	p = p.ReadUint16(&parameterValuesLength)
-	T.ParameterValues = slices.Resize(T.ParameterValues, int(parameterValuesLength))
-	for i := 0; i < int(parameterValuesLength); i++ {
-		var parameterValueLength int32
-		p = p.ReadInt32(&parameterValueLength)
-		if parameterValueLength == -1 {
-			T.ParameterValues[i] = nil
-			continue
-		}
-		T.ParameterValues[i] = slices.Resize(T.ParameterValues[i], int(parameterValueLength))
-		p = p.ReadBytes(T.ParameterValues[i])
-	}
-
-	var resultFormatCodesLength uint16
-	p = p.ReadUint16(&resultFormatCodesLength)
-	T.ResultFormatCodes = slices.Resize(T.ResultFormatCodes, int(resultFormatCodesLength))
-	for i := 0; i < int(resultFormatCodesLength); i++ {
-		p = p.ReadInt16(&T.ResultFormatCodes[i])
-	}
-
-	return true
-}
-
-func (T *Bind) IntoPacket(packet fed.Packet) fed.Packet {
-	size := 0
-	size += len(T.Destination) + 1
-	size += len(T.Source) + 1
-	size += 2
-	size += len(T.ParameterFormatCodes) * 2
-	size += 2
-	for _, v := range T.ParameterValues {
-		size += 4 + len(v)
-	}
-	size += 2
-	size += len(T.ResultFormatCodes) * 2
-
-	packet = packet.Reset(TypeBind, size)
-	packet = packet.AppendString(T.Destination)
-	packet = packet.AppendString(T.Source)
-	packet = packet.AppendUint16(uint16(len(T.ParameterFormatCodes)))
-	for _, v := range T.ParameterFormatCodes {
-		packet = packet.AppendInt16(v)
-	}
-	packet = packet.AppendUint16(uint16(len(T.ParameterValues)))
-	for _, v := range T.ParameterValues {
-		if v == nil {
-			packet = packet.AppendInt32(-1)
-			continue
-		}
-		packet = packet.AppendInt32(int32(len(v)))
-		packet = packet.AppendBytes(v)
-	}
-	packet = packet.AppendUint16(uint16(len(T.ResultFormatCodes)))
-	for _, v := range T.ResultFormatCodes {
-		packet = packet.AppendInt16(v)
-	}
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/close.go b/lib/fed/packets/v3.0/close.go
deleted file mode 100644
index 89221f91217347809832b29ce2760b0fc5a0711c..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/close.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package packets
-
-import "gfx.cafe/gfx/pggat/lib/fed"
-
-type Close struct {
-	Which  byte
-	Target string
-}
-
-func (T *Close) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeClose {
-		return false
-	}
-	p := packet.ReadUint8(&T.Which)
-	p = p.ReadString(&T.Target)
-	return true
-}
-
-func (T *Close) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = packet.Reset(TypeClose, 2+len(T.Target))
-	packet = packet.AppendUint8(T.Which)
-	packet = packet.AppendString(T.Target)
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/commandcomplete.go b/lib/fed/packets/v3.0/commandcomplete.go
deleted file mode 100644
index 647804d4ff54d755e6bee2fef6fc0ba65856b15a..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/commandcomplete.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package packets
-
-import "gfx.cafe/gfx/pggat/lib/fed"
-
-type CommandComplete string
-
-func (T *CommandComplete) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeCommandComplete {
-		return false
-	}
-	packet.ReadString((*string)(T))
-	return true
-}
-
-func (T *CommandComplete) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = packet.Reset(TypeCommandComplete, len(*T)+1)
-	packet = packet.AppendString(string(*T))
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/copydata.go b/lib/fed/packets/v3.0/copydata.go
deleted file mode 100644
index 3043745bcbe0c5fdece8514e44dc242d5e8dae8e..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/copydata.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package packets
-
-import (
-	"gfx.cafe/gfx/pggat/lib/fed"
-	"gfx.cafe/gfx/pggat/lib/util/slices"
-)
-
-type CopyData []byte
-
-func (T *CopyData) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeCopyData {
-		return false
-	}
-
-	*T = slices.Resize(*T, len(packet.Payload()))
-	packet.ReadBytes(*T)
-	return true
-}
-
-func (T *CopyData) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = fed.NewPacket(TypeCopyData, len(*T))
-	packet = packet.AppendBytes(*T)
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/copyfail.go b/lib/fed/packets/v3.0/copyfail.go
deleted file mode 100644
index 6f42d09721b867f64281b0158fc25d7543048494..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/copyfail.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package packets
-
-import "gfx.cafe/gfx/pggat/lib/fed"
-
-type CopyFail struct {
-	Reason string
-}
-
-func (T *CopyFail) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeCopyFail {
-		return false
-	}
-	packet.ReadString(&T.Reason)
-	return true
-}
-
-func (T *CopyFail) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = packet.Reset(TypeCopyFail, len(T.Reason)+1)
-	packet = packet.AppendString(T.Reason)
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/datarow.go b/lib/fed/packets/v3.0/datarow.go
deleted file mode 100644
index 31e2662266010779626a68835db14a6ae93dc43a..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/datarow.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package packets
-
-import (
-	"gfx.cafe/gfx/pggat/lib/fed"
-	"gfx.cafe/gfx/pggat/lib/util/slices"
-)
-
-type DataRow struct {
-	Columns [][]byte
-}
-
-func (T *DataRow) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeDataRow {
-		return false
-	}
-
-	var columnCount uint16
-	p := packet.ReadUint16(&columnCount)
-	T.Columns = slices.Resize(T.Columns, int(columnCount))
-	for i := 0; i < int(columnCount); i++ {
-		var valueLength int32
-		p = p.ReadInt32(&valueLength)
-		if valueLength == -1 {
-			continue
-		}
-		T.Columns[i] = slices.Resize(T.Columns[i], int(valueLength))
-		p = p.ReadBytes(T.Columns[i])
-	}
-
-	return true
-}
-
-func (T *DataRow) IntoPacket(packet fed.Packet) fed.Packet {
-	size := 2
-	for _, v := range T.Columns {
-		size += len(v) + 4
-	}
-
-	packet = packet.Reset(TypeDataRow, size)
-	packet = packet.AppendUint16(uint16(len(T.Columns)))
-	for _, v := range T.Columns {
-		if v == nil {
-			packet = packet.AppendInt32(-1)
-			continue
-		}
-
-		packet = packet.AppendInt32(int32(len(v)))
-		packet = packet.AppendBytes(v)
-	}
-
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/describe.go b/lib/fed/packets/v3.0/describe.go
deleted file mode 100644
index e6510ffe49a5db52e503cfc7d6b781e6844f090c..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/describe.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package packets
-
-import "gfx.cafe/gfx/pggat/lib/fed"
-
-type Describe struct {
-	Which  byte
-	Target string
-}
-
-func (T *Describe) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeDescribe {
-		return false
-	}
-	p := packet.ReadUint8(&T.Which)
-	p = p.ReadString(&T.Target)
-	return true
-}
-
-func (T *Describe) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = packet.Reset(TypeDescribe, len(T.Target)+2)
-	packet = packet.AppendUint8(T.Which)
-	packet = packet.AppendString(T.Target)
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/errorresponse.go b/lib/fed/packets/v3.0/errorresponse.go
deleted file mode 100644
index a97f46058cac2f83ab2d77d4aa5f475937a3f58e..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/errorresponse.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package packets
-
-import (
-	"gfx.cafe/gfx/pggat/lib/fed"
-	"gfx.cafe/gfx/pggat/lib/perror"
-)
-
-type ErrorResponse struct {
-	Error perror.Error
-}
-
-func (T *ErrorResponse) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeErrorResponse {
-		return false
-	}
-
-	var severity perror.Severity
-	var code perror.Code
-	var message string
-	var extra []perror.ExtraField
-
-	p := packet.Payload()
-
-	for {
-		var typ uint8
-		p = p.ReadUint8(&typ)
-
-		if typ == 0 {
-			break
-		}
-
-		var value string
-		p = p.ReadString(&value)
-
-		switch typ {
-		case 'S':
-			severity = perror.Severity(value)
-		case 'C':
-			code = perror.Code(value)
-		case 'M':
-			message = value
-		default:
-			extra = append(extra, perror.ExtraField{
-				Type:  perror.Extra(typ),
-				Value: value,
-			})
-		}
-	}
-
-	T.Error = perror.New(
-		severity,
-		code,
-		message,
-		extra...,
-	)
-	return true
-}
-
-func (T *ErrorResponse) IntoPacket(packet fed.Packet) fed.Packet {
-	size := 1
-	size += len(T.Error.Severity()) + 2
-	size += len(T.Error.Code()) + 2
-	size += len(T.Error.Message()) + 2
-	for _, field := range T.Error.Extra() {
-		size += len(field.Value) + 2
-	}
-
-	packet = packet.Reset(TypeErrorResponse, size)
-
-	packet = packet.AppendUint8('S')
-	packet = packet.AppendString(string(T.Error.Severity()))
-
-	packet = packet.AppendUint8('C')
-	packet = packet.AppendString(string(T.Error.Code()))
-
-	packet = packet.AppendUint8('M')
-	packet = packet.AppendString(T.Error.Message())
-
-	for _, field := range T.Error.Extra() {
-		packet = packet.AppendUint8(uint8(field.Type))
-		packet = packet.AppendString(field.Value)
-	}
-
-	packet = packet.AppendUint8(0)
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/errors.go b/lib/fed/packets/v3.0/errors.go
deleted file mode 100644
index b7bc928bb4bc9565be27eb54f1f8e18dc8ba4d9a..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/errors.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package packets
-
-import "gfx.cafe/gfx/pggat/lib/perror"
-
-var (
-	ErrBadFormat = perror.New(
-		perror.FATAL,
-		perror.ProtocolViolation,
-		"Bad packet format",
-	)
-	ErrUnexpectedPacket = perror.New(
-		perror.FATAL,
-		perror.ProtocolViolation,
-		"unexpected packet",
-	)
-)
diff --git a/lib/fed/packets/v3.0/execute.go b/lib/fed/packets/v3.0/execute.go
deleted file mode 100644
index 9d8c172d2d657dc6e0dae70037c0b3d59b762358..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/execute.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package packets
-
-import "gfx.cafe/gfx/pggat/lib/fed"
-
-type Execute struct {
-	Target  string
-	MaxRows int32
-}
-
-func (T *Execute) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeExecute {
-		return false
-	}
-	p := packet.ReadString(&T.Target)
-	p = p.ReadInt32(&T.MaxRows)
-	return true
-}
-
-func (T *Execute) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = packet.Reset(TypeExecute, len(T.Target)+5)
-	packet = packet.AppendString(T.Target)
-	packet = packet.AppendInt32(T.MaxRows)
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/negotiateprotocolversion.go b/lib/fed/packets/v3.0/negotiateprotocolversion.go
deleted file mode 100644
index aad5dfadb33ce7cfd82a18f8d0f6e1d39fa06894..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/negotiateprotocolversion.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package packets
-
-import (
-	"gfx.cafe/gfx/pggat/lib/fed"
-	"gfx.cafe/gfx/pggat/lib/util/slices"
-)
-
-type NegotiateProtocolVersion struct {
-	MinorProtocolVersion int32
-	UnrecognizedOptions  []string
-}
-
-func (T *NegotiateProtocolVersion) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeNegotiateProtocolVersion {
-		return false
-	}
-	p := packet.ReadInt32(&T.MinorProtocolVersion)
-
-	var numUnrecognizedOptions int32
-	p = p.ReadInt32(&numUnrecognizedOptions)
-
-	T.UnrecognizedOptions = slices.Resize(T.UnrecognizedOptions, int(numUnrecognizedOptions))
-	for i := 0; i < int(numUnrecognizedOptions); i++ {
-		p = p.ReadString(&T.UnrecognizedOptions[i])
-	}
-
-	return true
-}
-
-func (T *NegotiateProtocolVersion) IntoPacket(packet fed.Packet) fed.Packet {
-	size := 8
-	for _, v := range T.UnrecognizedOptions {
-		size += len(v) + 1
-	}
-
-	packet = packet.Reset(TypeNegotiateProtocolVersion, size)
-	packet = packet.AppendInt32(T.MinorProtocolVersion)
-	packet = packet.AppendInt32(int32(len(T.UnrecognizedOptions)))
-	for _, v := range T.UnrecognizedOptions {
-		packet = packet.AppendString(v)
-	}
-
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/packets.go b/lib/fed/packets/v3.0/packets.go
new file mode 100644
index 0000000000000000000000000000000000000000..e8244ab5285c73c3ead171e19f6b06320effbe63
--- /dev/null
+++ b/lib/fed/packets/v3.0/packets.go
@@ -0,0 +1,3242 @@
+package packets
+
+// automatically generated. do not edit
+
+import (
+	"gfx.cafe/gfx/pggat/lib/fed"
+	"gfx.cafe/gfx/pggat/lib/util/slices"
+
+	"errors"
+)
+
+var (
+	ErrUnexpectedPacket = errors.New("unexpected packet")
+	ErrInvalidFormat    = errors.New("invalid packet format")
+)
+
+const (
+	TypeAuthentication           = 'R'
+	TypeBackendKeyData           = 'K'
+	TypeBind                     = 'B'
+	TypeBindComplete             = '2'
+	TypeClose                    = 'C'
+	TypeCloseComplete            = '3'
+	TypeCommandComplete          = 'C'
+	TypeCopyBothResponse         = 'W'
+	TypeCopyData                 = 'd'
+	TypeCopyDone                 = 'c'
+	TypeCopyFail                 = 'f'
+	TypeCopyInResponse           = 'G'
+	TypeCopyOutResponse          = 'H'
+	TypeDataRow                  = 'D'
+	TypeDescribe                 = 'D'
+	TypeEmptyQueryResponse       = 'I'
+	TypeErrorResponse            = 'E'
+	TypeExecute                  = 'E'
+	TypeFlush                    = 'H'
+	TypeFunctionCall             = 'F'
+	TypeFunctionCallResponse     = 'V'
+	TypeGSSResponse              = 'p'
+	TypeNegotiateProtocolVersion = 'v'
+	TypeNoData                   = 'n'
+	TypeNoticeResponse           = 'N'
+	TypeNotificationResponse     = 'A'
+	TypeParameterDescription     = 't'
+	TypeParameterStatus          = 'S'
+	TypeParse                    = 'P'
+	TypeParseComplete            = '1'
+	TypePasswordMessage          = 'p'
+	TypePortalSuspended          = 's'
+	TypeQuery                    = 'Q'
+	TypeReadyForQuery            = 'Z'
+	TypeRowDescription           = 'T'
+	TypeSASLInitialResponse      = 'p'
+	TypeSASLResponse             = 'p'
+	TypeSync                     = 'S'
+	TypeTerminate                = 'X'
+)
+
+type AuthenticationPayloadCleartextPassword struct{}
+
+func (*AuthenticationPayloadCleartextPassword) AuthenticationPayloadMode() int32 {
+	return 3
+}
+
+func (T *AuthenticationPayloadCleartextPassword) Length() (length int) {
+
+	return
+}
+
+func (T *AuthenticationPayloadCleartextPassword) ReadFrom(decoder *fed.Decoder) (err error) {
+
+	return
+}
+
+func (T *AuthenticationPayloadCleartextPassword) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+type AuthenticationPayloadGSS struct{}
+
+func (*AuthenticationPayloadGSS) AuthenticationPayloadMode() int32 {
+	return 7
+}
+
+func (T *AuthenticationPayloadGSS) Length() (length int) {
+
+	return
+}
+
+func (T *AuthenticationPayloadGSS) ReadFrom(decoder *fed.Decoder) (err error) {
+
+	return
+}
+
+func (T *AuthenticationPayloadGSS) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+type AuthenticationPayloadGSSContinue []uint8
+
+func (*AuthenticationPayloadGSSContinue) AuthenticationPayloadMode() int32 {
+	return 8
+}
+
+func (T *AuthenticationPayloadGSSContinue) Length() (length int) {
+	for _, temp1 := range *T {
+		_ = temp1
+
+		length += 1
+
+	}
+
+	return
+}
+
+func (T *AuthenticationPayloadGSSContinue) ReadFrom(decoder *fed.Decoder) (err error) {
+	(*T) = (*T)[:0]
+
+	for {
+		if decoder.Position() >= decoder.Length() {
+			break
+		}
+
+		(*T) = slices.Resize((*T), len((*T))+1)
+
+		*(*uint8)(&((*T)[len((*T))-1])), err = decoder.Uint8()
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+func (T *AuthenticationPayloadGSSContinue) WriteTo(encoder *fed.Encoder) (err error) {
+	for _, temp2 := range *T {
+		err = encoder.Uint8(uint8(temp2))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+type AuthenticationPayloadKerberosV5 struct{}
+
+func (*AuthenticationPayloadKerberosV5) AuthenticationPayloadMode() int32 {
+	return 2
+}
+
+func (T *AuthenticationPayloadKerberosV5) Length() (length int) {
+
+	return
+}
+
+func (T *AuthenticationPayloadKerberosV5) ReadFrom(decoder *fed.Decoder) (err error) {
+
+	return
+}
+
+func (T *AuthenticationPayloadKerberosV5) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+type AuthenticationPayloadMD5Password [4]uint8
+
+func (*AuthenticationPayloadMD5Password) AuthenticationPayloadMode() int32 {
+	return 5
+}
+
+func (T *AuthenticationPayloadMD5Password) Length() (length int) {
+	for _, temp3 := range *T {
+		_ = temp3
+
+		length += 1
+
+	}
+
+	return
+}
+
+func (T *AuthenticationPayloadMD5Password) ReadFrom(decoder *fed.Decoder) (err error) {
+	for temp4 := 0; temp4 < 4; temp4++ {
+		*(*uint8)(&((*T)[temp4])), err = decoder.Uint8()
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+func (T *AuthenticationPayloadMD5Password) WriteTo(encoder *fed.Encoder) (err error) {
+	for _, temp5 := range *T {
+		err = encoder.Uint8(uint8(temp5))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+type AuthenticationPayloadOk struct{}
+
+func (*AuthenticationPayloadOk) AuthenticationPayloadMode() int32 {
+	return 0
+}
+
+func (T *AuthenticationPayloadOk) Length() (length int) {
+
+	return
+}
+
+func (T *AuthenticationPayloadOk) ReadFrom(decoder *fed.Decoder) (err error) {
+
+	return
+}
+
+func (T *AuthenticationPayloadOk) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+type AuthenticationPayloadSASLMethod struct {
+	Method string
+}
+
+type AuthenticationPayloadSASL []AuthenticationPayloadSASLMethod
+
+func (*AuthenticationPayloadSASL) AuthenticationPayloadMode() int32 {
+	return 10
+}
+
+func (T *AuthenticationPayloadSASL) Length() (length int) {
+	for _, temp6 := range *T {
+		_ = temp6
+
+		length += len(temp6.Method) + 1
+
+	}
+
+	var temp7 string
+	_ = temp7
+
+	length += len(temp7) + 1
+
+	return
+}
+
+func (T *AuthenticationPayloadSASL) ReadFrom(decoder *fed.Decoder) (err error) {
+	(*T) = (*T)[:0]
+
+	for {
+		(*T) = slices.Resize((*T), len((*T))+1)
+
+		*(*string)(&((*T)[len((*T))-1].Method)), err = decoder.String()
+		if err != nil {
+			return
+		}
+		if (*T)[len((*T))-1].Method == *new(string) {
+			(*T) = (*T)[:len((*T))-1]
+			break
+		}
+	}
+
+	return
+}
+
+func (T *AuthenticationPayloadSASL) WriteTo(encoder *fed.Encoder) (err error) {
+	for _, temp8 := range *T {
+		err = encoder.String(string(temp8.Method))
+		if err != nil {
+			return
+		}
+
+	}
+
+	var temp9 string
+
+	err = encoder.String(string(temp9))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+type AuthenticationPayloadSASLContinue []uint8
+
+func (*AuthenticationPayloadSASLContinue) AuthenticationPayloadMode() int32 {
+	return 11
+}
+
+func (T *AuthenticationPayloadSASLContinue) Length() (length int) {
+	for _, temp10 := range *T {
+		_ = temp10
+
+		length += 1
+
+	}
+
+	return
+}
+
+func (T *AuthenticationPayloadSASLContinue) ReadFrom(decoder *fed.Decoder) (err error) {
+	(*T) = (*T)[:0]
+
+	for {
+		if decoder.Position() >= decoder.Length() {
+			break
+		}
+
+		(*T) = slices.Resize((*T), len((*T))+1)
+
+		*(*uint8)(&((*T)[len((*T))-1])), err = decoder.Uint8()
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+func (T *AuthenticationPayloadSASLContinue) WriteTo(encoder *fed.Encoder) (err error) {
+	for _, temp11 := range *T {
+		err = encoder.Uint8(uint8(temp11))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+type AuthenticationPayloadSASLFinal []uint8
+
+func (*AuthenticationPayloadSASLFinal) AuthenticationPayloadMode() int32 {
+	return 12
+}
+
+func (T *AuthenticationPayloadSASLFinal) Length() (length int) {
+	for _, temp12 := range *T {
+		_ = temp12
+
+		length += 1
+
+	}
+
+	return
+}
+
+func (T *AuthenticationPayloadSASLFinal) ReadFrom(decoder *fed.Decoder) (err error) {
+	(*T) = (*T)[:0]
+
+	for {
+		if decoder.Position() >= decoder.Length() {
+			break
+		}
+
+		(*T) = slices.Resize((*T), len((*T))+1)
+
+		*(*uint8)(&((*T)[len((*T))-1])), err = decoder.Uint8()
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+func (T *AuthenticationPayloadSASLFinal) WriteTo(encoder *fed.Encoder) (err error) {
+	for _, temp13 := range *T {
+		err = encoder.Uint8(uint8(temp13))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+type AuthenticationPayloadSSPI struct{}
+
+func (*AuthenticationPayloadSSPI) AuthenticationPayloadMode() int32 {
+	return 9
+}
+
+func (T *AuthenticationPayloadSSPI) Length() (length int) {
+
+	return
+}
+
+func (T *AuthenticationPayloadSSPI) ReadFrom(decoder *fed.Decoder) (err error) {
+
+	return
+}
+
+func (T *AuthenticationPayloadSSPI) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+type AuthenticationPayloadMode interface {
+	AuthenticationPayloadMode() int32
+
+	Length() int
+	ReadFrom(decoder *fed.Decoder) error
+	WriteTo(encoder *fed.Encoder) error
+}
+type AuthenticationPayload struct {
+	Mode AuthenticationPayloadMode
+}
+
+type Authentication AuthenticationPayload
+
+func (T *Authentication) Type() fed.Type {
+	return TypeAuthentication
+}
+
+func (T *Authentication) Length() (length int) {
+	length += 4
+
+	length += (*T).Mode.Length()
+
+	return
+}
+
+func (T *Authentication) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	var temp14 int32
+
+	*(*int32)(&(temp14)), err = decoder.Int32()
+	if err != nil {
+		return
+	}
+
+	switch temp14 {
+	case 3:
+		(*T).Mode = new(AuthenticationPayloadCleartextPassword)
+	case 7:
+		(*T).Mode = new(AuthenticationPayloadGSS)
+	case 8:
+		(*T).Mode = new(AuthenticationPayloadGSSContinue)
+	case 2:
+		(*T).Mode = new(AuthenticationPayloadKerberosV5)
+	case 5:
+		(*T).Mode = new(AuthenticationPayloadMD5Password)
+	case 0:
+		(*T).Mode = new(AuthenticationPayloadOk)
+	case 10:
+		(*T).Mode = new(AuthenticationPayloadSASL)
+	case 11:
+		(*T).Mode = new(AuthenticationPayloadSASLContinue)
+	case 12:
+		(*T).Mode = new(AuthenticationPayloadSASLFinal)
+	case 9:
+		(*T).Mode = new(AuthenticationPayloadSSPI)
+	default:
+		err = ErrInvalidFormat
+		return
+	}
+
+	err = (*T).Mode.ReadFrom(decoder)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *Authentication) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.Int32(int32((*T).Mode.AuthenticationPayloadMode()))
+	if err != nil {
+		return
+	}
+
+	err = (*T).Mode.WriteTo(encoder)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*Authentication)(nil)
+
+type BackendKeyDataPayload struct {
+	ProcessID int32
+	SecretKey int32
+}
+
+type BackendKeyData BackendKeyDataPayload
+
+func (T *BackendKeyData) Type() fed.Type {
+	return TypeBackendKeyData
+}
+
+func (T *BackendKeyData) Length() (length int) {
+	length += 4
+
+	length += 4
+
+	return
+}
+
+func (T *BackendKeyData) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*int32)(&((*T).ProcessID)), err = decoder.Int32()
+	if err != nil {
+		return
+	}
+	*(*int32)(&((*T).SecretKey)), err = decoder.Int32()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *BackendKeyData) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.Int32(int32((*T).ProcessID))
+	if err != nil {
+		return
+	}
+
+	err = encoder.Int32(int32((*T).SecretKey))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*BackendKeyData)(nil)
+
+type BindPayload struct {
+	Destination       string
+	Source            string
+	FormatCodes       []int16
+	Parameters        [][]uint8
+	ResultFormatCodes []int16
+}
+
+type Bind BindPayload
+
+func (T *Bind) Type() fed.Type {
+	return TypeBind
+}
+
+func (T *Bind) Length() (length int) {
+	length += len((*T).Destination) + 1
+
+	length += len((*T).Source) + 1
+
+	temp15 := uint16(len((*T).FormatCodes))
+	_ = temp15
+
+	length += 2
+
+	for _, temp16 := range (*T).FormatCodes {
+		_ = temp16
+
+		length += 2
+
+	}
+
+	temp17 := uint16(len((*T).Parameters))
+	_ = temp17
+
+	length += 2
+
+	for _, temp18 := range (*T).Parameters {
+		_ = temp18
+
+		temp19 := int32(len(temp18))
+		_ = temp19
+
+		length += 4
+
+		for _, temp20 := range temp18 {
+			_ = temp20
+
+			length += 1
+
+		}
+
+	}
+
+	temp21 := uint16(len((*T).ResultFormatCodes))
+	_ = temp21
+
+	length += 2
+
+	for _, temp22 := range (*T).ResultFormatCodes {
+		_ = temp22
+
+		length += 2
+
+	}
+
+	return
+}
+
+func (T *Bind) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*string)(&((*T).Destination)), err = decoder.String()
+	if err != nil {
+		return
+	}
+	*(*string)(&((*T).Source)), err = decoder.String()
+	if err != nil {
+		return
+	}
+	var temp23 uint16
+	*(*uint16)(&(temp23)), err = decoder.Uint16()
+	if err != nil {
+		return
+	}
+
+	(*T).FormatCodes = slices.Resize((*T).FormatCodes, int(temp23))
+
+	for temp24 := 0; temp24 < int(temp23); temp24++ {
+		*(*int16)(&((*T).FormatCodes[temp24])), err = decoder.Int16()
+		if err != nil {
+			return
+		}
+
+	}
+
+	var temp25 uint16
+	*(*uint16)(&(temp25)), err = decoder.Uint16()
+	if err != nil {
+		return
+	}
+
+	(*T).Parameters = slices.Resize((*T).Parameters, int(temp25))
+
+	for temp26 := 0; temp26 < int(temp25); temp26++ {
+		var temp27 int32
+		*(*int32)(&(temp27)), err = decoder.Int32()
+		if err != nil {
+			return
+		}
+
+		if temp27 == -1 {
+			(*T).Parameters[temp26] = nil
+		} else {
+			if (*T).Parameters[temp26] == nil {
+				(*T).Parameters[temp26] = make([]uint8, int(temp27))
+			} else {
+				(*T).Parameters[temp26] = slices.Resize((*T).Parameters[temp26], int(temp27))
+			}
+
+			for temp28 := 0; temp28 < int(temp27); temp28++ {
+				*(*uint8)(&((*T).Parameters[temp26][temp28])), err = decoder.Uint8()
+				if err != nil {
+					return
+				}
+
+			}
+		}
+
+	}
+
+	var temp29 uint16
+	*(*uint16)(&(temp29)), err = decoder.Uint16()
+	if err != nil {
+		return
+	}
+
+	(*T).ResultFormatCodes = slices.Resize((*T).ResultFormatCodes, int(temp29))
+
+	for temp30 := 0; temp30 < int(temp29); temp30++ {
+		*(*int16)(&((*T).ResultFormatCodes[temp30])), err = decoder.Int16()
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+func (T *Bind) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.String(string((*T).Destination))
+	if err != nil {
+		return
+	}
+
+	err = encoder.String(string((*T).Source))
+	if err != nil {
+		return
+	}
+
+	temp31 := uint16(len((*T).FormatCodes))
+
+	err = encoder.Uint16(uint16(temp31))
+	if err != nil {
+		return
+	}
+
+	for _, temp32 := range (*T).FormatCodes {
+		err = encoder.Int16(int16(temp32))
+		if err != nil {
+			return
+		}
+
+	}
+
+	temp33 := uint16(len((*T).Parameters))
+
+	err = encoder.Uint16(uint16(temp33))
+	if err != nil {
+		return
+	}
+
+	for _, temp34 := range (*T).Parameters {
+		temp35 := int32(len(temp34))
+
+		if temp34 == nil {
+			temp35 = -1
+		}
+
+		err = encoder.Int32(int32(temp35))
+		if err != nil {
+			return
+		}
+
+		for _, temp36 := range temp34 {
+			err = encoder.Uint8(uint8(temp36))
+			if err != nil {
+				return
+			}
+
+		}
+
+	}
+
+	temp37 := uint16(len((*T).ResultFormatCodes))
+
+	err = encoder.Uint16(uint16(temp37))
+	if err != nil {
+		return
+	}
+
+	for _, temp38 := range (*T).ResultFormatCodes {
+		err = encoder.Int16(int16(temp38))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+var _ fed.Packet = (*Bind)(nil)
+
+type BindComplete struct{}
+
+func (T *BindComplete) Type() fed.Type {
+	return TypeBindComplete
+}
+
+func (T *BindComplete) Length() (length int) {
+
+	return
+}
+
+func (T *BindComplete) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	return
+}
+
+func (T *BindComplete) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+var _ fed.Packet = (*BindComplete)(nil)
+
+type ClosePayload struct {
+	Which uint8
+	Name  string
+}
+
+type Close ClosePayload
+
+func (T *Close) Type() fed.Type {
+	return TypeClose
+}
+
+func (T *Close) Length() (length int) {
+	length += 1
+
+	length += len((*T).Name) + 1
+
+	return
+}
+
+func (T *Close) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*uint8)(&((*T).Which)), err = decoder.Uint8()
+	if err != nil {
+		return
+	}
+	*(*string)(&((*T).Name)), err = decoder.String()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *Close) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.Uint8(uint8((*T).Which))
+	if err != nil {
+		return
+	}
+
+	err = encoder.String(string((*T).Name))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*Close)(nil)
+
+type CloseComplete struct{}
+
+func (T *CloseComplete) Type() fed.Type {
+	return TypeCloseComplete
+}
+
+func (T *CloseComplete) Length() (length int) {
+
+	return
+}
+
+func (T *CloseComplete) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	return
+}
+
+func (T *CloseComplete) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+var _ fed.Packet = (*CloseComplete)(nil)
+
+type CommandComplete string
+
+func (T *CommandComplete) Type() fed.Type {
+	return TypeCommandComplete
+}
+
+func (T *CommandComplete) Length() (length int) {
+	length += len((*T)) + 1
+
+	return
+}
+
+func (T *CommandComplete) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*string)(&(*T)), err = decoder.String()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *CommandComplete) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.String(string((*T)))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*CommandComplete)(nil)
+
+type CopyBothResponsePayload struct {
+	Mode              int8
+	ColumnFormatCodes []int16
+}
+
+type CopyBothResponse CopyBothResponsePayload
+
+func (T *CopyBothResponse) Type() fed.Type {
+	return TypeCopyBothResponse
+}
+
+func (T *CopyBothResponse) Length() (length int) {
+	length += 1
+
+	temp39 := uint16(len((*T).ColumnFormatCodes))
+	_ = temp39
+
+	length += 2
+
+	for _, temp40 := range (*T).ColumnFormatCodes {
+		_ = temp40
+
+		length += 2
+
+	}
+
+	return
+}
+
+func (T *CopyBothResponse) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*int8)(&((*T).Mode)), err = decoder.Int8()
+	if err != nil {
+		return
+	}
+	var temp41 uint16
+	*(*uint16)(&(temp41)), err = decoder.Uint16()
+	if err != nil {
+		return
+	}
+
+	(*T).ColumnFormatCodes = slices.Resize((*T).ColumnFormatCodes, int(temp41))
+
+	for temp42 := 0; temp42 < int(temp41); temp42++ {
+		*(*int16)(&((*T).ColumnFormatCodes[temp42])), err = decoder.Int16()
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+func (T *CopyBothResponse) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.Int8(int8((*T).Mode))
+	if err != nil {
+		return
+	}
+
+	temp43 := uint16(len((*T).ColumnFormatCodes))
+
+	err = encoder.Uint16(uint16(temp43))
+	if err != nil {
+		return
+	}
+
+	for _, temp44 := range (*T).ColumnFormatCodes {
+		err = encoder.Int16(int16(temp44))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+var _ fed.Packet = (*CopyBothResponse)(nil)
+
+type CopyData []uint8
+
+func (T *CopyData) Type() fed.Type {
+	return TypeCopyData
+}
+
+func (T *CopyData) Length() (length int) {
+	for _, temp45 := range *T {
+		_ = temp45
+
+		length += 1
+
+	}
+
+	return
+}
+
+func (T *CopyData) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	(*T) = (*T)[:0]
+
+	for {
+		if decoder.Position() >= decoder.Length() {
+			break
+		}
+
+		(*T) = slices.Resize((*T), len((*T))+1)
+
+		*(*uint8)(&((*T)[len((*T))-1])), err = decoder.Uint8()
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+func (T *CopyData) WriteTo(encoder *fed.Encoder) (err error) {
+	for _, temp46 := range *T {
+		err = encoder.Uint8(uint8(temp46))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+var _ fed.Packet = (*CopyData)(nil)
+
+type CopyDone struct{}
+
+func (T *CopyDone) Type() fed.Type {
+	return TypeCopyDone
+}
+
+func (T *CopyDone) Length() (length int) {
+
+	return
+}
+
+func (T *CopyDone) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	return
+}
+
+func (T *CopyDone) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+var _ fed.Packet = (*CopyDone)(nil)
+
+type CopyFail string
+
+func (T *CopyFail) Type() fed.Type {
+	return TypeCopyFail
+}
+
+func (T *CopyFail) Length() (length int) {
+	length += len((*T)) + 1
+
+	return
+}
+
+func (T *CopyFail) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*string)(&(*T)), err = decoder.String()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *CopyFail) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.String(string((*T)))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*CopyFail)(nil)
+
+type CopyInResponsePayload struct {
+	Mode              int8
+	ColumnFormatCodes []int16
+}
+
+type CopyInResponse CopyInResponsePayload
+
+func (T *CopyInResponse) Type() fed.Type {
+	return TypeCopyInResponse
+}
+
+func (T *CopyInResponse) Length() (length int) {
+	length += 1
+
+	temp47 := uint16(len((*T).ColumnFormatCodes))
+	_ = temp47
+
+	length += 2
+
+	for _, temp48 := range (*T).ColumnFormatCodes {
+		_ = temp48
+
+		length += 2
+
+	}
+
+	return
+}
+
+func (T *CopyInResponse) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*int8)(&((*T).Mode)), err = decoder.Int8()
+	if err != nil {
+		return
+	}
+	var temp49 uint16
+	*(*uint16)(&(temp49)), err = decoder.Uint16()
+	if err != nil {
+		return
+	}
+
+	(*T).ColumnFormatCodes = slices.Resize((*T).ColumnFormatCodes, int(temp49))
+
+	for temp50 := 0; temp50 < int(temp49); temp50++ {
+		*(*int16)(&((*T).ColumnFormatCodes[temp50])), err = decoder.Int16()
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+func (T *CopyInResponse) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.Int8(int8((*T).Mode))
+	if err != nil {
+		return
+	}
+
+	temp51 := uint16(len((*T).ColumnFormatCodes))
+
+	err = encoder.Uint16(uint16(temp51))
+	if err != nil {
+		return
+	}
+
+	for _, temp52 := range (*T).ColumnFormatCodes {
+		err = encoder.Int16(int16(temp52))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+var _ fed.Packet = (*CopyInResponse)(nil)
+
+type CopyOutResponsePayload struct {
+	Mode              int8
+	ColumnFormatCodes []int16
+}
+
+type CopyOutResponse CopyOutResponsePayload
+
+func (T *CopyOutResponse) Type() fed.Type {
+	return TypeCopyOutResponse
+}
+
+func (T *CopyOutResponse) Length() (length int) {
+	length += 1
+
+	temp53 := uint16(len((*T).ColumnFormatCodes))
+	_ = temp53
+
+	length += 2
+
+	for _, temp54 := range (*T).ColumnFormatCodes {
+		_ = temp54
+
+		length += 2
+
+	}
+
+	return
+}
+
+func (T *CopyOutResponse) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*int8)(&((*T).Mode)), err = decoder.Int8()
+	if err != nil {
+		return
+	}
+	var temp55 uint16
+	*(*uint16)(&(temp55)), err = decoder.Uint16()
+	if err != nil {
+		return
+	}
+
+	(*T).ColumnFormatCodes = slices.Resize((*T).ColumnFormatCodes, int(temp55))
+
+	for temp56 := 0; temp56 < int(temp55); temp56++ {
+		*(*int16)(&((*T).ColumnFormatCodes[temp56])), err = decoder.Int16()
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+func (T *CopyOutResponse) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.Int8(int8((*T).Mode))
+	if err != nil {
+		return
+	}
+
+	temp57 := uint16(len((*T).ColumnFormatCodes))
+
+	err = encoder.Uint16(uint16(temp57))
+	if err != nil {
+		return
+	}
+
+	for _, temp58 := range (*T).ColumnFormatCodes {
+		err = encoder.Int16(int16(temp58))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+var _ fed.Packet = (*CopyOutResponse)(nil)
+
+type DataRow [][]uint8
+
+func (T *DataRow) Type() fed.Type {
+	return TypeDataRow
+}
+
+func (T *DataRow) Length() (length int) {
+	temp59 := uint16(len((*T)))
+	_ = temp59
+
+	length += 2
+
+	for _, temp60 := range *T {
+		_ = temp60
+
+		temp61 := int32(len(temp60))
+		_ = temp61
+
+		length += 4
+
+		for _, temp62 := range temp60 {
+			_ = temp62
+
+			length += 1
+
+		}
+
+	}
+
+	return
+}
+
+func (T *DataRow) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	var temp63 uint16
+	*(*uint16)(&(temp63)), err = decoder.Uint16()
+	if err != nil {
+		return
+	}
+
+	(*T) = slices.Resize((*T), int(temp63))
+
+	for temp64 := 0; temp64 < int(temp63); temp64++ {
+		var temp65 int32
+		*(*int32)(&(temp65)), err = decoder.Int32()
+		if err != nil {
+			return
+		}
+
+		if temp65 == -1 {
+			(*T)[temp64] = nil
+		} else {
+			if (*T)[temp64] == nil {
+				(*T)[temp64] = make([]uint8, int(temp65))
+			} else {
+				(*T)[temp64] = slices.Resize((*T)[temp64], int(temp65))
+			}
+
+			for temp66 := 0; temp66 < int(temp65); temp66++ {
+				*(*uint8)(&((*T)[temp64][temp66])), err = decoder.Uint8()
+				if err != nil {
+					return
+				}
+
+			}
+		}
+
+	}
+
+	return
+}
+
+func (T *DataRow) WriteTo(encoder *fed.Encoder) (err error) {
+	temp67 := uint16(len((*T)))
+
+	err = encoder.Uint16(uint16(temp67))
+	if err != nil {
+		return
+	}
+
+	for _, temp68 := range *T {
+		temp69 := int32(len(temp68))
+
+		if temp68 == nil {
+			temp69 = -1
+		}
+
+		err = encoder.Int32(int32(temp69))
+		if err != nil {
+			return
+		}
+
+		for _, temp70 := range temp68 {
+			err = encoder.Uint8(uint8(temp70))
+			if err != nil {
+				return
+			}
+
+		}
+
+	}
+
+	return
+}
+
+var _ fed.Packet = (*DataRow)(nil)
+
+type DescribePayload struct {
+	Which uint8
+	Name  string
+}
+
+type Describe DescribePayload
+
+func (T *Describe) Type() fed.Type {
+	return TypeDescribe
+}
+
+func (T *Describe) Length() (length int) {
+	length += 1
+
+	length += len((*T).Name) + 1
+
+	return
+}
+
+func (T *Describe) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*uint8)(&((*T).Which)), err = decoder.Uint8()
+	if err != nil {
+		return
+	}
+	*(*string)(&((*T).Name)), err = decoder.String()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *Describe) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.Uint8(uint8((*T).Which))
+	if err != nil {
+		return
+	}
+
+	err = encoder.String(string((*T).Name))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*Describe)(nil)
+
+type EmptyQueryResponse struct{}
+
+func (T *EmptyQueryResponse) Type() fed.Type {
+	return TypeEmptyQueryResponse
+}
+
+func (T *EmptyQueryResponse) Length() (length int) {
+
+	return
+}
+
+func (T *EmptyQueryResponse) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	return
+}
+
+func (T *EmptyQueryResponse) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+var _ fed.Packet = (*EmptyQueryResponse)(nil)
+
+type ErrorResponseField struct {
+	Code  uint8
+	Value string
+}
+
+type ErrorResponse []ErrorResponseField
+
+func (T *ErrorResponse) Type() fed.Type {
+	return TypeErrorResponse
+}
+
+func (T *ErrorResponse) Length() (length int) {
+	for _, temp71 := range *T {
+		_ = temp71
+
+		length += 1
+
+		length += len(temp71.Value) + 1
+
+	}
+
+	var temp72 uint8
+	_ = temp72
+
+	length += 1
+
+	return
+}
+
+func (T *ErrorResponse) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	(*T) = (*T)[:0]
+
+	for {
+		(*T) = slices.Resize((*T), len((*T))+1)
+
+		*(*uint8)(&((*T)[len((*T))-1].Code)), err = decoder.Uint8()
+		if err != nil {
+			return
+		}
+		if (*T)[len((*T))-1].Code == *new(uint8) {
+			(*T) = (*T)[:len((*T))-1]
+			break
+		}
+		*(*string)(&((*T)[len((*T))-1].Value)), err = decoder.String()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+func (T *ErrorResponse) WriteTo(encoder *fed.Encoder) (err error) {
+	for _, temp73 := range *T {
+		err = encoder.Uint8(uint8(temp73.Code))
+		if err != nil {
+			return
+		}
+
+		err = encoder.String(string(temp73.Value))
+		if err != nil {
+			return
+		}
+
+	}
+
+	var temp74 uint8
+
+	err = encoder.Uint8(uint8(temp74))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*ErrorResponse)(nil)
+
+type ExecutePayload struct {
+	Target  string
+	MaxRows uint32
+}
+
+type Execute ExecutePayload
+
+func (T *Execute) Type() fed.Type {
+	return TypeExecute
+}
+
+func (T *Execute) Length() (length int) {
+	length += len((*T).Target) + 1
+
+	length += 4
+
+	return
+}
+
+func (T *Execute) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*string)(&((*T).Target)), err = decoder.String()
+	if err != nil {
+		return
+	}
+	*(*uint32)(&((*T).MaxRows)), err = decoder.Uint32()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *Execute) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.String(string((*T).Target))
+	if err != nil {
+		return
+	}
+
+	err = encoder.Uint32(uint32((*T).MaxRows))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*Execute)(nil)
+
+type Flush struct{}
+
+func (T *Flush) Type() fed.Type {
+	return TypeFlush
+}
+
+func (T *Flush) Length() (length int) {
+
+	return
+}
+
+func (T *Flush) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	return
+}
+
+func (T *Flush) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+var _ fed.Packet = (*Flush)(nil)
+
+type FunctionCallPayload struct {
+	ObjectID            int32
+	ArgumentFormatCodes []int16
+	Arguments           [][]uint8
+	ResultFormatCode    int16
+}
+
+type FunctionCall FunctionCallPayload
+
+func (T *FunctionCall) Type() fed.Type {
+	return TypeFunctionCall
+}
+
+func (T *FunctionCall) Length() (length int) {
+	length += 4
+
+	temp75 := uint16(len((*T).ArgumentFormatCodes))
+	_ = temp75
+
+	length += 2
+
+	for _, temp76 := range (*T).ArgumentFormatCodes {
+		_ = temp76
+
+		length += 2
+
+	}
+
+	temp77 := uint16(len((*T).Arguments))
+	_ = temp77
+
+	length += 2
+
+	for _, temp78 := range (*T).Arguments {
+		_ = temp78
+
+		temp79 := int32(len(temp78))
+		_ = temp79
+
+		length += 4
+
+		for _, temp80 := range temp78 {
+			_ = temp80
+
+			length += 1
+
+		}
+
+	}
+
+	length += 2
+
+	return
+}
+
+func (T *FunctionCall) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*int32)(&((*T).ObjectID)), err = decoder.Int32()
+	if err != nil {
+		return
+	}
+	var temp81 uint16
+	*(*uint16)(&(temp81)), err = decoder.Uint16()
+	if err != nil {
+		return
+	}
+
+	(*T).ArgumentFormatCodes = slices.Resize((*T).ArgumentFormatCodes, int(temp81))
+
+	for temp82 := 0; temp82 < int(temp81); temp82++ {
+		*(*int16)(&((*T).ArgumentFormatCodes[temp82])), err = decoder.Int16()
+		if err != nil {
+			return
+		}
+
+	}
+
+	var temp83 uint16
+	*(*uint16)(&(temp83)), err = decoder.Uint16()
+	if err != nil {
+		return
+	}
+
+	(*T).Arguments = slices.Resize((*T).Arguments, int(temp83))
+
+	for temp84 := 0; temp84 < int(temp83); temp84++ {
+		var temp85 int32
+		*(*int32)(&(temp85)), err = decoder.Int32()
+		if err != nil {
+			return
+		}
+
+		if temp85 == -1 {
+			(*T).Arguments[temp84] = nil
+		} else {
+			if (*T).Arguments[temp84] == nil {
+				(*T).Arguments[temp84] = make([]uint8, int(temp85))
+			} else {
+				(*T).Arguments[temp84] = slices.Resize((*T).Arguments[temp84], int(temp85))
+			}
+
+			for temp86 := 0; temp86 < int(temp85); temp86++ {
+				*(*uint8)(&((*T).Arguments[temp84][temp86])), err = decoder.Uint8()
+				if err != nil {
+					return
+				}
+
+			}
+		}
+
+	}
+
+	*(*int16)(&((*T).ResultFormatCode)), err = decoder.Int16()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *FunctionCall) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.Int32(int32((*T).ObjectID))
+	if err != nil {
+		return
+	}
+
+	temp87 := uint16(len((*T).ArgumentFormatCodes))
+
+	err = encoder.Uint16(uint16(temp87))
+	if err != nil {
+		return
+	}
+
+	for _, temp88 := range (*T).ArgumentFormatCodes {
+		err = encoder.Int16(int16(temp88))
+		if err != nil {
+			return
+		}
+
+	}
+
+	temp89 := uint16(len((*T).Arguments))
+
+	err = encoder.Uint16(uint16(temp89))
+	if err != nil {
+		return
+	}
+
+	for _, temp90 := range (*T).Arguments {
+		temp91 := int32(len(temp90))
+
+		if temp90 == nil {
+			temp91 = -1
+		}
+
+		err = encoder.Int32(int32(temp91))
+		if err != nil {
+			return
+		}
+
+		for _, temp92 := range temp90 {
+			err = encoder.Uint8(uint8(temp92))
+			if err != nil {
+				return
+			}
+
+		}
+
+	}
+
+	err = encoder.Int16(int16((*T).ResultFormatCode))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*FunctionCall)(nil)
+
+type FunctionCallResponse []uint8
+
+func (T *FunctionCallResponse) Type() fed.Type {
+	return TypeFunctionCallResponse
+}
+
+func (T *FunctionCallResponse) Length() (length int) {
+	temp93 := int32(len((*T)))
+	_ = temp93
+
+	length += 4
+
+	for _, temp94 := range *T {
+		_ = temp94
+
+		length += 1
+
+	}
+
+	return
+}
+
+func (T *FunctionCallResponse) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	var temp95 int32
+	*(*int32)(&(temp95)), err = decoder.Int32()
+	if err != nil {
+		return
+	}
+
+	if temp95 == -1 {
+		(*T) = nil
+	} else {
+		if (*T) == nil {
+			(*T) = make([]uint8, int(temp95))
+		} else {
+			(*T) = slices.Resize((*T), int(temp95))
+		}
+
+		for temp96 := 0; temp96 < int(temp95); temp96++ {
+			*(*uint8)(&((*T)[temp96])), err = decoder.Uint8()
+			if err != nil {
+				return
+			}
+
+		}
+	}
+
+	return
+}
+
+func (T *FunctionCallResponse) WriteTo(encoder *fed.Encoder) (err error) {
+	temp97 := int32(len((*T)))
+
+	if (*T) == nil {
+		temp97 = -1
+	}
+
+	err = encoder.Int32(int32(temp97))
+	if err != nil {
+		return
+	}
+
+	for _, temp98 := range *T {
+		err = encoder.Uint8(uint8(temp98))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+var _ fed.Packet = (*FunctionCallResponse)(nil)
+
+type GSSResponse []uint8
+
+func (T *GSSResponse) Type() fed.Type {
+	return TypeGSSResponse
+}
+
+func (T *GSSResponse) Length() (length int) {
+	for _, temp99 := range *T {
+		_ = temp99
+
+		length += 1
+
+	}
+
+	return
+}
+
+func (T *GSSResponse) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	(*T) = (*T)[:0]
+
+	for {
+		if decoder.Position() >= decoder.Length() {
+			break
+		}
+
+		(*T) = slices.Resize((*T), len((*T))+1)
+
+		*(*uint8)(&((*T)[len((*T))-1])), err = decoder.Uint8()
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+func (T *GSSResponse) WriteTo(encoder *fed.Encoder) (err error) {
+	for _, temp100 := range *T {
+		err = encoder.Uint8(uint8(temp100))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+var _ fed.Packet = (*GSSResponse)(nil)
+
+type NegotiateProtocolVersionPayload struct {
+	MinorProtocolVersion        int32
+	UnrecognizedProtocolOptions []string
+}
+
+type NegotiateProtocolVersion NegotiateProtocolVersionPayload
+
+func (T *NegotiateProtocolVersion) Type() fed.Type {
+	return TypeNegotiateProtocolVersion
+}
+
+func (T *NegotiateProtocolVersion) Length() (length int) {
+	length += 4
+
+	temp101 := uint32(len((*T).UnrecognizedProtocolOptions))
+	_ = temp101
+
+	length += 4
+
+	for _, temp102 := range (*T).UnrecognizedProtocolOptions {
+		_ = temp102
+
+		length += len(temp102) + 1
+
+	}
+
+	return
+}
+
+func (T *NegotiateProtocolVersion) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*int32)(&((*T).MinorProtocolVersion)), err = decoder.Int32()
+	if err != nil {
+		return
+	}
+	var temp103 uint32
+	*(*uint32)(&(temp103)), err = decoder.Uint32()
+	if err != nil {
+		return
+	}
+
+	(*T).UnrecognizedProtocolOptions = slices.Resize((*T).UnrecognizedProtocolOptions, int(temp103))
+
+	for temp104 := 0; temp104 < int(temp103); temp104++ {
+		*(*string)(&((*T).UnrecognizedProtocolOptions[temp104])), err = decoder.String()
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+func (T *NegotiateProtocolVersion) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.Int32(int32((*T).MinorProtocolVersion))
+	if err != nil {
+		return
+	}
+
+	temp105 := uint32(len((*T).UnrecognizedProtocolOptions))
+
+	err = encoder.Uint32(uint32(temp105))
+	if err != nil {
+		return
+	}
+
+	for _, temp106 := range (*T).UnrecognizedProtocolOptions {
+		err = encoder.String(string(temp106))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+var _ fed.Packet = (*NegotiateProtocolVersion)(nil)
+
+type NoData struct{}
+
+func (T *NoData) Type() fed.Type {
+	return TypeNoData
+}
+
+func (T *NoData) Length() (length int) {
+
+	return
+}
+
+func (T *NoData) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	return
+}
+
+func (T *NoData) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+var _ fed.Packet = (*NoData)(nil)
+
+type NoticeResponseField struct {
+	Code  uint8
+	Value string
+}
+
+type NoticeResponse []NoticeResponseField
+
+func (T *NoticeResponse) Type() fed.Type {
+	return TypeNoticeResponse
+}
+
+func (T *NoticeResponse) Length() (length int) {
+	for _, temp107 := range *T {
+		_ = temp107
+
+		length += 1
+
+		length += len(temp107.Value) + 1
+
+	}
+
+	var temp108 uint8
+	_ = temp108
+
+	length += 1
+
+	return
+}
+
+func (T *NoticeResponse) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	(*T) = (*T)[:0]
+
+	for {
+		(*T) = slices.Resize((*T), len((*T))+1)
+
+		*(*uint8)(&((*T)[len((*T))-1].Code)), err = decoder.Uint8()
+		if err != nil {
+			return
+		}
+		if (*T)[len((*T))-1].Code == *new(uint8) {
+			(*T) = (*T)[:len((*T))-1]
+			break
+		}
+		*(*string)(&((*T)[len((*T))-1].Value)), err = decoder.String()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+func (T *NoticeResponse) WriteTo(encoder *fed.Encoder) (err error) {
+	for _, temp109 := range *T {
+		err = encoder.Uint8(uint8(temp109.Code))
+		if err != nil {
+			return
+		}
+
+		err = encoder.String(string(temp109.Value))
+		if err != nil {
+			return
+		}
+
+	}
+
+	var temp110 uint8
+
+	err = encoder.Uint8(uint8(temp110))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*NoticeResponse)(nil)
+
+type NotificationResponsePayload struct {
+	ProcessID int32
+	Channel   string
+	Payload   string
+}
+
+type NotificationResponse NotificationResponsePayload
+
+func (T *NotificationResponse) Type() fed.Type {
+	return TypeNotificationResponse
+}
+
+func (T *NotificationResponse) Length() (length int) {
+	length += 4
+
+	length += len((*T).Channel) + 1
+
+	length += len((*T).Payload) + 1
+
+	return
+}
+
+func (T *NotificationResponse) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*int32)(&((*T).ProcessID)), err = decoder.Int32()
+	if err != nil {
+		return
+	}
+	*(*string)(&((*T).Channel)), err = decoder.String()
+	if err != nil {
+		return
+	}
+	*(*string)(&((*T).Payload)), err = decoder.String()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *NotificationResponse) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.Int32(int32((*T).ProcessID))
+	if err != nil {
+		return
+	}
+
+	err = encoder.String(string((*T).Channel))
+	if err != nil {
+		return
+	}
+
+	err = encoder.String(string((*T).Payload))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*NotificationResponse)(nil)
+
+type ParameterDescription []int32
+
+func (T *ParameterDescription) Type() fed.Type {
+	return TypeParameterDescription
+}
+
+func (T *ParameterDescription) Length() (length int) {
+	temp111 := uint16(len((*T)))
+	_ = temp111
+
+	length += 2
+
+	for _, temp112 := range *T {
+		_ = temp112
+
+		length += 4
+
+	}
+
+	return
+}
+
+func (T *ParameterDescription) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	var temp113 uint16
+	*(*uint16)(&(temp113)), err = decoder.Uint16()
+	if err != nil {
+		return
+	}
+
+	(*T) = slices.Resize((*T), int(temp113))
+
+	for temp114 := 0; temp114 < int(temp113); temp114++ {
+		*(*int32)(&((*T)[temp114])), err = decoder.Int32()
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+func (T *ParameterDescription) WriteTo(encoder *fed.Encoder) (err error) {
+	temp115 := uint16(len((*T)))
+
+	err = encoder.Uint16(uint16(temp115))
+	if err != nil {
+		return
+	}
+
+	for _, temp116 := range *T {
+		err = encoder.Int32(int32(temp116))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+var _ fed.Packet = (*ParameterDescription)(nil)
+
+type ParameterStatusPayload struct {
+	Key   string
+	Value string
+}
+
+type ParameterStatus ParameterStatusPayload
+
+func (T *ParameterStatus) Type() fed.Type {
+	return TypeParameterStatus
+}
+
+func (T *ParameterStatus) Length() (length int) {
+	length += len((*T).Key) + 1
+
+	length += len((*T).Value) + 1
+
+	return
+}
+
+func (T *ParameterStatus) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*string)(&((*T).Key)), err = decoder.String()
+	if err != nil {
+		return
+	}
+	*(*string)(&((*T).Value)), err = decoder.String()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *ParameterStatus) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.String(string((*T).Key))
+	if err != nil {
+		return
+	}
+
+	err = encoder.String(string((*T).Value))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*ParameterStatus)(nil)
+
+type ParsePayload struct {
+	Destination        string
+	Query              string
+	ParameterDataTypes []int32
+}
+
+type Parse ParsePayload
+
+func (T *Parse) Type() fed.Type {
+	return TypeParse
+}
+
+func (T *Parse) Length() (length int) {
+	length += len((*T).Destination) + 1
+
+	length += len((*T).Query) + 1
+
+	temp117 := uint16(len((*T).ParameterDataTypes))
+	_ = temp117
+
+	length += 2
+
+	for _, temp118 := range (*T).ParameterDataTypes {
+		_ = temp118
+
+		length += 4
+
+	}
+
+	return
+}
+
+func (T *Parse) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*string)(&((*T).Destination)), err = decoder.String()
+	if err != nil {
+		return
+	}
+	*(*string)(&((*T).Query)), err = decoder.String()
+	if err != nil {
+		return
+	}
+	var temp119 uint16
+	*(*uint16)(&(temp119)), err = decoder.Uint16()
+	if err != nil {
+		return
+	}
+
+	(*T).ParameterDataTypes = slices.Resize((*T).ParameterDataTypes, int(temp119))
+
+	for temp120 := 0; temp120 < int(temp119); temp120++ {
+		*(*int32)(&((*T).ParameterDataTypes[temp120])), err = decoder.Int32()
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+func (T *Parse) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.String(string((*T).Destination))
+	if err != nil {
+		return
+	}
+
+	err = encoder.String(string((*T).Query))
+	if err != nil {
+		return
+	}
+
+	temp121 := uint16(len((*T).ParameterDataTypes))
+
+	err = encoder.Uint16(uint16(temp121))
+	if err != nil {
+		return
+	}
+
+	for _, temp122 := range (*T).ParameterDataTypes {
+		err = encoder.Int32(int32(temp122))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+var _ fed.Packet = (*Parse)(nil)
+
+type ParseComplete struct{}
+
+func (T *ParseComplete) Type() fed.Type {
+	return TypeParseComplete
+}
+
+func (T *ParseComplete) Length() (length int) {
+
+	return
+}
+
+func (T *ParseComplete) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	return
+}
+
+func (T *ParseComplete) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+var _ fed.Packet = (*ParseComplete)(nil)
+
+type PasswordMessage string
+
+func (T *PasswordMessage) Type() fed.Type {
+	return TypePasswordMessage
+}
+
+func (T *PasswordMessage) Length() (length int) {
+	length += len((*T)) + 1
+
+	return
+}
+
+func (T *PasswordMessage) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*string)(&(*T)), err = decoder.String()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *PasswordMessage) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.String(string((*T)))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*PasswordMessage)(nil)
+
+type PortalSuspended struct{}
+
+func (T *PortalSuspended) Type() fed.Type {
+	return TypePortalSuspended
+}
+
+func (T *PortalSuspended) Length() (length int) {
+
+	return
+}
+
+func (T *PortalSuspended) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	return
+}
+
+func (T *PortalSuspended) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+var _ fed.Packet = (*PortalSuspended)(nil)
+
+type Query string
+
+func (T *Query) Type() fed.Type {
+	return TypeQuery
+}
+
+func (T *Query) Length() (length int) {
+	length += len((*T)) + 1
+
+	return
+}
+
+func (T *Query) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*string)(&(*T)), err = decoder.String()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *Query) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.String(string((*T)))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*Query)(nil)
+
+type ReadyForQuery uint8
+
+func (T *ReadyForQuery) Type() fed.Type {
+	return TypeReadyForQuery
+}
+
+func (T *ReadyForQuery) Length() (length int) {
+	length += 1
+
+	return
+}
+
+func (T *ReadyForQuery) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*uint8)(&(*T)), err = decoder.Uint8()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *ReadyForQuery) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.Uint8(uint8((*T)))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*ReadyForQuery)(nil)
+
+type RowDescriptionRow struct {
+	Name                  string
+	TableID               int32
+	ColumnAttributeNumber int16
+	FieldDataType         int32
+	DataTypeSize          int16
+	TypeModifier          int32
+	FormatCode            int16
+}
+
+type RowDescription []RowDescriptionRow
+
+func (T *RowDescription) Type() fed.Type {
+	return TypeRowDescription
+}
+
+func (T *RowDescription) Length() (length int) {
+	temp123 := uint16(len((*T)))
+	_ = temp123
+
+	length += 2
+
+	for _, temp124 := range *T {
+		_ = temp124
+
+		length += len(temp124.Name) + 1
+
+		length += 4
+
+		length += 2
+
+		length += 4
+
+		length += 2
+
+		length += 4
+
+		length += 2
+
+	}
+
+	return
+}
+
+func (T *RowDescription) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	var temp125 uint16
+	*(*uint16)(&(temp125)), err = decoder.Uint16()
+	if err != nil {
+		return
+	}
+
+	(*T) = slices.Resize((*T), int(temp125))
+
+	for temp126 := 0; temp126 < int(temp125); temp126++ {
+		*(*string)(&((*T)[temp126].Name)), err = decoder.String()
+		if err != nil {
+			return
+		}
+		*(*int32)(&((*T)[temp126].TableID)), err = decoder.Int32()
+		if err != nil {
+			return
+		}
+		*(*int16)(&((*T)[temp126].ColumnAttributeNumber)), err = decoder.Int16()
+		if err != nil {
+			return
+		}
+		*(*int32)(&((*T)[temp126].FieldDataType)), err = decoder.Int32()
+		if err != nil {
+			return
+		}
+		*(*int16)(&((*T)[temp126].DataTypeSize)), err = decoder.Int16()
+		if err != nil {
+			return
+		}
+		*(*int32)(&((*T)[temp126].TypeModifier)), err = decoder.Int32()
+		if err != nil {
+			return
+		}
+		*(*int16)(&((*T)[temp126].FormatCode)), err = decoder.Int16()
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+func (T *RowDescription) WriteTo(encoder *fed.Encoder) (err error) {
+	temp127 := uint16(len((*T)))
+
+	err = encoder.Uint16(uint16(temp127))
+	if err != nil {
+		return
+	}
+
+	for _, temp128 := range *T {
+		err = encoder.String(string(temp128.Name))
+		if err != nil {
+			return
+		}
+
+		err = encoder.Int32(int32(temp128.TableID))
+		if err != nil {
+			return
+		}
+
+		err = encoder.Int16(int16(temp128.ColumnAttributeNumber))
+		if err != nil {
+			return
+		}
+
+		err = encoder.Int32(int32(temp128.FieldDataType))
+		if err != nil {
+			return
+		}
+
+		err = encoder.Int16(int16(temp128.DataTypeSize))
+		if err != nil {
+			return
+		}
+
+		err = encoder.Int32(int32(temp128.TypeModifier))
+		if err != nil {
+			return
+		}
+
+		err = encoder.Int16(int16(temp128.FormatCode))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+var _ fed.Packet = (*RowDescription)(nil)
+
+type SASLInitialResponsePayload struct {
+	Mechanism             string
+	InitialClientResponse []uint8
+}
+
+type SASLInitialResponse SASLInitialResponsePayload
+
+func (T *SASLInitialResponse) Type() fed.Type {
+	return TypeSASLInitialResponse
+}
+
+func (T *SASLInitialResponse) Length() (length int) {
+	length += len((*T).Mechanism) + 1
+
+	temp129 := int32(len((*T).InitialClientResponse))
+	_ = temp129
+
+	length += 4
+
+	for _, temp130 := range (*T).InitialClientResponse {
+		_ = temp130
+
+		length += 1
+
+	}
+
+	return
+}
+
+func (T *SASLInitialResponse) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	*(*string)(&((*T).Mechanism)), err = decoder.String()
+	if err != nil {
+		return
+	}
+	var temp131 int32
+	*(*int32)(&(temp131)), err = decoder.Int32()
+	if err != nil {
+		return
+	}
+
+	if temp131 == -1 {
+		(*T).InitialClientResponse = nil
+	} else {
+		if (*T).InitialClientResponse == nil {
+			(*T).InitialClientResponse = make([]uint8, int(temp131))
+		} else {
+			(*T).InitialClientResponse = slices.Resize((*T).InitialClientResponse, int(temp131))
+		}
+
+		for temp132 := 0; temp132 < int(temp131); temp132++ {
+			*(*uint8)(&((*T).InitialClientResponse[temp132])), err = decoder.Uint8()
+			if err != nil {
+				return
+			}
+
+		}
+	}
+
+	return
+}
+
+func (T *SASLInitialResponse) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.String(string((*T).Mechanism))
+	if err != nil {
+		return
+	}
+
+	temp133 := int32(len((*T).InitialClientResponse))
+
+	if (*T).InitialClientResponse == nil {
+		temp133 = -1
+	}
+
+	err = encoder.Int32(int32(temp133))
+	if err != nil {
+		return
+	}
+
+	for _, temp134 := range (*T).InitialClientResponse {
+		err = encoder.Uint8(uint8(temp134))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+var _ fed.Packet = (*SASLInitialResponse)(nil)
+
+type SASLResponse []uint8
+
+func (T *SASLResponse) Type() fed.Type {
+	return TypeSASLResponse
+}
+
+func (T *SASLResponse) Length() (length int) {
+	for _, temp135 := range *T {
+		_ = temp135
+
+		length += 1
+
+	}
+
+	return
+}
+
+func (T *SASLResponse) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	(*T) = (*T)[:0]
+
+	for {
+		if decoder.Position() >= decoder.Length() {
+			break
+		}
+
+		(*T) = slices.Resize((*T), len((*T))+1)
+
+		*(*uint8)(&((*T)[len((*T))-1])), err = decoder.Uint8()
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+func (T *SASLResponse) WriteTo(encoder *fed.Encoder) (err error) {
+	for _, temp136 := range *T {
+		err = encoder.Uint8(uint8(temp136))
+		if err != nil {
+			return
+		}
+
+	}
+
+	return
+}
+
+var _ fed.Packet = (*SASLResponse)(nil)
+
+type StartupPayloadControlPayloadCancelKey struct {
+	ProcessID int32
+	SecretKey int32
+}
+
+type StartupPayloadControlPayloadCancel StartupPayloadControlPayloadCancelKey
+
+func (*StartupPayloadControlPayloadCancel) StartupPayloadControlPayloadMode() int16 {
+	return 5678
+}
+
+func (T *StartupPayloadControlPayloadCancel) Length() (length int) {
+	length += 4
+
+	length += 4
+
+	return
+}
+
+func (T *StartupPayloadControlPayloadCancel) ReadFrom(decoder *fed.Decoder) (err error) {
+	*(*int32)(&((*T).ProcessID)), err = decoder.Int32()
+	if err != nil {
+		return
+	}
+	*(*int32)(&((*T).SecretKey)), err = decoder.Int32()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *StartupPayloadControlPayloadCancel) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.Int32(int32((*T).ProcessID))
+	if err != nil {
+		return
+	}
+
+	err = encoder.Int32(int32((*T).SecretKey))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+type StartupPayloadControlPayloadGSSAPI struct{}
+
+func (*StartupPayloadControlPayloadGSSAPI) StartupPayloadControlPayloadMode() int16 {
+	return 5680
+}
+
+func (T *StartupPayloadControlPayloadGSSAPI) Length() (length int) {
+
+	return
+}
+
+func (T *StartupPayloadControlPayloadGSSAPI) ReadFrom(decoder *fed.Decoder) (err error) {
+
+	return
+}
+
+func (T *StartupPayloadControlPayloadGSSAPI) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+type StartupPayloadControlPayloadSSL struct{}
+
+func (*StartupPayloadControlPayloadSSL) StartupPayloadControlPayloadMode() int16 {
+	return 5679
+}
+
+func (T *StartupPayloadControlPayloadSSL) Length() (length int) {
+
+	return
+}
+
+func (T *StartupPayloadControlPayloadSSL) ReadFrom(decoder *fed.Decoder) (err error) {
+
+	return
+}
+
+func (T *StartupPayloadControlPayloadSSL) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+type StartupPayloadControlPayloadMode interface {
+	StartupPayloadControlPayloadMode() int16
+
+	Length() int
+	ReadFrom(decoder *fed.Decoder) error
+	WriteTo(encoder *fed.Encoder) error
+}
+type StartupPayloadControlPayload struct {
+	Mode StartupPayloadControlPayloadMode
+}
+
+type StartupPayloadControl StartupPayloadControlPayload
+
+func (*StartupPayloadControl) StartupPayloadMode() int16 {
+	return 1234
+}
+
+func (T *StartupPayloadControl) Length() (length int) {
+	length += 2
+
+	length += (*T).Mode.Length()
+
+	return
+}
+
+func (T *StartupPayloadControl) ReadFrom(decoder *fed.Decoder) (err error) {
+	var temp137 int16
+
+	*(*int16)(&(temp137)), err = decoder.Int16()
+	if err != nil {
+		return
+	}
+
+	switch temp137 {
+	case 5678:
+		(*T).Mode = new(StartupPayloadControlPayloadCancel)
+	case 5680:
+		(*T).Mode = new(StartupPayloadControlPayloadGSSAPI)
+	case 5679:
+		(*T).Mode = new(StartupPayloadControlPayloadSSL)
+	default:
+		err = ErrInvalidFormat
+		return
+	}
+
+	err = (*T).Mode.ReadFrom(decoder)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *StartupPayloadControl) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.Int16(int16((*T).Mode.StartupPayloadControlPayloadMode()))
+	if err != nil {
+		return
+	}
+
+	err = (*T).Mode.WriteTo(encoder)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+type StartupPayloadVersion3PayloadParameter struct {
+	Key   string
+	Value string
+}
+
+type StartupPayloadVersion3Payload struct {
+	MinorVersion int16
+	Parameters   []StartupPayloadVersion3PayloadParameter
+}
+
+type StartupPayloadVersion3 StartupPayloadVersion3Payload
+
+func (*StartupPayloadVersion3) StartupPayloadMode() int16 {
+	return 3
+}
+
+func (T *StartupPayloadVersion3) Length() (length int) {
+	length += 2
+
+	for _, temp138 := range (*T).Parameters {
+		_ = temp138
+
+		length += len(temp138.Key) + 1
+
+		length += len(temp138.Value) + 1
+
+	}
+
+	var temp139 string
+	_ = temp139
+
+	length += len(temp139) + 1
+
+	return
+}
+
+func (T *StartupPayloadVersion3) ReadFrom(decoder *fed.Decoder) (err error) {
+	*(*int16)(&((*T).MinorVersion)), err = decoder.Int16()
+	if err != nil {
+		return
+	}
+	(*T).Parameters = (*T).Parameters[:0]
+
+	for {
+		(*T).Parameters = slices.Resize((*T).Parameters, len((*T).Parameters)+1)
+
+		*(*string)(&((*T).Parameters[len((*T).Parameters)-1].Key)), err = decoder.String()
+		if err != nil {
+			return
+		}
+		if (*T).Parameters[len((*T).Parameters)-1].Key == *new(string) {
+			(*T).Parameters = (*T).Parameters[:len((*T).Parameters)-1]
+			break
+		}
+		*(*string)(&((*T).Parameters[len((*T).Parameters)-1].Value)), err = decoder.String()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+func (T *StartupPayloadVersion3) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.Int16(int16((*T).MinorVersion))
+	if err != nil {
+		return
+	}
+
+	for _, temp140 := range (*T).Parameters {
+		err = encoder.String(string(temp140.Key))
+		if err != nil {
+			return
+		}
+
+		err = encoder.String(string(temp140.Value))
+		if err != nil {
+			return
+		}
+
+	}
+
+	var temp141 string
+
+	err = encoder.String(string(temp141))
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+type StartupPayloadMode interface {
+	StartupPayloadMode() int16
+
+	Length() int
+	ReadFrom(decoder *fed.Decoder) error
+	WriteTo(encoder *fed.Encoder) error
+}
+type StartupPayload struct {
+	Mode StartupPayloadMode
+}
+
+type Startup StartupPayload
+
+func (T *Startup) Type() fed.Type {
+	return 0
+}
+
+func (T *Startup) Length() (length int) {
+	length += 2
+
+	length += (*T).Mode.Length()
+
+	return
+}
+
+func (T *Startup) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	var temp142 int16
+
+	*(*int16)(&(temp142)), err = decoder.Int16()
+	if err != nil {
+		return
+	}
+
+	switch temp142 {
+	case 1234:
+		(*T).Mode = new(StartupPayloadControl)
+	case 3:
+		(*T).Mode = new(StartupPayloadVersion3)
+	default:
+		err = ErrInvalidFormat
+		return
+	}
+
+	err = (*T).Mode.ReadFrom(decoder)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (T *Startup) WriteTo(encoder *fed.Encoder) (err error) {
+	err = encoder.Int16(int16((*T).Mode.StartupPayloadMode()))
+	if err != nil {
+		return
+	}
+
+	err = (*T).Mode.WriteTo(encoder)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+var _ fed.Packet = (*Startup)(nil)
+
+type Sync struct{}
+
+func (T *Sync) Type() fed.Type {
+	return TypeSync
+}
+
+func (T *Sync) Length() (length int) {
+
+	return
+}
+
+func (T *Sync) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	return
+}
+
+func (T *Sync) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+var _ fed.Packet = (*Sync)(nil)
+
+type Terminate struct{}
+
+func (T *Terminate) Type() fed.Type {
+	return TypeTerminate
+}
+
+func (T *Terminate) Length() (length int) {
+
+	return
+}
+
+func (T *Terminate) ReadFrom(decoder *fed.Decoder) (err error) {
+	if decoder.Type() != T.Type() {
+		return ErrUnexpectedPacket
+	}
+
+	return
+}
+
+func (T *Terminate) WriteTo(encoder *fed.Encoder) (err error) {
+
+	return
+}
+
+var _ fed.Packet = (*Terminate)(nil)
diff --git a/lib/fed/packets/v3.0/parameterstatus.go b/lib/fed/packets/v3.0/parameterstatus.go
deleted file mode 100644
index 2280971fac953618dc054a0c9c196df76dc117df..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/parameterstatus.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package packets
-
-import "gfx.cafe/gfx/pggat/lib/fed"
-
-type ParameterStatus struct {
-	Key   string
-	Value string
-}
-
-func (T *ParameterStatus) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeParameterStatus {
-		return false
-	}
-	p := packet.ReadString(&T.Key)
-	p = p.ReadString(&T.Value)
-	return true
-}
-
-func (T *ParameterStatus) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = packet.Reset(TypeParameterStatus, len(T.Key)+len(T.Value)+2)
-	packet = packet.AppendString(T.Key)
-	packet = packet.AppendString(T.Value)
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/parse.go b/lib/fed/packets/v3.0/parse.go
deleted file mode 100644
index 1ae827a42c91c05cd4d5528bc569eeb4bfa8e51c..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/parse.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package packets
-
-import (
-	"gfx.cafe/gfx/pggat/lib/fed"
-	"gfx.cafe/gfx/pggat/lib/util/slices"
-)
-
-type Parse struct {
-	Destination        string
-	Query              string
-	ParameterDataTypes []int32
-}
-
-func (T *Parse) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeParse {
-		return false
-	}
-	p := packet.ReadString(&T.Destination)
-	p = p.ReadString(&T.Query)
-	var parameterDataTypesCount int16
-	p = p.ReadInt16(&parameterDataTypesCount)
-	T.ParameterDataTypes = slices.Resize(T.ParameterDataTypes, int(parameterDataTypesCount))
-	for i := 0; i < int(parameterDataTypesCount); i++ {
-		p = p.ReadInt32(&T.ParameterDataTypes[i])
-	}
-	return true
-}
-
-func (T *Parse) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = packet.Reset(TypeParse, len(T.Destination)+len(T.Query)+4+len(T.ParameterDataTypes)*4)
-	packet = packet.AppendString(T.Destination)
-	packet = packet.AppendString(T.Query)
-	packet = packet.AppendInt16(int16(len(T.ParameterDataTypes)))
-	for _, v := range T.ParameterDataTypes {
-		packet = packet.AppendInt32(v)
-	}
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/passwordmessage.go b/lib/fed/packets/v3.0/passwordmessage.go
deleted file mode 100644
index 7994328304204bceb80eac06877ee526bbfbd457..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/passwordmessage.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package packets
-
-import (
-	"gfx.cafe/gfx/pggat/lib/fed"
-)
-
-type PasswordMessage struct {
-	Password string
-}
-
-func (T *PasswordMessage) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeAuthenticationResponse {
-		return false
-	}
-	packet.ReadString(&T.Password)
-	return true
-}
-
-func (T *PasswordMessage) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = packet.Reset(TypeAuthenticationResponse, len(T.Password)+1)
-	packet = packet.AppendString(T.Password)
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/query.go b/lib/fed/packets/v3.0/query.go
deleted file mode 100644
index 5a0e43afdd9b09518ecea02420c4e28c65f1ddda..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/query.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package packets
-
-import "gfx.cafe/gfx/pggat/lib/fed"
-
-type Query string
-
-func (T *Query) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeQuery {
-		return false
-	}
-	packet.ReadString((*string)(T))
-	return true
-}
-
-func (T *Query) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = packet.Reset(TypeQuery, len(*T)+1)
-	packet = packet.AppendString(string(*T))
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/readyforquery.go b/lib/fed/packets/v3.0/readyforquery.go
deleted file mode 100644
index aaf111dd3c4f74413934ebd337056bf8fc9ed5eb..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/readyforquery.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package packets
-
-import (
-	"gfx.cafe/gfx/pggat/lib/fed"
-)
-
-type ReadyForQuery byte
-
-func (T *ReadyForQuery) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeReadyForQuery {
-		return false
-	}
-	packet.ReadUint8((*byte)(T))
-	return true
-}
-
-func (T *ReadyForQuery) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = fed.NewPacket(TypeReadyForQuery, 1)
-	packet = packet.AppendUint8(byte(*T))
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/rowdescription.go b/lib/fed/packets/v3.0/rowdescription.go
deleted file mode 100644
index fa4001c520d36fae689a42a7e50d9a304eb4dd3c..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/rowdescription.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package packets
-
-import (
-	"gfx.cafe/gfx/pggat/lib/fed"
-	"gfx.cafe/gfx/pggat/lib/util/slices"
-)
-
-type RowDescriptionField struct {
-	Name         string
-	TableID      int32
-	ColumnID     int16
-	Type         int32
-	TypeLength   int16
-	TypeModifier int32
-	FormatCode   int16
-}
-
-type RowDescription struct {
-	Fields []RowDescriptionField
-}
-
-func (T *RowDescription) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeRowDescription {
-		return false
-	}
-
-	var fieldsPerRow uint16
-	p := packet.ReadUint16(&fieldsPerRow)
-	T.Fields = slices.Resize(T.Fields, int(fieldsPerRow))
-	for i := 0; i < int(fieldsPerRow); i++ {
-		p = p.ReadString(&T.Fields[i].Name)
-		p = p.ReadInt32(&T.Fields[i].TableID)
-		p = p.ReadInt16(&T.Fields[i].ColumnID)
-		p = p.ReadInt32(&T.Fields[i].Type)
-		p = p.ReadInt16(&T.Fields[i].TypeLength)
-		p = p.ReadInt32(&T.Fields[i].TypeModifier)
-		p = p.ReadInt16(&T.Fields[i].FormatCode)
-	}
-
-	return true
-}
-
-func (T *RowDescription) IntoPacket(packet fed.Packet) fed.Packet {
-	size := 2
-	for _, v := range T.Fields {
-		size += len(v.Name) + 1
-		size += 4 + 2 + 4 + 2 + 4 + 2
-	}
-
-	packet = packet.Reset(TypeRowDescription, size)
-	packet = packet.AppendUint16(uint16(len(T.Fields)))
-	for _, v := range T.Fields {
-		packet = packet.AppendString(v.Name)
-		packet = packet.AppendInt32(v.TableID)
-		packet = packet.AppendInt16(v.ColumnID)
-		packet = packet.AppendInt32(v.Type)
-		packet = packet.AppendInt16(v.TypeLength)
-		packet = packet.AppendInt32(v.TypeModifier)
-		packet = packet.AppendInt16(v.FormatCode)
-	}
-
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/saslinitialresponse.go b/lib/fed/packets/v3.0/saslinitialresponse.go
deleted file mode 100644
index b9046788c0f16a71d1bfca4669e51ee8c54d45ab..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/saslinitialresponse.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package packets
-
-import (
-	"gfx.cafe/gfx/pggat/lib/fed"
-	"gfx.cafe/gfx/pggat/lib/util/slices"
-)
-
-type SASLInitialResponse struct {
-	Mechanism       string
-	InitialResponse []byte
-}
-
-func (T *SASLInitialResponse) ReadFromPacket(packet fed.Packet) bool {
-	if packet.Type() != TypeAuthenticationResponse {
-		return false
-	}
-
-	p := packet.ReadString(&T.Mechanism)
-
-	var initialResponseSize int32
-	p = p.ReadInt32(&initialResponseSize)
-
-	T.InitialResponse = slices.Resize(T.InitialResponse, int(initialResponseSize))
-	p = p.ReadBytes(T.InitialResponse)
-
-	return true
-}
-
-func (T *SASLInitialResponse) IntoPacket(packet fed.Packet) fed.Packet {
-	packet = packet.Reset(TypeAuthenticationResponse, len(T.Mechanism)+5+len(T.InitialResponse))
-	packet = packet.AppendString(T.Mechanism)
-	packet = packet.AppendInt32(int32(len(T.InitialResponse)))
-	packet = packet.AppendBytes(T.InitialResponse)
-	return packet
-}
diff --git a/lib/fed/packets/v3.0/types.go b/lib/fed/packets/v3.0/types.go
deleted file mode 100644
index f1d5335e7923940a588442a40b88e299d074a09a..0000000000000000000000000000000000000000
--- a/lib/fed/packets/v3.0/types.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package packets
-
-import "gfx.cafe/gfx/pggat/lib/fed"
-
-const (
-	TypeAuthentication           fed.Type = 'R'
-	TypeBackendKeyData           fed.Type = 'K'
-	TypeBind                     fed.Type = 'B'
-	TypeBindComplete             fed.Type = '2'
-	TypeClose                    fed.Type = 'C'
-	TypeCloseComplete            fed.Type = '3'
-	TypeCommandComplete          fed.Type = 'C'
-	TypeCopyData                 fed.Type = 'd'
-	TypeCopyDone                 fed.Type = 'c'
-	TypeCopyFail                 fed.Type = 'f'
-	TypeCopyInResponse           fed.Type = 'G'
-	TypeCopyOutResponse          fed.Type = 'H'
-	TypeCopyBothResponse         fed.Type = 'W'
-	TypeDataRow                  fed.Type = 'D'
-	TypeDescribe                 fed.Type = 'D'
-	TypeEmptyQueryResponse       fed.Type = 'I'
-	TypeErrorResponse            fed.Type = 'E'
-	TypeExecute                  fed.Type = 'E'
-	TypeFlush                    fed.Type = 'H'
-	TypeFunctionCall             fed.Type = 'F'
-	TypeFunctionCallResponse     fed.Type = 'V'
-	TypeAuthenticationResponse   fed.Type = 'p'
-	TypeNegotiateProtocolVersion fed.Type = 'v'
-	TypeNoData                   fed.Type = 'n'
-	TypeNoticeResponse           fed.Type = 'N'
-	TypeNotificationResponse     fed.Type = 'A'
-	TypeParameterDescription     fed.Type = 't'
-	TypeParameterStatus          fed.Type = 'S'
-	TypeParse                    fed.Type = 'P'
-	TypeParseComplete            fed.Type = '1'
-	TypePortalSuspended          fed.Type = 's'
-	TypeQuery                    fed.Type = 'Q'
-	TypeReadyForQuery            fed.Type = 'Z'
-	TypeRowDescription           fed.Type = 'T'
-	TypeSync                     fed.Type = 'S'
-	TypeTerminate                fed.Type = 'X'
-)
diff --git a/lib/fed/readwriter.go b/lib/fed/readwriter.go
deleted file mode 100644
index bde9a0d17f45d0b48b682e4c87d2108b419ff9dd..0000000000000000000000000000000000000000
--- a/lib/fed/readwriter.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package fed
-
-type Reader interface {
-	ReadPacket(typed bool, buffer Packet) (Packet, error)
-}
-
-type Writer interface {
-	WritePacket(Packet) error
-}
-
-type ReadWriter interface {
-	Reader
-	Writer
-}
-
-type ReadWriteCloser interface {
-	ReadWriter
-
-	Close() error
-}
diff --git a/lib/fed/ssl.go b/lib/fed/ssl.go
deleted file mode 100644
index 9b830d23ded6eef3b32461fb91053ec67a3d61ae..0000000000000000000000000000000000000000
--- a/lib/fed/ssl.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package fed
-
-import "crypto/tls"
-
-type SSL interface {
-	SSL() bool
-}
-
-type SSLClient interface {
-	SSL
-
-	EnableSSLClient(config *tls.Config) error
-}
-
-type SSLServer interface {
-	SSL
-
-	EnableSSLServer(config *tls.Config) error
-}
diff --git a/lib/gat/handler.go b/lib/gat/handler.go
index 6026c1e6778260a9f25d5a2ce66e5b14677c051f..b0d2d339c14e119b9e56ddd5bacf46bce0319f8b 100644
--- a/lib/gat/handler.go
+++ b/lib/gat/handler.go
@@ -15,7 +15,7 @@ type Handler interface {
 type CancellableHandler interface {
 	Handler
 
-	Cancel(key [8]byte)
+	Cancel(key fed.BackendKey)
 }
 
 type MetricsHandler interface {
diff --git a/lib/gat/handlers/discovery/discoverers/google_cloud_sql/discoverer.go b/lib/gat/handlers/discovery/discoverers/google_cloud_sql/discoverer.go
index 6bb9351b023cd1c14533778c68fe9c1a575b239d..8bd3fa07d6e98783e6f557a109bc655f09f8ddb6 100644
--- a/lib/gat/handlers/discovery/discoverers/google_cloud_sql/discoverer.go
+++ b/lib/gat/handlers/discovery/discoverers/google_cloud_sql/discoverer.go
@@ -6,6 +6,7 @@ import (
 	"strings"
 
 	"gfx.cafe/gfx/pggat/lib/gat/pool/recipe"
+	"gfx.cafe/gfx/pggat/lib/util/flip"
 
 	"github.com/caddyserver/caddy/v2"
 	sqladmin "google.golang.org/api/sqladmin/v1beta4"
@@ -134,27 +135,32 @@ func (T *Discoverer) instanceToCluster(primary *sqladmin.DatabaseInstance, repli
 			}
 
 			var result authQueryResult
-			client := new(gsql.Client)
-			err := gsql.ExtendedQuery(client, &result, "SELECT usename, passwd FROM pg_shadow WHERE usename=$1", user.Name)
-			if err != nil {
-				return discovery.Cluster{}, err
-			}
-			err = client.Close()
-			if err != nil {
-				return discovery.Cluster{}, err
-			}
 
-			initialPacket, err := client.ReadPacket(true, nil)
-			if err != nil {
-				return discovery.Cluster{}, err
-			}
-			_, err, err2 := bouncers.Bounce(fed.NewConn(client), admin, initialPacket)
-			if err != nil {
+			inward, outward := gsql.NewPair()
+
+			var b flip.Bank
+			b.Queue(func() error {
+				return gsql.ExtendedQuery(inward, &result, "SELECT usename, passwd FROM pg_shadow WHERE usename=$1", user.Name)
+			})
+
+			b.Queue(func() error {
+				initialPacket, err := outward.ReadPacket(true)
+				if err != nil {
+					return err
+				}
+				err, err2 := bouncers.Bounce(outward, admin, initialPacket)
+				if err != nil {
+					return err
+				}
+				if err2 != nil {
+					return err2
+				}
+				return outward.Close()
+			})
+
+			if err = b.Wait(); err != nil {
 				return discovery.Cluster{}, err
 			}
-			if err2 != nil {
-				return discovery.Cluster{}, err2
-			}
 
 			password = result.Password
 		}
diff --git a/lib/gat/handlers/discovery/module.go b/lib/gat/handlers/discovery/module.go
index bd6614d2e3f9350789e84dd0cb2637f060d85bbd..3652a482255fbb723c260cf9b57de265e4123248 100644
--- a/lib/gat/handlers/discovery/module.go
+++ b/lib/gat/handlers/discovery/module.go
@@ -556,7 +556,7 @@ func (T *Module) Handle(conn *fed.Conn) error {
 	return p.Serve(conn)
 }
 
-func (T *Module) Cancel(key [8]byte) {
+func (T *Module) Cancel(key fed.BackendKey) {
 	T.mu.RLock()
 	defer T.mu.RUnlock()
 	T.pools.Range(func(_ string, _ string, p pool.WithCredentials) bool {
diff --git a/lib/gat/handlers/pgbouncer/module.go b/lib/gat/handlers/pgbouncer/module.go
index a5a8f025fcdc70e1c491edd94bd21d854f08dc38..0dd0baafc43eef66cd60f726a7d85a8e60fcc24d 100644
--- a/lib/gat/handlers/pgbouncer/module.go
+++ b/lib/gat/handlers/pgbouncer/module.go
@@ -20,6 +20,7 @@ import (
 	"gfx.cafe/gfx/pggat/lib/gat/poolers/transaction"
 	"gfx.cafe/gfx/pggat/lib/perror"
 	"gfx.cafe/gfx/pggat/lib/util/dur"
+	"gfx.cafe/gfx/pggat/lib/util/flip"
 	"gfx.cafe/gfx/pggat/lib/util/slices"
 
 	"gfx.cafe/gfx/pggat/lib/auth/credentials"
@@ -107,19 +108,26 @@ func (T *Module) getPassword(user, database string) (string, bool) {
 		}
 
 		var result authQueryResult
-		client := new(gsql.Client)
-		err := gsql.ExtendedQuery(client, &result, T.Config.PgBouncer.AuthQuery, user)
-		if err != nil {
-			T.log.Warn("auth query failed", zap.Error(err))
-			return "", false
-		}
-		err = client.Close()
-		if err != nil {
-			T.log.Warn("auth query failed", zap.Error(err))
-			return "", false
-		}
-		err = authPool.ServeBot(client)
-		if err != nil && !errors.Is(err, io.EOF) {
+
+		var b flip.Bank
+
+		inward, outward := gsql.NewPair()
+		b.Queue(func() error {
+			if err := gsql.ExtendedQuery(inward, &result, T.Config.PgBouncer.AuthQuery, user); err != nil {
+				return err
+			}
+			return inward.Close()
+		})
+
+		b.Queue(func() error {
+			err := authPool.ServeBot(outward)
+			if err != nil && !errors.Is(err, io.EOF) {
+				return err
+			}
+			return nil
+		})
+
+		if err := b.Wait(); err != nil {
 			T.log.Warn("auth query failed", zap.Error(err))
 			return "", false
 		}
@@ -285,13 +293,7 @@ func (T *Module) lookup(user, database string) (pool.WithCredentials, bool) {
 func (T *Module) Handle(conn *fed.Conn) error {
 	// check ssl
 	if T.Config.PgBouncer.ClientTLSSSLMode.IsRequired() {
-		var ssl bool
-		netConn, ok := conn.ReadWriteCloser.(*fed.NetConn)
-		if ok {
-			ssl = netConn.SSL()
-		}
-
-		if !ssl {
+		if !conn.SSL {
 			return perror.New(
 				perror.FATAL,
 				perror.InvalidPassword,
@@ -346,7 +348,7 @@ func (T *Module) ReadMetrics(metrics *metrics.Handler) {
 	})
 }
 
-func (T *Module) Cancel(key [8]byte) {
+func (T *Module) Cancel(key fed.BackendKey) {
 	T.mu.RLock()
 	defer T.mu.RUnlock()
 	T.pools.Range(func(_ string, _ string, p pool.WithCredentials) bool {
diff --git a/lib/gat/handlers/pool/module.go b/lib/gat/handlers/pool/module.go
index 61b0085a7abd477aac62cd69d0d83d37df226374..ef57d02e42edbf73219a9b66155faaffd6fa7249 100644
--- a/lib/gat/handlers/pool/module.go
+++ b/lib/gat/handlers/pool/module.go
@@ -97,7 +97,7 @@ func (T *Module) Handle(conn *fed.Conn) error {
 	return T.pool.Serve(conn)
 }
 
-func (T *Module) Cancel(key [8]byte) {
+func (T *Module) Cancel(key fed.BackendKey) {
 	T.pool.Cancel(key)
 }
 
diff --git a/lib/gat/handlers/require_ssl/module.go b/lib/gat/handlers/require_ssl/module.go
index 47f7cb8e63bb3f6ce31fd810505154aa1817a941..476a8285e12db50108b43d5fb435aa1aa8f97bca 100644
--- a/lib/gat/handlers/require_ssl/module.go
+++ b/lib/gat/handlers/require_ssl/module.go
@@ -26,15 +26,8 @@ func (T *Module) CaddyModule() caddy.ModuleInfo {
 }
 
 func (T *Module) Handle(conn *fed.Conn) error {
-	var ssl bool
-
-	sslConn, ok := conn.ReadWriteCloser.(fed.SSL)
-	if ok {
-		ssl = sslConn.SSL()
-	}
-
 	if T.SSL {
-		if !ssl {
+		if !conn.SSL {
 			return perror.New(
 				perror.FATAL,
 				perror.InvalidPassword,
@@ -44,7 +37,7 @@ func (T *Module) Handle(conn *fed.Conn) error {
 		return nil
 	}
 
-	if ssl {
+	if conn.SSL {
 		return perror.New(
 			perror.FATAL,
 			perror.InvalidPassword,
diff --git a/lib/gat/listen.go b/lib/gat/listen.go
index baa1090cef04d4ea7c474b0e98d3807af28d2c0f..7204a61222fa347492060a8c196ee5d1b02be5f8 100644
--- a/lib/gat/listen.go
+++ b/lib/gat/listen.go
@@ -35,9 +35,7 @@ func (T *Listener) accept() (*fed.Conn, error) {
 	if err != nil {
 		return nil, err
 	}
-	return fed.NewConn(
-		fed.NewNetConn(raw),
-	), nil
+	return fed.NewConn(raw), nil
 }
 
 func (T *Listener) Provision(ctx caddy.Context) error {
diff --git a/lib/gat/matchers/localaddress.go b/lib/gat/matchers/localaddress.go
index c9ac3093f475bf7f3b6ddd3a9c6f128ff68f3933..920d2852733269c18e18f044df48528c5276e5b3 100644
--- a/lib/gat/matchers/localaddress.go
+++ b/lib/gat/matchers/localaddress.go
@@ -48,11 +48,7 @@ func (T *LocalAddress) Provision(ctx caddy.Context) error {
 }
 
 func (T *LocalAddress) Matches(conn *fed.Conn) bool {
-	netConn, ok := conn.ReadWriteCloser.(*fed.NetConn)
-	if !ok {
-		return false
-	}
-	switch addr := netConn.LocalAddr().(type) {
+	switch addr := conn.NetConn.LocalAddr().(type) {
 	case *net.TCPAddr:
 		expected, ok := T.addr.(*net.TCPAddr)
 		if !ok {
diff --git a/lib/gat/matchers/ssl.go b/lib/gat/matchers/ssl.go
index 57ae1a0acb907587d5c30070051af0a5a30d4017..255bde81dd008ff70c925f00fa9d3f76f9c722cb 100644
--- a/lib/gat/matchers/ssl.go
+++ b/lib/gat/matchers/ssl.go
@@ -25,11 +25,7 @@ func (T *SSL) CaddyModule() caddy.ModuleInfo {
 }
 
 func (T *SSL) Matches(conn *fed.Conn) bool {
-	sslConn, ok := conn.ReadWriteCloser.(fed.SSL)
-	if !ok {
-		return T.SSL == false
-	}
-	return sslConn.SSL() == T.SSL
+	return conn.SSL == T.SSL
 }
 
 var _ gat.Matcher = (*SSL)(nil)
diff --git a/lib/gat/pool/conn.go b/lib/gat/pool/conn.go
index 065a645895951e6693e756a2054e03f45f89b031..c1ea8981e07e6487e41bc72d0c49f126a1cb26d8 100644
--- a/lib/gat/pool/conn.go
+++ b/lib/gat/pool/conn.go
@@ -55,7 +55,7 @@ func (T *pooledConn) GetInitialParameters() map[strutil.CIString]string {
 	return T.conn.InitialParameters
 }
 
-func (T *pooledConn) GetBackendKey() [8]byte {
+func (T *pooledConn) GetBackendKey() fed.BackendKey {
 	return T.conn.BackendKey
 }
 
diff --git a/lib/gat/pool/flow.go b/lib/gat/pool/flow.go
index e12636210e444859639229cf308bb4e48c2b6cec..712255fcf206e81e5cfe29ee250290a0faa6d922 100644
--- a/lib/gat/pool/flow.go
+++ b/lib/gat/pool/flow.go
@@ -2,7 +2,6 @@ package pool
 
 import (
 	"gfx.cafe/gfx/pggat/lib/bouncer/backends/v0"
-	"gfx.cafe/gfx/pggat/lib/fed"
 	"gfx.cafe/gfx/pggat/lib/fed/middlewares/eqp"
 	"gfx.cafe/gfx/pggat/lib/fed/middlewares/ps"
 	packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
@@ -43,8 +42,6 @@ func syncInitialParameters(options Config, client *pooledClient, server *pooledS
 	clientParams := client.GetInitialParameters()
 	serverParams := server.GetInitialParameters()
 
-	var packet fed.Packet
-
 	for key, value := range clientParams {
 		// skip already set params
 		if serverParams[key] == value {
@@ -52,8 +49,7 @@ func syncInitialParameters(options Config, client *pooledClient, server *pooledS
 				Key:   key.String(),
 				Value: serverParams[key],
 			}
-			packet = p.IntoPacket(packet)
-			clientErr = client.GetConn().WritePacket(packet)
+			clientErr = client.GetConn().WritePacket(&p)
 			if clientErr != nil {
 				return
 			}
@@ -70,8 +66,7 @@ func syncInitialParameters(options Config, client *pooledClient, server *pooledS
 			Key:   key.String(),
 			Value: value,
 		}
-		packet = p.IntoPacket(packet)
-		clientErr = client.GetConn().WritePacket(packet)
+		clientErr = client.GetConn().WritePacket(&p)
 		if clientErr != nil {
 			return
 		}
@@ -80,7 +75,7 @@ func syncInitialParameters(options Config, client *pooledClient, server *pooledS
 			continue
 		}
 
-		serverErr, _, packet = backends.SetParameter(server.GetConn(), nil, packet, key, value)
+		serverErr, _ = backends.SetParameter(server.GetConn(), nil, key, value)
 		if serverErr != nil {
 			return
 		}
@@ -98,8 +93,7 @@ func syncInitialParameters(options Config, client *pooledClient, server *pooledS
 			Key:   key.String(),
 			Value: value,
 		}
-		packet = p.IntoPacket(packet)
-		clientErr = client.GetConn().WritePacket(packet)
+		clientErr = client.GetConn().WritePacket(&p)
 		if clientErr != nil {
 			return
 		}
diff --git a/lib/gat/pool/pool.go b/lib/gat/pool/pool.go
index 8399e39b80bb5ba6e913de1415d8a4dbafc9b7c1..258ced2197dc5d878d51b13bbd5770168ed4c767 100644
--- a/lib/gat/pool/pool.go
+++ b/lib/gat/pool/pool.go
@@ -29,7 +29,7 @@ type Pool struct {
 	recipes          map[string]*Recipe
 	recipeScaleOrder slices.Sorted[string]
 	clients          map[uuid.UUID]*pooledClient
-	clientsByKey     map[[8]byte]*pooledClient
+	clientsByKey     map[fed.BackendKey]*pooledClient
 	servers          map[uuid.UUID]*pooledServer
 	serversByRecipe  map[string][]*pooledServer
 	mu               sync.RWMutex
@@ -265,7 +265,7 @@ func (T *Pool) releaseServer(server *pooledServer) {
 	if T.config.ServerResetQuery != "" {
 		server.SetState(metrics.ConnStateRunningResetQuery, uuid.Nil)
 
-		err, _, _ := backends.QueryString(server.GetConn(), nil, nil, T.config.ServerResetQuery)
+		err, _ := backends.QueryString(server.GetConn(), nil, T.config.ServerResetQuery)
 		if err != nil {
 			T.removeServer(server)
 			return
@@ -295,7 +295,7 @@ func (T *Pool) Serve(
 // ServeBot is for clients that don't need initial parameters, cancelling queries, and are ready now. Use Serve for
 // real clients
 func (T *Pool) ServeBot(
-	conn fed.ReadWriteCloser,
+	conn *fed.Conn,
 ) error {
 	defer func() {
 		_ = conn.Close()
@@ -303,9 +303,7 @@ func (T *Pool) ServeBot(
 
 	client := newClient(
 		T.config,
-		&fed.Conn{
-			ReadWriteCloser: conn,
-		},
+		conn,
 	)
 
 	return T.serve(client, true)
@@ -330,8 +328,6 @@ func (T *Pool) serve(client *pooledClient, initialized bool) error {
 		}
 	}()
 
-	var packet fed.Packet
-
 	if !initialized {
 		server = T.acquireServer(client)
 		if server == nil {
@@ -347,8 +343,7 @@ func (T *Pool) serve(client *pooledClient, initialized bool) error {
 		}
 
 		p := packets.ReadyForQuery('I')
-		packet = p.IntoPacket(packet)
-		err = client.GetConn().WritePacket(packet)
+		err = client.GetConn().WritePacket(&p)
 		if err != nil {
 			return err
 		}
@@ -361,7 +356,8 @@ func (T *Pool) serve(client *pooledClient, initialized bool) error {
 			server = nil
 		}
 
-		packet, err = client.GetConn().ReadPacket(true, packet)
+		var packet fed.Packet
+		packet, err = client.GetConn().ReadPacket(true)
 		if err != nil {
 			return err
 		}
@@ -375,8 +371,9 @@ func (T *Pool) serve(client *pooledClient, initialized bool) error {
 			err, serverErr = pair(T.config, client, server)
 		}
 		if err == nil && serverErr == nil {
-			packet, err, serverErr = bouncers.Bounce(client.GetConn(), server.GetConn(), packet)
+			err, serverErr = bouncers.Bounce(client.GetConn(), server.GetConn(), packet)
 		}
+
 		if serverErr != nil {
 			return serverErr
 		} else {
@@ -398,7 +395,7 @@ func (T *Pool) addClient(client *pooledClient) {
 	}
 	T.clients[client.GetID()] = client
 	if T.clientsByKey == nil {
-		T.clientsByKey = make(map[[8]byte]*pooledClient)
+		T.clientsByKey = make(map[fed.BackendKey]*pooledClient)
 	}
 	T.clientsByKey[client.GetBackendKey()] = client
 	T.pooler.AddClient(client.GetID())
@@ -418,7 +415,7 @@ func (T *Pool) removeClientL1(client *pooledClient) {
 	delete(T.clientsByKey, client.GetBackendKey())
 }
 
-func (T *Pool) Cancel(key [8]byte) {
+func (T *Pool) Cancel(key fed.BackendKey) {
 	T.mu.RLock()
 	defer T.mu.RUnlock()
 
diff --git a/lib/gat/pool/recipe/dialer.go b/lib/gat/pool/recipe/dialer.go
index 8202c2b9a6b1633fb835e7c092d9e8a84757ee69..b11ac3a98efea8fea1c4ea03079f4104705ad94d 100644
--- a/lib/gat/pool/recipe/dialer.go
+++ b/lib/gat/pool/recipe/dialer.go
@@ -28,9 +28,7 @@ func (T Dialer) Dial() (*fed.Conn, error) {
 	if err != nil {
 		return nil, err
 	}
-	conn := fed.NewConn(
-		fed.NewNetConn(c),
-	)
+	conn := fed.NewConn(c)
 	conn.User = T.Username
 	conn.Database = T.Database
 	err = backends.Accept(
@@ -48,14 +46,12 @@ func (T Dialer) Dial() (*fed.Conn, error) {
 	return conn, nil
 }
 
-func (T Dialer) Cancel(key [8]byte) {
+func (T Dialer) Cancel(key fed.BackendKey) {
 	c, err := net.Dial(T.Network, T.Address)
 	if err != nil {
 		return
 	}
-	conn := fed.NewConn(
-		fed.NewNetConn(c),
-	)
+	conn := fed.NewConn(c)
 	defer func() {
 		_ = conn.Close()
 	}()
@@ -64,5 +60,5 @@ func (T Dialer) Cancel(key [8]byte) {
 	}
 
 	// wait for server to close the connection, this means that the server received it ok
-	_, _ = conn.ReadPacket(true, nil)
+	_, _ = conn.ReadPacket(true)
 }
diff --git a/lib/gat/pool/recipe/recipe.go b/lib/gat/pool/recipe/recipe.go
index aeb25e76eb3a4a81b6c4061df7a615274ffe8b7e..78a79391ad159a1e286ac6b0cd69b7aa7f86ea45 100644
--- a/lib/gat/pool/recipe/recipe.go
+++ b/lib/gat/pool/recipe/recipe.go
@@ -70,6 +70,6 @@ func (T *Recipe) Dial() (*fed.Conn, error) {
 	return T.config.Dialer.Dial()
 }
 
-func (T *Recipe) Cancel(key [8]byte) {
+func (T *Recipe) Cancel(key fed.BackendKey) {
 	T.config.Dialer.Cancel(key)
 }
diff --git a/lib/gat/poolers/session/pooler.go b/lib/gat/poolers/session/pooler.go
index 8f0dd004ce3ab9caf066e670325726eaf8508f23..18385ba9fbf9ef4c6d5c724abc4e3f7e883c3716 100644
--- a/lib/gat/poolers/session/pooler.go
+++ b/lib/gat/poolers/session/pooler.go
@@ -12,7 +12,7 @@ import (
 type Pooler struct {
 	queue   []uuid.UUID
 	servers map[uuid.UUID]struct{}
-	ready   *sync.Cond
+	ready   sync.Cond
 	closed  bool
 	mu      sync.Mutex
 }
@@ -38,9 +38,10 @@ func (T *Pooler) AddServer(server uuid.UUID) {
 	}
 	T.servers[server] = struct{}{}
 
-	if T.ready != nil {
-		T.ready.Signal()
+	if T.ready.L == nil {
+		T.ready.L = &T.mu
 	}
+	T.ready.Signal()
 }
 
 func (T *Pooler) DeleteServer(server uuid.UUID) {
@@ -79,8 +80,8 @@ func (T *Pooler) AcquireBlocking() uuid.UUID {
 	}
 
 	for len(T.queue) == 0 {
-		if T.ready == nil {
-			T.ready = sync.NewCond(&T.mu)
+		if T.ready.L == nil {
+			T.ready.L = &T.mu
 		}
 		T.ready.Wait()
 	}
@@ -122,9 +123,10 @@ func (T *Pooler) Close() {
 	defer T.mu.Unlock()
 
 	T.closed = true
-	if T.ready != nil {
-		T.ready.Broadcast()
+	if T.ready.L == nil {
+		T.ready.L = &T.mu
 	}
+	T.ready.Broadcast()
 }
 
 var _ pool.Pooler = (*Pooler)(nil)
diff --git a/lib/gat/server.go b/lib/gat/server.go
index d31a0f6d593725e6214bbc6a9c98772a21d16a9d..81a1cd58e605f87d638697f36e5e08aedf3d2f2c 100644
--- a/lib/gat/server.go
+++ b/lib/gat/server.go
@@ -12,7 +12,6 @@ import (
 
 	"gfx.cafe/gfx/pggat/lib/bouncer/frontends/v0"
 	"gfx.cafe/gfx/pggat/lib/fed"
-	packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
 	"gfx.cafe/gfx/pggat/lib/gat/metrics"
 	"gfx.cafe/gfx/pggat/lib/perror"
 )
@@ -95,7 +94,7 @@ func (T *Server) Stop() error {
 	return nil
 }
 
-func (T *Server) Cancel(key [8]byte) {
+func (T *Server) Cancel(key fed.BackendKey) {
 	for _, cancellableHandler := range T.cancellableHandlers {
 		cancellableHandler.Cancel(key)
 	}
@@ -123,23 +122,21 @@ func (T *Server) Serve(conn *fed.Conn) {
 				return
 			}
 
-			errResp := packets.ErrorResponse{
-				Error: perror.Wrap(err),
-			}
-			_ = conn.WritePacket(errResp.IntoPacket(nil))
+			errResp := perror.ToPacket(perror.Wrap(err))
+			_ = conn.WritePacket(errResp)
 			return
 		}
 	}
 
 	// database not found
-	errResp := packets.ErrorResponse{
-		Error: perror.New(
+	errResp := perror.ToPacket(
+		perror.New(
 			perror.FATAL,
 			perror.InvalidPassword,
 			fmt.Sprintf(`Database "%s" not found`, conn.Database),
 		),
-	}
-	_ = conn.WritePacket(errResp.IntoPacket(nil))
+	)
+	_ = conn.WritePacket(errResp)
 	T.log.Warn("database not found", zap.String("user", conn.User), zap.String("database", conn.Database))
 }
 
@@ -153,7 +150,7 @@ func (T *Server) accept(listener *Listener, conn *fed.Conn) {
 		tlsConfig = listener.ssl.ServerTLSConfig()
 	}
 
-	var cancelKey [8]byte
+	var cancelKey fed.BackendKey
 	var isCanceling bool
 	var err error
 	cancelKey, isCanceling, err = frontends.Accept(conn, tlsConfig)
diff --git a/lib/gsql/addr.go b/lib/gsql/addr.go
deleted file mode 100644
index 61bcf5d2439817290ec46ef99a1ed926d9003004..0000000000000000000000000000000000000000
--- a/lib/gsql/addr.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package gsql
-
-import "net"
-
-type Addr struct{}
-
-func (Addr) Network() string {
-	return "gsql"
-}
-
-func (Addr) String() string {
-	return "local gsql client"
-}
-
-var _ net.Addr = Addr{}
diff --git a/lib/gsql/client.go b/lib/gsql/client.go
deleted file mode 100644
index de0a0630725fab6bf7b678fe490196a813a992f3..0000000000000000000000000000000000000000
--- a/lib/gsql/client.go
+++ /dev/null
@@ -1,146 +0,0 @@
-package gsql
-
-import (
-	"io"
-	"net"
-	"sync"
-
-	"gfx.cafe/gfx/pggat/lib/fed"
-	"gfx.cafe/gfx/pggat/lib/util/ring"
-	"gfx.cafe/gfx/pggat/lib/util/slices"
-)
-
-type batch struct {
-	result  ResultWriter
-	packets []fed.Packet
-}
-
-type Client struct {
-	write ResultWriter
-	read  ring.Ring[fed.Packet]
-
-	queue ring.Ring[batch]
-
-	closed bool
-	mu     sync.Mutex
-
-	readC  *sync.Cond
-	writeC *sync.Cond
-}
-
-func (T *Client) Do(result ResultWriter, packets ...fed.Packet) {
-	T.mu.Lock()
-	defer T.mu.Unlock()
-
-	T.queue.PushBack(batch{
-		result:  result,
-		packets: packets,
-	})
-
-	if T.readC != nil {
-		T.readC.Broadcast()
-	}
-}
-
-func (T *Client) queueNext() bool {
-	b, ok := T.queue.PopFront()
-	if ok {
-		for _, packet := range b.packets {
-			T.read.PushBack(packet)
-		}
-		T.write = b.result
-		if T.writeC != nil {
-			T.writeC.Broadcast()
-		}
-		return true
-	}
-
-	return false
-}
-
-func (T *Client) ReadPacket(typed bool, buffer fed.Packet) (packet fed.Packet, err error) {
-	packet = buffer
-
-	T.mu.Lock()
-	defer T.mu.Unlock()
-
-	var p fed.Packet
-	for {
-		var ok bool
-		p, ok = T.read.PopFront()
-		if ok {
-			break
-		}
-
-		// try to add next in queue
-		if T.queueNext() {
-			continue
-		}
-
-		if T.closed {
-			err = io.EOF
-			return
-		}
-
-		if T.readC == nil {
-			T.readC = sync.NewCond(&T.mu)
-		}
-		T.readC.Wait()
-	}
-
-	if (p.Type() == 0 && typed) || (p.Type() != 0 && !typed) {
-		err = ErrTypedMismatch
-		return
-	}
-
-	packet = slices.Resize(packet, len(p))
-	copy(packet, p)
-	return
-}
-
-func (T *Client) WritePacket(packet fed.Packet) error {
-	T.mu.Lock()
-	defer T.mu.Unlock()
-
-	for T.write == nil {
-		if T.read.Length() == 0 && T.queueNext() {
-			continue
-		}
-
-		if T.closed {
-			return io.EOF
-		}
-
-		if T.writeC == nil {
-			T.writeC = sync.NewCond(&T.mu)
-		}
-		T.writeC.Wait()
-	}
-
-	if err := T.write.WritePacket(packet); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func (T *Client) Close() error {
-	T.mu.Lock()
-	defer T.mu.Unlock()
-
-	if T.closed {
-		return net.ErrClosed
-	}
-
-	T.closed = true
-
-	if T.writeC != nil {
-		T.writeC.Broadcast()
-	}
-	if T.readC != nil {
-		T.readC.Broadcast()
-	}
-	return nil
-}
-
-var _ fed.ReadWriteCloser = (*Client)(nil)
diff --git a/lib/gsql/eq.go b/lib/gsql/eq.go
index 7908d0a819d20fe147ff594113385a2d5a1acd22..3cfc8d49ecec61e2a664687bdb6124f475610a69 100644
--- a/lib/gsql/eq.go
+++ b/lib/gsql/eq.go
@@ -8,19 +8,18 @@ import (
 	packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
 )
 
-func ExtendedQuery(client *Client, result any, query string, args ...any) error {
+func ExtendedQuery(client *fed.Conn, result any, query string, args ...any) error {
 	if len(args) == 0 {
-		Query(client, []any{result}, query)
-		return nil
+		return Query(client, []any{result}, query)
 	}
 
-	var pkts []fed.Packet
-
 	// parse
 	parse := packets.Parse{
 		Query: query,
 	}
-	pkts = append(pkts, parse.IntoPacket(nil))
+	if err := client.WritePacket(&parse); err != nil {
+		return err
+	}
 
 	// bind
 	params := make([][]byte, 0, len(args))
@@ -58,25 +57,46 @@ outer:
 		params = append(params, value)
 	}
 	bind := packets.Bind{
-		ParameterValues: params,
+		Parameters: params,
+	}
+	if err := client.WritePacket(&bind); err != nil {
+		return err
 	}
-	pkts = append(pkts, bind.IntoPacket(nil))
 
 	// describe
 	describe := packets.Describe{
 		Which: 'P',
 	}
-	pkts = append(pkts, describe.IntoPacket(nil))
+	if err := client.WritePacket(&describe); err != nil {
+		return err
+	}
 
 	// execute
 	execute := packets.Execute{}
-	pkts = append(pkts, execute.IntoPacket(nil))
+	if err := client.WritePacket(&execute); err != nil {
+		return err
+	}
 
 	// sync
-	sync := fed.NewPacket(packets.TypeSync)
-	pkts = append(pkts, sync)
+	sync := packets.Sync{}
+	if err := client.WritePacket(&sync); err != nil {
+		return err
+	}
 
 	// result
-	client.Do(NewQueryWriter(result), pkts...)
+	if err := readQueryResults(client, result); err != nil {
+		return err
+	}
+
+	// make sure we receive ready for query
+	packet, err := client.ReadPacket(true)
+	if err != nil {
+		return err
+	}
+
+	if packet.Type() != packets.TypeReadyForQuery {
+		return ErrExpectedReadyForQuery
+	}
+
 	return nil
 }
diff --git a/lib/gsql/errors.go b/lib/gsql/errors.go
index a550f7496561030d181eedbcbc8ba281007e2d61..86c95c7766102bd9c30a5b3d629e45f449ed31a9 100644
--- a/lib/gsql/errors.go
+++ b/lib/gsql/errors.go
@@ -3,9 +3,9 @@ package gsql
 import "errors"
 
 var (
-	ErrResultTooBig       = errors.New("got too many rows for result")
-	ErrExtraFields        = errors.New("received unexpected fields")
-	ErrResultMustBeNonNil = errors.New("result must be non nil")
-	ErrUnexpectedType     = errors.New("unexpected result type")
-	ErrTypedMismatch      = errors.New("tried to read typed packet as untyped or untyped packet as typed")
+	ErrResultTooBig          = errors.New("got too many rows for result")
+	ErrExtraFields           = errors.New("received unexpected fields")
+	ErrResultMustBeNonNil    = errors.New("result must be non nil")
+	ErrUnexpectedType        = errors.New("unexpected result type")
+	ErrExpectedReadyForQuery = errors.New("expected query to end with ReadyForQuery")
 )
diff --git a/lib/gsql/pair.go b/lib/gsql/pair.go
new file mode 100644
index 0000000000000000000000000000000000000000..8355d5d13bb0e2726b516efe399dab2ff47e528b
--- /dev/null
+++ b/lib/gsql/pair.go
@@ -0,0 +1,14 @@
+package gsql
+
+import (
+	"gfx.cafe/gfx/pggat/lib/fed"
+	"gfx.cafe/gfx/pggat/lib/util/mio"
+)
+
+func NewPair() (*fed.Conn, *fed.Conn) {
+	conn := new(mio.Conn)
+	inward := fed.NewConn(mio.InwardConn{Conn: conn})
+	outward := fed.NewConn(mio.OutwardConn{Conn: conn})
+
+	return inward, outward
+}
diff --git a/lib/gsql/query.go b/lib/gsql/query.go
index c4c8f7f4bade875322000a62aadea58dbe3d0b60..73e236a386bbd023e719cb68197bcc2e0703b699 100644
--- a/lib/gsql/query.go
+++ b/lib/gsql/query.go
@@ -5,44 +5,34 @@ import (
 	packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
 )
 
-func Query(client *Client, results []any, query string) {
+func Query(client *fed.Conn, results []any, query string) error {
 	var q = packets.Query(query)
-
-	client.Do(NewQueryWriter(results...), q.IntoPacket(nil))
-}
-
-type QueryWriter struct {
-	writers   []RowWriter
-	writerNum int
-}
-
-func NewQueryWriter(results ...any) *QueryWriter {
-	var writers = make([]RowWriter, 0, len(results))
-	for _, result := range results {
-		writers = append(writers, MakeRowWriter(result))
-	}
-
-	return &QueryWriter{
-		writers: writers,
+	if err := client.WritePacket(&q); err != nil {
+		return err
 	}
-}
 
-func (T *QueryWriter) WritePacket(packet fed.Packet) error {
-	if T.writerNum >= len(T.writers) {
-		// ignore
-		return nil
+	if err := readQueryResults(client, results...); err != nil {
+		return err
 	}
 
-	result := &T.writers[T.writerNum]
-	if err := result.WritePacket(packet); err != nil {
+	// make sure we receive ready for query
+	packet, err := client.ReadPacket(true)
+	if err != nil {
 		return err
 	}
 
-	if result.Done() {
-		T.writerNum++
+	if packet.Type() != packets.TypeReadyForQuery {
+		return ErrExpectedReadyForQuery
 	}
 
 	return nil
 }
 
-var _ ResultWriter = (*QueryWriter)(nil)
+func readQueryResults(client *fed.Conn, results ...any) error {
+	for _, result := range results {
+		if err := readRows(client, result); err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/lib/gsql/query_test.go b/lib/gsql/query_test.go
index 320bf4d5d048e267b97b92692472d56f84e83985..ed9606eb297b20425f26456047c37869c6974115 100644
--- a/lib/gsql/query_test.go
+++ b/lib/gsql/query_test.go
@@ -3,12 +3,15 @@ package gsql
 import (
 	"log"
 	"net"
+	"net/http"
+	_ "net/http/pprof"
 	"testing"
 
 	"gfx.cafe/gfx/pggat/lib/auth/credentials"
 	"gfx.cafe/gfx/pggat/lib/bouncer/backends/v0"
 	"gfx.cafe/gfx/pggat/lib/bouncer/bouncers/v2"
 	"gfx.cafe/gfx/pggat/lib/fed"
+	"gfx.cafe/gfx/pggat/lib/util/flip"
 )
 
 type Result struct {
@@ -17,13 +20,17 @@ type Result struct {
 }
 
 func TestQuery(t *testing.T) {
+	go func() {
+		panic(http.ListenAndServe(":8080", nil))
+	}()
+
 	// open server
 	s, err := net.Dial("tcp", "localhost:5432")
 	if err != nil {
 		t.Error(err)
 		return
 	}
-	server := fed.NewConn(fed.NewNetConn(s))
+	server := fed.NewConn(s)
 	err = backends.Accept(
 		server,
 		"",
@@ -41,30 +48,33 @@ func TestQuery(t *testing.T) {
 		return
 	}
 
+	inward, outward := NewPair()
+
 	var res Result
-	client := new(Client)
-	err = ExtendedQuery(client, &res, "SELECT usename, passwd FROM pg_shadow WHERE usename=$1", "postgres")
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	err = client.Close()
-	if err != nil {
-		t.Error(err)
-	}
 
-	var initial fed.Packet
-	initial, err = client.ReadPacket(true, initial)
-	if err != nil {
+	var b flip.Bank
+	b.Queue(func() error {
+		return ExtendedQuery(inward, &res, "SELECT usename, passwd FROM pg_shadow WHERE usename=$1", "postgres")
+	})
+
+	b.Queue(func() error {
+		initial, err := outward.ReadPacket(true)
+		if err != nil {
+			return err
+		}
+		clientErr, serverErr := bouncers.Bounce(outward, server, initial)
+		if clientErr != nil {
+			return clientErr
+		}
+		if serverErr != nil {
+			return serverErr
+		}
+		return outward.Close()
+	})
+
+	if err = b.Wait(); err != nil {
 		t.Error(err)
 	}
-	_, clientErr, serverErr := bouncers.Bounce(fed.NewConn(client), server, initial)
-	if clientErr != nil {
-		t.Error(clientErr)
-	}
-	if serverErr != nil {
-		t.Error(serverErr)
-	}
 
 	log.Printf("%#v", res)
 }
diff --git a/lib/gsql/result.go b/lib/gsql/result.go
deleted file mode 100644
index 11c084500333e529c726cf7bdd05a63b97eef2e7..0000000000000000000000000000000000000000
--- a/lib/gsql/result.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package gsql
-
-import "gfx.cafe/gfx/pggat/lib/fed"
-
-type ResultWriter interface {
-	fed.Writer
-}
diff --git a/lib/gsql/row.go b/lib/gsql/row.go
index 574e8e7738cc52e972109effc9411636fd60ddf6..c6da94d94aeab937a61e55a36de4e0e1dae634cc 100644
--- a/lib/gsql/row.go
+++ b/lib/gsql/row.go
@@ -1,39 +1,61 @@
 package gsql
 
 import (
-	"errors"
 	"reflect"
 	"strconv"
 
 	"gfx.cafe/gfx/pggat/lib/fed"
 	packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
+	"gfx.cafe/gfx/pggat/lib/perror"
 )
 
-type RowWriter struct {
-	result reflect.Value
-	rd     packets.RowDescription
-	row    int
-	done   bool
-}
+func readRows(client *fed.Conn, result any) error {
+	res := reflect.ValueOf(result)
+	row := 0
+	var rd packets.RowDescription
 
-func MakeRowWriter(result any) RowWriter {
-	return RowWriter{
-		result: reflect.ValueOf(result),
-	}
-}
+	for {
+		packet, err := client.ReadPacket(true)
+		if err != nil {
+			return err
+		}
 
-func NewRowWriter(result any) *RowWriter {
-	w := MakeRowWriter(result)
-	return &w
+		switch packet.Type() {
+		case packets.TypeRowDescription:
+			err = fed.ToConcrete(&rd, packet)
+			if err != nil {
+				return err
+			}
+		case packets.TypeDataRow:
+			var dr packets.DataRow
+			err = fed.ToConcrete(&dr, packet)
+			if err != nil {
+				return err
+			}
+			for i, col := range dr {
+				if err = setColumn(res, rd, row, i, col); err != nil {
+					return err
+				}
+			}
+			row += 1
+		case packets.TypeErrorResponse:
+			var p packets.ErrorResponse
+			err = fed.ToConcrete(&p, packet)
+			if err != nil {
+				return err
+			}
+			return perror.FromPacket(&p)
+		case packets.TypeCommandComplete:
+			return nil
+		}
+	}
 }
 
-func (T *RowWriter) set(i int, col []byte) error {
-	if i >= len(T.rd.Fields) {
+func setColumn(result reflect.Value, rd packets.RowDescription, row, i int, col []byte) error {
+	if i >= len(rd) {
 		return ErrExtraFields
 	}
-	desc := T.rd.Fields[i]
-
-	result := T.result
+	desc := rd[i]
 
 	// unptr
 	for result.Kind() == reflect.Pointer {
@@ -52,22 +74,22 @@ outer:
 		kind := result.Kind()
 		switch kind {
 		case reflect.Array:
-			if T.row >= result.Len() {
+			if row >= result.Len() {
 				return ErrResultTooBig
 			}
-			result = result.Index(T.row)
+			result = result.Index(row)
 			break outer
 		case reflect.Slice:
-			for T.row >= result.Len() {
+			for row >= result.Len() {
 				if !result.CanSet() {
 					return ErrResultTooBig
 				}
 				result.Set(reflect.Append(result, reflect.Zero(result.Type().Elem())))
 			}
-			result = result.Index(T.row)
+			result = result.Index(row)
 			break outer
 		case reflect.Struct, reflect.Map:
-			if T.row != 0 {
+			if row != 0 {
 				return ErrResultTooBig
 			}
 			break outer
@@ -224,39 +246,3 @@ outer2:
 		return ErrUnexpectedType
 	}
 }
-
-func (T *RowWriter) WritePacket(packet fed.Packet) error {
-	switch packet.Type() {
-	case packets.TypeRowDescription:
-		if !T.rd.ReadFromPacket(packet) {
-			return errors.New("invalid format")
-		}
-	case packets.TypeDataRow:
-		var dr packets.DataRow
-		if !dr.ReadFromPacket(packet) {
-			return errors.New("invalid format")
-		}
-		for i, col := range dr.Columns {
-			if err := T.set(i, col); err != nil {
-				return err
-			}
-		}
-		T.row += 1
-	case packets.TypeErrorResponse:
-		var err packets.ErrorResponse
-		if !err.ReadFromPacket(packet) {
-			return errors.New("invalid format")
-		}
-		return err.Error
-	case packets.TypeCommandComplete:
-		T.done = true
-		return nil
-	}
-	return nil
-}
-
-func (T *RowWriter) Done() bool {
-	return T.done
-}
-
-var _ ResultWriter = (*RowWriter)(nil)
diff --git a/lib/perror/packet.go b/lib/perror/packet.go
new file mode 100644
index 0000000000000000000000000000000000000000..606c1071ee62e6fd38263b74ec8e96e9dcc1cd6e
--- /dev/null
+++ b/lib/perror/packet.go
@@ -0,0 +1,60 @@
+package perror
+
+import packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
+
+func FromPacket(packet *packets.ErrorResponse) Error {
+	var severity Severity
+	var code Code
+	var message string
+	var extra []ExtraField
+
+	for _, field := range *packet {
+		switch field.Code {
+		case 'S':
+			severity = Severity(field.Value)
+		case 'C':
+			code = Code(field.Value)
+		case 'M':
+			message = field.Value
+		default:
+			extra = append(extra, ExtraField{
+				Type:  Extra(field.Code),
+				Value: field.Value,
+			})
+		}
+	}
+
+	return New(
+		severity,
+		code,
+		message,
+		extra...,
+	)
+}
+
+func ToPacket(err Error) *packets.ErrorResponse {
+	var resp packets.ErrorResponse
+	resp = append(
+		resp,
+		packets.ErrorResponseField{
+			Code:  'S',
+			Value: string(err.Severity()),
+		},
+		packets.ErrorResponseField{
+			Code:  'C',
+			Value: string(err.Code()),
+		},
+		packets.ErrorResponseField{
+			Code:  'M',
+			Value: err.Message(),
+		},
+	)
+	extra := err.Extra()
+	for _, field := range extra {
+		resp = append(resp, packets.ErrorResponseField{
+			Code:  uint8(field.Type),
+			Value: field.Value,
+		})
+	}
+	return &resp
+}
diff --git a/lib/util/flip/bank.go b/lib/util/flip/bank.go
index 12273bb9a6885049c0640cd136bbf2fab94cb09e..3880be76c7e2d41299f354cda47ef1cb5ea85d39 100644
--- a/lib/util/flip/bank.go
+++ b/lib/util/flip/bank.go
@@ -1,57 +1,29 @@
 package flip
 
-import (
-	"sync"
-)
-
-type Bank struct {
-	pending []func() error
-	mu      sync.Mutex
-}
+type Bank []func() error
 
 func (T *Bank) Queue(fn func() error) {
-	T.mu.Lock()
-	defer T.mu.Unlock()
-	T.pending = append(T.pending, fn)
-}
-
-func (T *Bank) take() []func() error {
-	T.mu.Lock()
-	defer T.mu.Unlock()
-	v := T.pending
-	T.pending = nil
-	return v
-}
-
-func (T *Bank) give(sz []func() error) {
-	T.mu.Lock()
-	defer T.mu.Unlock()
-	if T.pending == nil {
-		T.pending = sz[:0]
-	}
+	*T = append(*T, fn)
 }
 
 func (T *Bank) Wait() error {
-	batch := T.take()
-	defer T.give(batch)
-
-	if len(batch) == 0 {
+	if len(*T) == 0 {
 		return nil
 	}
 
-	if len(batch) == 1 {
-		return batch[0]()
+	if len(*T) == 1 {
+		return (*T)[0]()
 	}
 
-	ch := make(chan error, len(batch))
+	ch := make(chan error, len(*T))
 
-	for _, pending := range batch {
+	for _, pending := range *T {
 		go func(pending func() error) {
 			ch <- pending()
 		}(pending)
 	}
 
-	for i := 0; i < len(batch); i++ {
+	for i := 0; i < len(*T); i++ {
 		err := <-ch
 		if err != nil {
 			return err
diff --git a/lib/util/mio/conn.go b/lib/util/mio/conn.go
new file mode 100644
index 0000000000000000000000000000000000000000..6117bc7cd8a5137e00b0808c6926a5634f6c0028
--- /dev/null
+++ b/lib/util/mio/conn.go
@@ -0,0 +1,121 @@
+package mio
+
+import (
+	"fmt"
+	"net"
+	"time"
+)
+
+type Conn struct {
+	out ReadWriteCloser
+	in  ReadWriteCloser
+}
+
+func (T *Conn) Close() error {
+	if err := T.out.Close(); err != nil {
+		return err
+	}
+	return T.in.Close()
+}
+
+type OutwardConn struct {
+	*Conn
+}
+
+func (T OutwardConn) Read(b []byte) (n int, err error) {
+	return T.out.Read(b)
+}
+
+func (T OutwardConn) Write(b []byte) (n int, err error) {
+	return T.in.Write(b)
+}
+
+func (T OutwardConn) LocalAddr() net.Addr {
+	return ConnAddr{
+		Outward: true,
+		Conn:    T.Conn,
+	}
+}
+
+func (T OutwardConn) RemoteAddr() net.Addr {
+	return ConnAddr{
+		Outward: false,
+		Conn:    T.Conn,
+	}
+}
+
+func (T OutwardConn) SetDeadline(t time.Time) error {
+	if err := T.SetReadDeadline(t); err != nil {
+		return err
+	}
+
+	return T.SetWriteDeadline(t)
+}
+
+func (T OutwardConn) SetReadDeadline(t time.Time) error {
+	return T.out.SetReadDeadline(t)
+}
+
+func (T OutwardConn) SetWriteDeadline(t time.Time) error {
+	return T.in.SetWriteDeadline(t)
+}
+
+type InwardConn struct {
+	*Conn
+}
+
+func (T InwardConn) Read(b []byte) (n int, err error) {
+	return T.in.Read(b)
+}
+
+func (T InwardConn) Write(b []byte) (n int, err error) {
+	return T.out.Write(b)
+}
+
+func (T InwardConn) LocalAddr() net.Addr {
+	return ConnAddr{
+		Outward: false,
+		Conn:    T.Conn,
+	}
+}
+
+func (T InwardConn) RemoteAddr() net.Addr {
+	return ConnAddr{
+		Outward: true,
+		Conn:    T.Conn,
+	}
+}
+
+func (T InwardConn) SetDeadline(t time.Time) error {
+	if err := T.SetReadDeadline(t); err != nil {
+		return err
+	}
+
+	return T.SetWriteDeadline(t)
+}
+
+func (T InwardConn) SetReadDeadline(t time.Time) error {
+	return T.in.SetReadDeadline(t)
+}
+
+func (T InwardConn) SetWriteDeadline(t time.Time) error {
+	return T.out.SetWriteDeadline(t)
+}
+
+var _ net.Conn = OutwardConn{}
+var _ net.Conn = InwardConn{}
+
+type ConnAddr struct {
+	Outward bool
+	Conn    *Conn
+}
+
+func (T ConnAddr) Network() string {
+	return "mio"
+}
+
+func (T ConnAddr) String() string {
+	return fmt.Sprintf("memory conn(%p)", T.Conn)
+}
+
+var _ net.Addr = ConnAddr{}
diff --git a/lib/util/mio/listener.go b/lib/util/mio/listener.go
new file mode 100644
index 0000000000000000000000000000000000000000..6c2d373530e8f5556b493caf5ea6239c8a7d22c7
--- /dev/null
+++ b/lib/util/mio/listener.go
@@ -0,0 +1,144 @@
+package mio
+
+import (
+	"errors"
+	"fmt"
+	"net"
+	"sync"
+
+	"gfx.cafe/gfx/pggat/lib/util/ring"
+)
+
+var (
+	listeners   map[string]*Listener
+	listenersMu sync.RWMutex
+)
+
+type Listener struct {
+	address  string
+	incoming ring.Ring[*Conn]
+	all      []*Conn
+	a        sync.Cond
+	closed   bool
+	mu       sync.Mutex
+}
+
+func Listen(address string) (*Listener, error) {
+	listenersMu.Lock()
+	defer listenersMu.Unlock()
+
+	if _, ok := listeners[address]; ok {
+		return nil, errors.New("address already in use")
+	}
+
+	l := &Listener{
+		address: address,
+	}
+	if listeners == nil {
+		listeners = make(map[string]*Listener)
+	}
+	listeners[address] = l
+
+	return l, nil
+}
+
+func (T *Listener) Accept() (net.Conn, error) {
+	T.mu.Lock()
+	defer T.mu.Unlock()
+
+	for T.incoming.Length() == 0 {
+		if T.closed {
+			return nil, net.ErrClosed
+		}
+
+		if T.a.L == nil {
+			T.a.L = &T.mu
+		}
+		T.a.Wait()
+	}
+
+	c, _ := T.incoming.PopFront()
+	return OutwardConn{Conn: c}, nil
+}
+
+func (T *Listener) close() error {
+	T.mu.Lock()
+	defer T.mu.Unlock()
+
+	if T.closed {
+		return net.ErrClosed
+	}
+	T.closed = true
+	for _, c := range T.all {
+		_ = c.Close()
+	}
+
+	return nil
+}
+
+func (T *Listener) Close() error {
+	if err := T.close(); err != nil {
+		return err
+	}
+
+	listenersMu.Lock()
+	defer listenersMu.Unlock()
+
+	delete(listeners, T.address)
+
+	return nil
+}
+
+func (T *Listener) Addr() net.Addr {
+	return ListenerAddr{
+		Listener: T,
+	}
+}
+
+var _ net.Listener = (*Listener)(nil)
+
+func lookup(address string) *Listener {
+	listenersMu.RLock()
+	defer listenersMu.RUnlock()
+
+	l, _ := listeners[address]
+	return l
+}
+
+func Dial(address string) (net.Conn, error) {
+	l := lookup(address)
+
+	if l == nil {
+		return nil, errors.New("address not found")
+	}
+
+	l.mu.Lock()
+	defer l.mu.Unlock()
+
+	c := new(Conn)
+
+	l.all = append(l.all, c)
+	l.incoming.PushBack(c)
+	if l.a.L == nil {
+		l.a.L = &l.mu
+	}
+	l.a.Signal()
+
+	return OutwardConn{
+		Conn: c,
+	}, nil
+}
+
+type ListenerAddr struct {
+	Listener *Listener
+}
+
+func (T ListenerAddr) Network() string {
+	return "mio"
+}
+
+func (T ListenerAddr) String() string {
+	return fmt.Sprintf("memory listener(%p)", T.Listener)
+}
+
+var _ net.Addr = ListenerAddr{}
diff --git a/lib/util/mio/readwritecloser.go b/lib/util/mio/readwritecloser.go
new file mode 100644
index 0000000000000000000000000000000000000000..9acb2a4abfb58223a1465ccb007d22c173146d6c
--- /dev/null
+++ b/lib/util/mio/readwritecloser.go
@@ -0,0 +1,132 @@
+package mio
+
+import (
+	"context"
+	"io"
+	"net"
+	"sync"
+	"time"
+)
+
+type ReadWriteCloser struct {
+	buf               []byte
+	r                 sync.Cond
+	closed            bool
+	readDeadline      time.Time
+	readDeadlineTimer *time.Timer
+	mu                sync.Mutex
+}
+
+func NewReadWriteCloserSize(size int) *ReadWriteCloser {
+	return &ReadWriteCloser{
+		buf: make([]byte, 0, size),
+	}
+}
+
+func (T *ReadWriteCloser) Read(b []byte) (n int, err error) {
+	T.mu.Lock()
+	defer T.mu.Unlock()
+
+	for len(T.buf) == 0 {
+		if T.closed {
+			return 0, io.EOF
+		}
+
+		if T.readDeadline != (time.Time{}) && time.Now().After(T.readDeadline) {
+			return 0, context.DeadlineExceeded
+		}
+
+		if T.r.L == nil {
+			T.r.L = &T.mu
+		}
+		T.r.Wait()
+	}
+
+	n = copy(b, T.buf)
+	copy(T.buf, T.buf[n:])
+	T.buf = T.buf[:len(T.buf)-n]
+
+	return
+}
+
+func (T *ReadWriteCloser) Write(b []byte) (n int, err error) {
+	T.mu.Lock()
+	defer T.mu.Unlock()
+
+	if T.closed {
+		return 0, net.ErrClosed
+	}
+
+	T.buf = append(T.buf, b...)
+	n = len(b)
+
+	if T.r.L == nil {
+		T.r.L = &T.mu
+	}
+	T.r.Broadcast()
+
+	return
+}
+
+func (T *ReadWriteCloser) Close() error {
+	T.mu.Lock()
+	defer T.mu.Unlock()
+
+	if T.closed {
+		return net.ErrClosed
+	}
+	T.closed = true
+
+	if T.readDeadlineTimer != nil {
+		T.readDeadlineTimer.Stop()
+	}
+
+	if T.r.L == nil {
+		T.r.L = &T.mu
+	}
+	T.r.Broadcast()
+
+	return nil
+}
+
+func (T *ReadWriteCloser) readDeadlineExceeded() {
+	T.mu.Lock()
+	defer T.mu.Unlock()
+
+	if T.r.L == nil {
+		T.r.L = &T.mu
+	}
+	T.r.Broadcast()
+}
+
+func (T *ReadWriteCloser) SetReadDeadline(t time.Time) error {
+	T.mu.Lock()
+	defer T.mu.Unlock()
+
+	if T.closed {
+		return net.ErrClosed
+	}
+
+	if t == (time.Time{}) {
+		if T.readDeadlineTimer != nil {
+			T.readDeadlineTimer.Stop()
+		}
+	} else {
+		if T.readDeadlineTimer == nil {
+			T.readDeadlineTimer = time.AfterFunc(t.Sub(time.Now()), T.readDeadlineExceeded)
+		} else {
+			T.readDeadlineTimer.Reset(t.Sub(time.Now()))
+		}
+	}
+
+	return nil
+}
+
+func (T *ReadWriteCloser) SetWriteDeadline(_ time.Time) error {
+	// all writes happen without blocking
+	return nil
+}
+
+func (T *ReadWriteCloser) SetDeadline(t time.Time) error {
+	return T.SetReadDeadline(t)
+}
diff --git a/pgbouncer.ini b/pgbouncer.ini
index 428dbd8b0a5b045ffedfa82d061e6dfccc05ecf3..73dbd84942fafe96e5d09957cb032810930329e1 100644
--- a/pgbouncer.ini
+++ b/pgbouncer.ini
@@ -7,4 +7,4 @@ auth_user = postgres
 server_idle_timeout = 10
 
 [databases]
-* = host=/tmp/ datestyle=Postgres,MDY timezone=PST8PDT
+* = host=localhost datestyle=Postgres,MDY timezone=PST8PDT
diff --git a/test/capturer.go b/test/capturer.go
deleted file mode 100644
index 3bdc1e3a7fb37763f31566ca47f87552417d7dc1..0000000000000000000000000000000000000000
--- a/test/capturer.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package test
-
-import (
-	"bytes"
-	"fmt"
-
-	"gfx.cafe/gfx/pggat/lib/fed"
-	"gfx.cafe/gfx/pggat/lib/gsql"
-)
-
-type Capturer struct {
-	Packets []fed.Packet
-}
-
-func (T *Capturer) WritePacket(packet fed.Packet) error {
-	T.Packets = append(T.Packets, bytes.Clone(packet))
-	return nil
-}
-
-func (T *Capturer) Check(other *Capturer) error {
-	if len(T.Packets) != len(other.Packets) {
-		return fmt.Errorf("wrong number of packets! got %d but expected %d", len(other.Packets), len(T.Packets))
-	}
-
-	for i := range T.Packets {
-		expected := T.Packets[i]
-		actual := other.Packets[i]
-
-		if !bytes.Equal(expected.Bytes(), actual.Bytes()) {
-			return fmt.Errorf("mismatched packet! expected %v but got %v", expected.Bytes(), actual.Bytes())
-		}
-	}
-
-	return nil
-}
-
-var _ gsql.ResultWriter = (*Capturer)(nil)
diff --git a/test/runner.go b/test/runner.go
index d23e94c631e58064f1d933793dcb7a782834a316..c1f24ccc563ed15570d3eb9d676e0a22651716c1 100644
--- a/test/runner.go
+++ b/test/runner.go
@@ -1,12 +1,14 @@
 package test
 
 import (
+	"bytes"
 	"errors"
 	"fmt"
 	"io"
 
 	"gfx.cafe/gfx/pggat/lib/bouncer/bouncers/v2"
 	"gfx.cafe/gfx/pggat/lib/fed"
+	"gfx.cafe/gfx/pggat/lib/fed/middlewares/unterminate"
 	packets "gfx.cafe/gfx/pggat/lib/fed/packets/v3.0"
 	"gfx.cafe/gfx/pggat/lib/gat/pool/recipe"
 	"gfx.cafe/gfx/pggat/lib/gsql"
@@ -26,67 +28,91 @@ func MakeRunner(config Config, test Test) Runner {
 	}
 }
 
-func (T *Runner) prepare(client *gsql.Client, until int) []Capturer {
-	results := make([]Capturer, until)
-
+func (T *Runner) prepare(client *fed.Conn, until int) error {
 	for i := 0; i < until; i++ {
 		x := T.test.Instructions[i]
 		switch v := x.(type) {
 		case inst.SimpleQuery:
 			q := packets.Query(v)
-			client.Do(&results[i], q.IntoPacket(nil))
+			if err := client.WritePacket(&q); err != nil {
+				return err
+			}
 		case inst.Sync:
-			client.Do(&results[i], fed.NewPacket(packets.TypeSync))
+			if err := client.WritePacket(&packets.Sync{}); err != nil {
+				return err
+			}
 		case inst.Parse:
 			p := packets.Parse{
 				Destination: v.Destination,
 				Query:       v.Query,
 			}
-			client.Do(&results[i], p.IntoPacket(nil))
+			if err := client.WritePacket(&p); err != nil {
+				return err
+			}
 		case inst.Bind:
 			p := packets.Bind{
 				Destination: v.Destination,
 				Source:      v.Source,
 			}
-			client.Do(&results[i], p.IntoPacket(nil))
+			if err := client.WritePacket(&p); err != nil {
+				return err
+			}
 		case inst.DescribePortal:
 			p := packets.Describe{
-				Which:  'P',
-				Target: string(v),
+				Which: 'P',
+				Name:  string(v),
+			}
+			if err := client.WritePacket(&p); err != nil {
+				return err
 			}
-			client.Do(&results[i], p.IntoPacket(nil))
 		case inst.DescribePreparedStatement:
 			p := packets.Describe{
-				Which:  'S',
-				Target: string(v),
+				Which: 'S',
+				Name:  string(v),
+			}
+			if err := client.WritePacket(&p); err != nil {
+				return err
 			}
-			client.Do(&results[i], p.IntoPacket(nil))
 		case inst.Execute:
 			p := packets.Execute{
 				Target: string(v),
 			}
-			client.Do(&results[i], p.IntoPacket(nil))
+			if err := client.WritePacket(&p); err != nil {
+				return err
+			}
 		case inst.ClosePortal:
 			p := packets.Close{
-				Which:  'P',
-				Target: string(v),
+				Which: 'P',
+				Name:  string(v),
+			}
+			if err := client.WritePacket(&p); err != nil {
+				return err
 			}
-			client.Do(&results[i], p.IntoPacket(nil))
 		case inst.ClosePreparedStatement:
 			p := packets.Close{
-				Which:  'S',
-				Target: string(v),
+				Which: 'S',
+				Name:  string(v),
+			}
+			if err := client.WritePacket(&p); err != nil {
+				return err
 			}
-			client.Do(&results[i], p.IntoPacket(nil))
 		case inst.CopyData:
 			p := packets.CopyData(v)
-			client.Do(&results[i], p.IntoPacket(nil))
+			if err := client.WritePacket(&p); err != nil {
+				return err
+			}
 		case inst.CopyDone:
-			client.Do(&results[i], fed.NewPacket(packets.TypeCopyDone))
+			if err := client.WritePacket(&packets.CopyDone{}); err != nil {
+				return err
+			}
 		}
 	}
 
-	return results
+	if err := client.WritePacket(&packets.Terminate{}); err != nil {
+		return err
+	}
+
+	return client.Flush()
 }
 
 func (T *Runner) runModeL1(dialer recipe.Dialer, client *fed.Conn) error {
@@ -98,9 +124,11 @@ func (T *Runner) runModeL1(dialer recipe.Dialer, client *fed.Conn) error {
 		_ = server.Close()
 	}()
 
+	client.Middleware = append(client.Middleware, unterminate.Unterminate)
+
 	for {
 		var p fed.Packet
-		p, err = client.ReadPacket(true, p)
+		p, err = client.ReadPacket(true)
 		if err != nil {
 			if errors.Is(err, io.EOF) {
 				break
@@ -108,7 +136,7 @@ func (T *Runner) runModeL1(dialer recipe.Dialer, client *fed.Conn) error {
 			return err
 		}
 
-		_, clientErr, serverErr := bouncers.Bounce(client, server, p)
+		clientErr, serverErr := bouncers.Bounce(client, server, p)
 		if clientErr != nil {
 			return clientErr
 		}
@@ -120,29 +148,31 @@ func (T *Runner) runModeL1(dialer recipe.Dialer, client *fed.Conn) error {
 	return nil
 }
 
-func (T *Runner) runModeOnce(dialer recipe.Dialer) ([]Capturer, error) {
-	var client gsql.Client
-	results := T.prepare(&client, len(T.test.Instructions))
-	if err := client.Close(); err != nil {
+func (T *Runner) runModeOnce(dialer recipe.Dialer) ([]byte, error) {
+	inward, outward := gsql.NewPair()
+	if err := T.prepare(inward, len(T.test.Instructions)); err != nil {
+		return nil, err
+	}
+
+	if err := T.runModeL1(dialer, outward); err != nil {
 		return nil, err
 	}
 
-	if err := T.runModeL1(dialer, fed.NewConn(&client)); err != nil {
+	if err := inward.Close(); err != nil {
 		return nil, err
 	}
 
-	return results, nil
+	return io.ReadAll(inward.NetConn)
 }
 
 func (T *Runner) runModeFail(dialer recipe.Dialer) error {
 	for i := 1; i < len(T.test.Instructions)+1; i++ {
-		var client gsql.Client
-		T.prepare(&client, i)
-		if err := client.Close(); err != nil {
+		inward, outward := gsql.NewPair()
+		if err := T.prepare(inward, i); err != nil {
 			return err
 		}
 
-		if err := T.runModeL1(dialer, fed.NewConn(&client)); err != nil && !errors.Is(err, io.EOF) {
+		if err := T.runModeL1(dialer, outward); err != nil && !errors.Is(err, io.EOF) {
 			return err
 		}
 	}
@@ -150,7 +180,7 @@ func (T *Runner) runModeFail(dialer recipe.Dialer) error {
 	return nil
 }
 
-func (T *Runner) runMode(dialer recipe.Dialer) ([]Capturer, error) {
+func (T *Runner) runMode(dialer recipe.Dialer) ([]byte, error) {
 	instances := T.config.Stress
 	if instances < 1 || T.test.SideEffects {
 		return T.runModeOnce(dialer)
@@ -175,14 +205,8 @@ func (T *Runner) runMode(dialer recipe.Dialer) ([]Capturer, error) {
 			if err != nil {
 				return err
 			}
-			if len(expected) != len(actual) {
-				return fmt.Errorf("wrong number of results! expected %d but got %d", len(expected), len(actual))
-			}
-			for i, exp := range expected {
-				act := actual[i]
-				if err = exp.Check(&act); err != nil {
-					return err
-				}
+			if !bytes.Equal(expected, actual) {
+				return fmt.Errorf("mismatched results: expected %v but got %v", expected, actual)
 			}
 			return nil
 		})
@@ -198,7 +222,7 @@ func (T *Runner) runMode(dialer recipe.Dialer) ([]Capturer, error) {
 func (T *Runner) Run() error {
 	var errs []error
 
-	var expected []Capturer
+	var expected []byte
 
 	// modes
 	for name, mode := range T.config.Modes {
@@ -216,30 +240,13 @@ func (T *Runner) Run() error {
 			continue
 		}
 
-		if len(expected) != len(actual) {
+		if !bytes.Equal(expected, actual) {
 			errs = append(errs, ErrorIn{
 				Name: name,
-				Err:  fmt.Errorf("wrong number of results! expected %d but got %d", len(expected), len(actual)),
+				Err:  fmt.Errorf("mismatched results: expected %v but got %v", expected, actual),
 			})
 			continue
 		}
-
-		var modeErrs []error
-
-		for i, exp := range expected {
-			act := actual[i]
-
-			if err = exp.Check(&act); err != nil {
-				modeErrs = append(modeErrs, fmt.Errorf("instruction %d: %s", i+1, err.Error()))
-			}
-		}
-
-		if len(modeErrs) > 0 {
-			errs = append(errs, ErrorIn{
-				Name: name,
-				Err:  Errors(modeErrs),
-			})
-		}
 	}
 
 	if len(errs) > 0 {