diff --git a/cmd/cgat/main.go b/cmd/cgat/main.go
index 8c6df416433e6ebf3744935080fd350e26898037..20108d1978150527696a92dbf065d2c383eaea22 100644
--- a/cmd/cgat/main.go
+++ b/cmd/cgat/main.go
@@ -8,17 +8,16 @@ import (
 	"pggat2/lib/bouncer/backends/v0"
 	"pggat2/lib/bouncer/bouncers/v0"
 	"pggat2/lib/bouncer/frontends/v0"
-	"pggat2/lib/middleware/middlewares/eqp"
 	"pggat2/lib/middleware/middlewares/unread"
 	"pggat2/lib/middleware/middlewares/unterminate"
-	"pggat2/lib/pnet"
-	"pggat2/lib/pnet/pio"
 	"pggat2/lib/rob"
 	"pggat2/lib/rob/schedulers/v2"
+	"pggat2/lib/zap"
+	"pggat2/lib/zap/zio"
 )
 
 type job struct {
-	client pnet.ReadWriter
+	client zap.ReadWriter
 	done   chan<- struct{}
 }
 
@@ -27,13 +26,12 @@ func testServer(r rob.Scheduler) {
 	if err != nil {
 		panic(err)
 	}
-	server := pio.MakeReadWriter(conn)
+	server := zio.MakeReadWriter(conn)
 	backends.Accept(&server)
-	consumer := eqp.MakeConsumer(&server)
 	sink := r.NewSink(0)
 	for {
 		j := sink.Read().(job)
-		bouncers.Bounce(j.client, &consumer)
+		bouncers.Bounce(j.client, &server)
 		select {
 		case j.done <- struct{}{}:
 		default:
@@ -60,14 +58,13 @@ func main() {
 		}
 		go func() {
 			source := r.NewSource()
-			client := pio.MakeReadWriter(conn)
+			client := zio.MakeReadWriter(conn)
 			ut := unterminate.MakeUnterminate(&client)
 			frontends.Accept(ut)
-			creator := eqp.MakeCreator(ut)
 			done := make(chan struct{})
 			defer close(done)
 			for {
-				u, err := unread.NewUnread(&creator)
+				u, err := unread.NewUnread(&ut)
 				if err != nil {
 					break
 				}
diff --git a/lib/bouncer/backends/v0/accept.go b/lib/bouncer/backends/v0/accept.go
index a1d5d0a6f043170023c07b17f20e25309d439828..65d6ede76b27c6666324a3c1bf3ee514973af5bb 100644
--- a/lib/bouncer/backends/v0/accept.go
+++ b/lib/bouncer/backends/v0/accept.go
@@ -6,9 +6,8 @@ import (
 	"pggat2/lib/auth/md5"
 	"pggat2/lib/auth/sasl"
 	"pggat2/lib/perror"
-	"pggat2/lib/pnet"
-	"pggat2/lib/pnet/packet"
-	packets "pggat2/lib/pnet/packet/packets/v3.0"
+	"pggat2/lib/zap"
+	packets "pggat2/lib/zap/packets/v3.0"
 )
 
 type Status int
@@ -23,22 +22,22 @@ var (
 	ErrBadPacket     = errors.New("bad packet")
 )
 
-func fail(server pnet.ReadWriter, err error) {
+func fail(server zap.ReadWriter, err error) {
 	panic(err)
 }
 
-func failpg(server pnet.ReadWriter, err perror.Error) {
+func failpg(server zap.ReadWriter, err perror.Error) {
 	panic(err)
 }
 
-func authenticationSASLChallenge(server pnet.ReadWriter, mechanism sasl.Client) (done bool, status Status) {
+func authenticationSASLChallenge(server zap.ReadWriter, mechanism sasl.Client) (done bool, status Status) {
 	in, err := server.Read()
 	if err != nil {
 		fail(server, err)
 		return false, Fail
 	}
 
-	if in.Type() != packet.Authentication {
+	if in.Type() != packets.Authentication {
 		fail(server, ErrProtocolError)
 		return false, Fail
 	}
@@ -61,7 +60,7 @@ func authenticationSASLChallenge(server pnet.ReadWriter, mechanism sasl.Client)
 		out := server.Write()
 		packets.WriteAuthenticationResponse(out, response)
 
-		err = server.Send(out.Finish())
+		err = server.Send(out)
 		if err != nil {
 			fail(server, err)
 			return false, Fail
@@ -82,7 +81,7 @@ func authenticationSASLChallenge(server pnet.ReadWriter, mechanism sasl.Client)
 	}
 }
 
-func authenticationSASL(server pnet.ReadWriter, mechanisms []string, username, password string) Status {
+func authenticationSASL(server zap.ReadWriter, mechanisms []string, username, password string) Status {
 	mechanism, err := sasl.NewClient(mechanisms, username, password)
 	if err != nil {
 		fail(server, err)
@@ -92,7 +91,7 @@ func authenticationSASL(server pnet.ReadWriter, mechanisms []string, username, p
 
 	out := server.Write()
 	packets.WriteSASLInitialResponse(out, mechanism.Name(), initialResponse)
-	err = server.Send(out.Finish())
+	err = server.Send(out)
 	if err != nil {
 		fail(server, err)
 		return Fail
@@ -112,10 +111,10 @@ func authenticationSASL(server pnet.ReadWriter, mechanisms []string, username, p
 	return Ok
 }
 
-func authenticationMD5(server pnet.ReadWriter, salt [4]byte, username, password string) Status {
+func authenticationMD5(server zap.ReadWriter, salt [4]byte, username, password string) Status {
 	out := server.Write()
 	packets.WritePasswordMessage(out, md5.Encode(username, password, salt))
-	err := server.Send(out.Finish())
+	err := server.Send(out)
 	if err != nil {
 		fail(server, err)
 		return Fail
@@ -123,10 +122,10 @@ func authenticationMD5(server pnet.ReadWriter, salt [4]byte, username, password
 	return Ok
 }
 
-func authenticationCleartext(server pnet.ReadWriter, password string) Status {
+func authenticationCleartext(server zap.ReadWriter, password string) Status {
 	out := server.Write()
 	packets.WritePasswordMessage(out, password)
-	err := server.Send(out.Finish())
+	err := server.Send(out)
 	if err != nil {
 		fail(server, err)
 		return Fail
@@ -134,7 +133,7 @@ func authenticationCleartext(server pnet.ReadWriter, password string) Status {
 	return Ok
 }
 
-func startup0(server pnet.ReadWriter, username, password string) (done bool, status Status) {
+func startup0(server zap.ReadWriter, username, password string) (done bool, status Status) {
 	in, err := server.Read()
 	if err != nil {
 		fail(server, err)
@@ -142,7 +141,7 @@ func startup0(server pnet.ReadWriter, username, password string) (done bool, sta
 	}
 
 	switch in.Type() {
-	case packet.ErrorResponse:
+	case packets.ErrorResponse:
 		perr, ok := packets.ReadErrorResponse(in)
 		if !ok {
 			fail(server, ErrBadPacket)
@@ -150,7 +149,7 @@ func startup0(server pnet.ReadWriter, username, password string) (done bool, sta
 		}
 		failpg(server, perr)
 		return false, Fail
-	case packet.Authentication:
+	case packets.Authentication:
 		method, ok := in.Int32()
 		if !ok {
 			fail(server, ErrBadPacket)
@@ -195,7 +194,7 @@ func startup0(server pnet.ReadWriter, username, password string) (done bool, sta
 			fail(server, errors.New("unknown authentication method"))
 			return false, Fail
 		}
-	case packet.NegotiateProtocolVersion:
+	case packets.NegotiateProtocolVersion:
 		// we only support protocol 3.0 for now
 		fail(server, errors.New("server wanted to negotiate protocol version"))
 		return false, Fail
@@ -205,7 +204,7 @@ func startup0(server pnet.ReadWriter, username, password string) (done bool, sta
 	}
 }
 
-func startup1(server pnet.ReadWriter) (done bool, status Status) {
+func startup1(server zap.ReadWriter) (done bool, status Status) {
 	in, err := server.Read()
 	if err != nil {
 		fail(server, err)
@@ -213,7 +212,7 @@ func startup1(server pnet.ReadWriter) (done bool, status Status) {
 	}
 
 	switch in.Type() {
-	case packet.BackendKeyData:
+	case packets.BackendKeyData:
 		var cancellationKey [8]byte
 		ok := in.Bytes(cancellationKey[:])
 		if !ok {
@@ -222,11 +221,11 @@ func startup1(server pnet.ReadWriter) (done bool, status Status) {
 		}
 		// TODO(garet) put cancellation key somewhere
 		return false, Ok
-	case packet.ParameterStatus:
+	case packets.ParameterStatus:
 		return false, Ok
-	case packet.ReadyForQuery:
+	case packets.ReadyForQuery:
 		return true, Ok
-	case packet.ErrorResponse:
+	case packets.ErrorResponse:
 		perr, ok := packets.ReadErrorResponse(in)
 		if !ok {
 			fail(server, ErrBadPacket)
@@ -234,7 +233,7 @@ func startup1(server pnet.ReadWriter) (done bool, status Status) {
 		}
 		failpg(server, perr)
 		return false, Fail
-	case packet.NoticeResponse:
+	case packets.NoticeResponse:
 		// TODO(garet) do something with notice
 		return false, Ok
 	default:
@@ -243,7 +242,7 @@ func startup1(server pnet.ReadWriter) (done bool, status Status) {
 	}
 }
 
-func Accept(server pnet.ReadWriter) {
+func Accept(server zap.ReadWriter) {
 	// we can re-use the memory for this pkt most of the way down because we don't pass this anywhere
 	out := server.Write()
 	out.Int16(3)
@@ -255,7 +254,7 @@ func Accept(server pnet.ReadWriter) {
 	out.String("uniswap")
 	out.String("")
 
-	err := server.Send(out.Finish())
+	err := server.Send(out)
 	if err != nil {
 		fail(server, err)
 		return
diff --git a/lib/bouncer/bouncers/v0/bouncer.go b/lib/bouncer/bouncers/v0/bouncer.go
index 250e90173863496ad7a13ccaf1b7e5ce3a3de875..5af359f57c676e631c2f37dd9427efe757fb65a3 100644
--- a/lib/bouncer/bouncers/v0/bouncer.go
+++ b/lib/bouncer/bouncers/v0/bouncer.go
@@ -6,9 +6,8 @@ import (
 	"runtime/debug"
 
 	"pggat2/lib/perror"
-	"pggat2/lib/pnet"
-	"pggat2/lib/pnet/packet"
-	packets "pggat2/lib/pnet/packet/packets/v3.0"
+	"pggat2/lib/zap"
+	"pggat2/lib/zap/packets/v3.0"
 )
 
 type Status int
@@ -18,21 +17,21 @@ const (
 	Ok
 )
 
-func clientFail(client pnet.ReadWriter, err perror.Error) {
+func clientFail(client zap.ReadWriter, err perror.Error) {
 	// DEBUG(garet)
 	log.Println("client fail", err)
 	debug.PrintStack()
 
 	out := client.Write()
 	packets.WriteErrorResponse(out, err)
-	_ = client.Send(out.Finish())
+	_ = client.Send(out)
 }
 
-func serverFail(server pnet.ReadWriter, err error) {
+func serverFail(server zap.ReadWriter, err error) {
 	panic(err)
 }
 
-func copyIn0(client, server pnet.ReadWriter) (done bool, status Status) {
+func copyIn0(client, server zap.ReadWriter) (done bool, status Status) {
 	in, err := client.Read()
 	if err != nil {
 		clientFail(client, perror.Wrap(err))
@@ -40,29 +39,29 @@ func copyIn0(client, server pnet.ReadWriter) (done bool, status Status) {
 	}
 
 	switch in.Type() {
-	case packet.CopyData:
-		err = pnet.ProxyPacket(server, in)
+	case packets.CopyData:
+		err = server.Send(zap.InToOut(in))
 		if err != nil {
 			serverFail(server, err)
 			return false, Fail
 		}
 		return true, Ok
-	case packet.CopyDone, packet.CopyFail:
-		err = pnet.ProxyPacket(server, in)
+	case packets.CopyDone, packets.CopyFail:
+		err = server.Send(zap.InToOut(in))
 		if err != nil {
 			serverFail(server, err)
 			return false, Fail
 		}
 		return true, Ok
 	default:
-		clientFail(client, pnet.ErrProtocolError)
+		clientFail(client, packets.ErrUnexpectedPacket)
 		return false, Fail
 	}
 }
 
-func copyIn(client, server pnet.ReadWriter, in packet.In) (status Status) {
+func copyIn(client, server zap.ReadWriter, in zap.In) (status Status) {
 	// send in (copyInResponse) to client
-	err := pnet.ProxyPacket(client, in)
+	err := client.Send(zap.InToOut(in))
 	if err != nil {
 		clientFail(client, perror.Wrap(err))
 		return Fail
@@ -82,7 +81,7 @@ func copyIn(client, server pnet.ReadWriter, in packet.In) (status Status) {
 	return Ok
 }
 
-func copyOut0(client, server pnet.ReadWriter) (done bool, status Status) {
+func copyOut0(client, server zap.ReadWriter) (done bool, status Status) {
 	in, err := server.Read()
 	if err != nil {
 		serverFail(server, err)
@@ -90,15 +89,15 @@ func copyOut0(client, server pnet.ReadWriter) (done bool, status Status) {
 	}
 
 	switch in.Type() {
-	case packet.CopyData:
-		err = pnet.ProxyPacket(client, in)
+	case packets.CopyData:
+		err = client.Send(zap.InToOut(in))
 		if err != nil {
 			clientFail(client, perror.Wrap(err))
 			return false, Fail
 		}
 		return false, Ok
-	case packet.CopyDone, packet.ErrorResponse:
-		err = pnet.ProxyPacket(client, in)
+	case packets.CopyDone, packets.ErrorResponse:
+		err = client.Send(zap.InToOut(in))
 		if err != nil {
 			clientFail(client, perror.Wrap(err))
 			return false, Fail
@@ -110,9 +109,9 @@ func copyOut0(client, server pnet.ReadWriter) (done bool, status Status) {
 	}
 }
 
-func copyOut(client, server pnet.ReadWriter, in packet.In) (status Status) {
+func copyOut(client, server zap.ReadWriter, in zap.In) (status Status) {
 	// send in (copyOutResponse) to client
-	err := pnet.ProxyPacket(client, in)
+	err := client.Send(zap.InToOut(in))
 	if err != nil {
 		clientFail(client, perror.Wrap(err))
 		return Fail
@@ -132,7 +131,7 @@ func copyOut(client, server pnet.ReadWriter, in packet.In) (status Status) {
 	return Ok
 }
 
-func query0(client, server pnet.ReadWriter) (done bool, status Status) {
+func query0(client, server zap.ReadWriter) (done bool, status Status) {
 	in, err := server.Read()
 	if err != nil {
 		serverFail(server, err)
@@ -140,33 +139,33 @@ func query0(client, server pnet.ReadWriter) (done bool, status Status) {
 	}
 
 	switch in.Type() {
-	case packet.CommandComplete,
-		packet.RowDescription,
-		packet.DataRow,
-		packet.EmptyQueryResponse,
-		packet.ErrorResponse,
-		packet.NoticeResponse,
-		packet.ParameterStatus:
-		err = pnet.ProxyPacket(client, in)
+	case packets.CommandComplete,
+		packets.RowDescription,
+		packets.DataRow,
+		packets.EmptyQueryResponse,
+		packets.ErrorResponse,
+		packets.NoticeResponse,
+		packets.ParameterStatus:
+		err = client.Send(zap.InToOut(in))
 		if err != nil {
 			clientFail(client, perror.Wrap(err))
 			return false, Fail
 		}
 		return false, Ok
-	case packet.CopyInResponse:
+	case packets.CopyInResponse:
 		status = copyIn(client, server, in)
 		if status != Ok {
 			return false, status
 		}
 		return false, Ok
-	case packet.CopyOutResponse:
+	case packets.CopyOutResponse:
 		status = copyOut(client, server, in)
 		if status != Ok {
 			return false, status
 		}
 		return false, Ok
-	case packet.ReadyForQuery:
-		err = pnet.ProxyPacket(client, in)
+	case packets.ReadyForQuery:
+		err = client.Send(zap.InToOut(in))
 		if err != nil {
 			clientFail(client, perror.Wrap(err))
 			return false, Fail
@@ -178,9 +177,9 @@ func query0(client, server pnet.ReadWriter) (done bool, status Status) {
 	}
 }
 
-func query(client, server pnet.ReadWriter, in packet.In) (status Status) {
+func query(client, server zap.ReadWriter, in zap.In) (status Status) {
 	// send in (initial query) to server
-	err := pnet.ProxyPacket(server, in)
+	err := server.Send(zap.InToOut(in))
 	if err != nil {
 		serverFail(server, err)
 		return Fail
@@ -199,7 +198,7 @@ func query(client, server pnet.ReadWriter, in packet.In) (status Status) {
 	return Ok
 }
 
-func functionCall0(client, server pnet.ReadWriter) (done bool, status Status) {
+func functionCall0(client, server zap.ReadWriter) (done bool, status Status) {
 	in, err := server.Read()
 	if err != nil {
 		serverFail(server, err)
@@ -207,15 +206,15 @@ func functionCall0(client, server pnet.ReadWriter) (done bool, status Status) {
 	}
 
 	switch in.Type() {
-	case packet.ErrorResponse, packet.FunctionCallResponse, packet.NoticeResponse:
-		err = pnet.ProxyPacket(client, in)
+	case packets.ErrorResponse, packets.FunctionCallResponse, packets.NoticeResponse:
+		err = client.Send(zap.InToOut(in))
 		if err != nil {
 			clientFail(client, perror.Wrap(err))
 			return false, Fail
 		}
 		return false, Ok
-	case packet.ReadyForQuery:
-		err = pnet.ProxyPacket(client, in)
+	case packets.ReadyForQuery:
+		err = client.Send(zap.InToOut(in))
 		if err != nil {
 			clientFail(client, perror.Wrap(err))
 			return false, Fail
@@ -227,9 +226,9 @@ func functionCall0(client, server pnet.ReadWriter) (done bool, status Status) {
 	}
 }
 
-func functionCall(client, server pnet.ReadWriter, in packet.In) (status Status) {
+func functionCall(client, server zap.ReadWriter, in zap.In) (status Status) {
 	// send in (FunctionCall) to server
-	err := pnet.ProxyPacket(server, in)
+	err := server.Send(zap.InToOut(in))
 	if err != nil {
 		serverFail(server, err)
 		return Fail
@@ -248,7 +247,7 @@ func functionCall(client, server pnet.ReadWriter, in packet.In) (status Status)
 	return Ok
 }
 
-func sync0(client, server pnet.ReadWriter) (done bool, status Status) {
+func sync0(client, server zap.ReadWriter) (done bool, status Status) {
 	in, err := server.Read()
 	if err != nil {
 		serverFail(server, err)
@@ -256,27 +255,27 @@ func sync0(client, server pnet.ReadWriter) (done bool, status Status) {
 	}
 
 	switch in.Type() {
-	case packet.ParseComplete,
-		packet.BindComplete,
-		packet.ErrorResponse,
-		packet.RowDescription,
-		packet.NoData,
-		packet.ParameterDescription,
+	case packets.ParseComplete,
+		packets.BindComplete,
+		packets.ErrorResponse,
+		packets.RowDescription,
+		packets.NoData,
+		packets.ParameterDescription,
 
-		packet.CommandComplete,
-		packet.DataRow,
-		packet.EmptyQueryResponse,
-		packet.NoticeResponse,
-		packet.ParameterStatus,
-		packet.PortalSuspended:
-		err = pnet.ProxyPacket(client, in)
+		packets.CommandComplete,
+		packets.DataRow,
+		packets.EmptyQueryResponse,
+		packets.NoticeResponse,
+		packets.ParameterStatus,
+		packets.PortalSuspended:
+		err = client.Send(zap.InToOut(in))
 		if err != nil {
 			clientFail(client, perror.Wrap(err))
 			return false, Fail
 		}
 		return false, Ok
-	case packet.ReadyForQuery:
-		err = pnet.ProxyPacket(client, in)
+	case packets.ReadyForQuery:
+		err = client.Send(zap.InToOut(in))
 		if err != nil {
 			clientFail(client, perror.Wrap(err))
 			return false, Fail
@@ -289,9 +288,9 @@ func sync0(client, server pnet.ReadWriter) (done bool, status Status) {
 	}
 }
 
-func sync(client, server pnet.ReadWriter, in packet.In) (status Status) {
+func sync(client, server zap.ReadWriter, in zap.In) (status Status) {
 	// send initial (sync) to server
-	err := pnet.ProxyPacket(server, in)
+	err := server.Send(zap.InToOut(in))
 	if err != nil {
 		serverFail(server, err)
 		return Fail
@@ -311,13 +310,13 @@ func sync(client, server pnet.ReadWriter, in packet.In) (status Status) {
 	return Ok
 }
 
-func eqp(client, server pnet.ReadWriter, in packet.In) (status Status) {
+func eqp(client, server zap.ReadWriter, in zap.In) (status Status) {
 	for {
 		switch in.Type() {
-		case packet.Sync:
+		case packets.Sync:
 			return sync(client, server, in)
-		case packet.Parse, packet.Bind, packet.Describe, packet.Execute:
-			err := pnet.ProxyPacket(server, in)
+		case packets.Parse, packets.Bind, packets.Describe, packets.Execute:
+			err := server.Send(zap.InToOut(in))
 			if err != nil {
 				serverFail(server, err)
 				return Fail
@@ -340,7 +339,7 @@ func eqp(client, server pnet.ReadWriter, in packet.In) (status Status) {
 	}
 }
 
-func Bounce(client, server pnet.ReadWriter) {
+func Bounce(client, server zap.ReadWriter) {
 	in, err := client.Read()
 	if err != nil {
 		clientFail(client, perror.Wrap(err))
@@ -348,11 +347,11 @@ func Bounce(client, server pnet.ReadWriter) {
 	}
 
 	switch in.Type() {
-	case packet.Query:
+	case packets.Query:
 		query(client, server, in)
-	case packet.FunctionCall:
+	case packets.FunctionCall:
 		functionCall(client, server, in)
-	case packet.Sync, packet.Parse, packet.Bind, packet.Describe, packet.Execute:
+	case packets.Sync, packets.Parse, packets.Bind, packets.Describe, packets.Execute:
 		eqp(client, server, in)
 	default:
 		log.Printf("operation %c", in.Type())
diff --git a/lib/bouncer/frontends/v0/accept.go b/lib/bouncer/frontends/v0/accept.go
index a160b66a7bd58bac9e5c9e06d85072a8b00b1ee6..fa4bee4f3ca808d779474830b9af89b2bdaadb74 100644
--- a/lib/bouncer/frontends/v0/accept.go
+++ b/lib/bouncer/frontends/v0/accept.go
@@ -8,8 +8,8 @@ import (
 
 	"pggat2/lib/auth/sasl"
 	"pggat2/lib/perror"
-	"pggat2/lib/pnet"
-	"pggat2/lib/pnet/packet/packets/v3.0"
+	"pggat2/lib/zap"
+	"pggat2/lib/zap/packets/v3.0"
 )
 
 type Status int
@@ -19,17 +19,17 @@ const (
 	Ok
 )
 
-func fail(client pnet.ReadWriter, err perror.Error) {
+func fail(client zap.ReadWriter, err perror.Error) {
 	// DEBUG(garet)
 	log.Println("client fail", err)
 	debug.PrintStack()
 
 	out := client.Write()
 	packets.WriteErrorResponse(out, err)
-	_ = client.Send(out.Finish())
+	_ = client.Send(out)
 }
 
-func startup0(client pnet.ReadWriter) (done bool, status Status) {
+func startup0(client zap.ReadWriter) (done bool, status Status) {
 	in, err := client.ReadUntyped()
 	if err != nil {
 		fail(client, perror.Wrap(err))
@@ -38,12 +38,12 @@ func startup0(client pnet.ReadWriter) (done bool, status Status) {
 
 	majorVersion, ok := in.Uint16()
 	if !ok {
-		fail(client, pnet.ErrBadPacketFormat)
+		fail(client, packets.ErrBadFormat)
 		return false, Fail
 	}
 	minorVersion, ok := in.Uint16()
 	if !ok {
-		fail(client, pnet.ErrBadPacketFormat)
+		fail(client, packets.ErrBadFormat)
 		return false, Fail
 	}
 
@@ -100,7 +100,7 @@ func startup0(client pnet.ReadWriter) (done bool, status Status) {
 	for {
 		key, ok := in.String()
 		if !ok {
-			fail(client, pnet.ErrBadPacketFormat)
+			fail(client, packets.ErrBadFormat)
 			return false, Fail
 		}
 		if key == "" {
@@ -109,7 +109,7 @@ func startup0(client pnet.ReadWriter) (done bool, status Status) {
 
 		value, ok := in.String()
 		if !ok {
-			fail(client, pnet.ErrBadPacketFormat)
+			fail(client, packets.ErrBadFormat)
 			return false, Fail
 		}
 
@@ -147,7 +147,7 @@ func startup0(client pnet.ReadWriter) (done bool, status Status) {
 		out := client.Write()
 		packets.WriteNegotiateProtocolVersion(out, 0, unsupportedOptions)
 
-		err = client.Send(out.Finish())
+		err = client.Send(out)
 		if err != nil {
 			fail(client, perror.Wrap(err))
 			return false, Fail
@@ -169,7 +169,7 @@ func startup0(client pnet.ReadWriter) (done bool, status Status) {
 	return true, Ok
 }
 
-func authenticationSASLInitial(client pnet.ReadWriter, username, password string) (server sasl.Server, resp []byte, done bool, status Status) {
+func authenticationSASLInitial(client zap.ReadWriter, username, password string) (server sasl.Server, resp []byte, done bool, status Status) {
 	// check which authentication method the client wants
 	in, err := client.Read()
 	if err != nil {
@@ -178,7 +178,7 @@ func authenticationSASLInitial(client pnet.ReadWriter, username, password string
 	}
 	mechanism, initialResponse, ok := packets.ReadSASLInitialResponse(in)
 	if !ok {
-		fail(client, pnet.ErrBadPacketFormat)
+		fail(client, packets.ErrBadFormat)
 		return nil, nil, false, Fail
 	}
 
@@ -196,7 +196,7 @@ func authenticationSASLInitial(client pnet.ReadWriter, username, password string
 	return tool, resp, done, Ok
 }
 
-func authenticationSASLContinue(client pnet.ReadWriter, tool sasl.Server) (resp []byte, done bool, status Status) {
+func authenticationSASLContinue(client zap.ReadWriter, tool sasl.Server) (resp []byte, done bool, status Status) {
 	in, err := client.Read()
 	if err != nil {
 		fail(client, perror.Wrap(err))
@@ -204,7 +204,7 @@ func authenticationSASLContinue(client pnet.ReadWriter, tool sasl.Server) (resp
 	}
 	clientResp, ok := packets.ReadAuthenticationResponse(in)
 	if !ok {
-		fail(client, pnet.ErrProtocolError)
+		fail(client, packets.ErrBadFormat)
 		return nil, false, Fail
 	}
 
@@ -216,10 +216,10 @@ func authenticationSASLContinue(client pnet.ReadWriter, tool sasl.Server) (resp
 	return resp, done, Ok
 }
 
-func authenticationSASL(client pnet.ReadWriter, username, password string) Status {
+func authenticationSASL(client zap.ReadWriter, username, password string) Status {
 	out := client.Write()
 	packets.WriteAuthenticationSASL(out, sasl.Mechanisms)
-	err := client.Send(out.Finish())
+	err := client.Send(out)
 	if err != nil {
 		fail(client, perror.Wrap(err))
 		return Fail
@@ -234,7 +234,7 @@ func authenticationSASL(client pnet.ReadWriter, username, password string) Statu
 		if done {
 			out = client.Write()
 			packets.WriteAuthenticationSASLFinal(out, resp)
-			err = client.Send(out.Finish())
+			err = client.Send(out)
 			if err != nil {
 				fail(client, perror.Wrap(err))
 				return Fail
@@ -243,7 +243,7 @@ func authenticationSASL(client pnet.ReadWriter, username, password string) Statu
 		} else {
 			out = client.Write()
 			packets.WriteAuthenticationSASLContinue(out, resp)
-			err = client.Send(out.Finish())
+			err = client.Send(out)
 			if err != nil {
 				fail(client, perror.Wrap(err))
 				return Fail
@@ -256,10 +256,10 @@ func authenticationSASL(client pnet.ReadWriter, username, password string) Statu
 	return Ok
 }
 
-func updateParameter(client pnet.ReadWriter, name, value string) Status {
+func updateParameter(client zap.ReadWriter, name, value string) Status {
 	out := client.Write()
 	packets.WriteParameterStatus(out, name, value)
-	err := client.Send(out.Finish())
+	err := client.Send(out)
 	if err != nil {
 		fail(client, perror.Wrap(err))
 		return Fail
@@ -267,7 +267,7 @@ func updateParameter(client pnet.ReadWriter, name, value string) Status {
 	return Ok
 }
 
-func Accept(client pnet.ReadWriter) {
+func Accept(client zap.ReadWriter) {
 	for {
 		done, status := startup0(client)
 		if status != Ok {
@@ -286,7 +286,7 @@ func Accept(client pnet.ReadWriter) {
 	// send auth Ok
 	out := client.Write()
 	packets.WriteAuthenticationOk(out)
-	err := client.Send(out.Finish())
+	err := client.Send(out)
 	if err != nil {
 		fail(client, perror.Wrap(err))
 		return
@@ -354,7 +354,7 @@ func Accept(client pnet.ReadWriter) {
 	}
 	out = client.Write()
 	packets.WriteBackendKeyData(out, cancellationKey)
-	err = client.Send(out.Finish())
+	err = client.Send(out)
 	if err != nil {
 		fail(client, perror.Wrap(err))
 		return
@@ -363,7 +363,7 @@ func Accept(client pnet.ReadWriter) {
 	// send ready for query
 	out = client.Write()
 	packets.WriteReadyForQuery(out, 'I')
-	err = client.Send(out.Finish())
+	err = client.Send(out)
 	if err != nil {
 		fail(client, perror.Wrap(err))
 		return
diff --git a/lib/middleware/middlewares/eqp/consumer.go b/lib/middleware/middlewares/eqp/consumer.go
deleted file mode 100644
index bd364d3f6ff27a300e8e8f3828a2c48b3af22a3a..0000000000000000000000000000000000000000
--- a/lib/middleware/middlewares/eqp/consumer.go
+++ /dev/null
@@ -1,125 +0,0 @@
-package eqp
-
-import (
-	"pggat2/lib/pnet"
-	"pggat2/lib/pnet/packet"
-	packets "pggat2/lib/pnet/packet/packets/v3.0"
-	"pggat2/lib/util/decorator"
-	"pggat2/lib/util/ring"
-)
-
-type Consumer struct {
-	noCopy decorator.NoCopy
-
-	preparedStatements        map[string]PreparedStatement
-	portals                   map[string]Portal
-	pendingPreparedStatements ring.Ring[string]
-	pendingPortals            ring.Ring[string]
-	inner                     pnet.ReadWriter
-}
-
-func MakeConsumer(inner pnet.ReadWriter) Consumer {
-	return Consumer{
-		preparedStatements: make(map[string]PreparedStatement),
-		portals:            make(map[string]Portal),
-		inner:              inner,
-	}
-}
-
-func NewConsumer(inner pnet.ReadWriter) *Consumer {
-	c := MakeConsumer(inner)
-	return &c
-}
-
-func (T *Consumer) Read() (packet.In, error) {
-	in, err := T.inner.Read()
-	if err != nil {
-		return packet.In{}, err
-	}
-	switch in.Type() {
-	case packet.ParseComplete:
-		T.pendingPreparedStatements.PopFront()
-	case packet.BindComplete:
-		T.pendingPortals.PopFront()
-	case packet.ReadyForQuery:
-		// remove all pending, they were not added.
-		for pending, ok := T.pendingPreparedStatements.PopFront(); ok; pending, ok = T.pendingPreparedStatements.PopFront() {
-			delete(T.preparedStatements, pending)
-		}
-		for pending, ok := T.pendingPortals.PopFront(); ok; pending, ok = T.pendingPortals.PopFront() {
-			delete(T.portals, pending)
-		}
-	}
-	return in, nil
-}
-
-func (T *Consumer) ReadUntyped() (packet.In, error) {
-	return T.inner.ReadUntyped()
-}
-
-func (T *Consumer) Write() packet.Out {
-	return T.inner.Write()
-}
-
-func (T *Consumer) WriteByte(b byte) error {
-	return T.inner.WriteByte(b)
-}
-
-func (T *Consumer) Send(typ packet.Type, bytes []byte) error {
-	buf := packet.MakeInBuf(typ, bytes)
-	in := packet.MakeIn(&buf)
-	switch typ {
-	case packet.Query:
-		// clobber unnamed portal and unnamed prepared statement
-		delete(T.preparedStatements, "")
-		delete(T.portals, "")
-	case packet.Parse:
-		destination, query, parameterDataTypes, ok := packets.ReadParse(in)
-		if !ok {
-			return ErrBadPacketFormat
-		}
-		if destination != "" {
-			if _, ok = T.preparedStatements[destination]; ok {
-				return ErrPreparedStatementExists
-			}
-		}
-		T.preparedStatements[destination] = PreparedStatement{
-			Query:              query,
-			ParameterDataTypes: parameterDataTypes,
-		}
-		T.pendingPreparedStatements.PushBack(destination)
-	case packet.Bind:
-		destination, source, parameterFormatCodes, parameterValues, resultFormatCodes, ok := packets.ReadBind(in)
-		if !ok {
-			return ErrBadPacketFormat
-		}
-		if destination != "" {
-			if _, ok = T.portals[destination]; ok {
-				return ErrPortalExists
-			}
-		}
-		T.portals[destination] = Portal{
-			Source:               source,
-			ParameterFormatCodes: parameterFormatCodes,
-			ParameterValues:      parameterValues,
-			ResultFormatCodes:    resultFormatCodes,
-		}
-		T.pendingPortals.PushBack(destination)
-	case packet.Close:
-		which, target, ok := packets.ReadClose(in)
-		if !ok {
-			return ErrBadPacketFormat
-		}
-		switch which {
-		case 'S':
-			delete(T.preparedStatements, target)
-		case 'P':
-			delete(T.portals, target)
-		default:
-			return ErrUnknownCloseTarget
-		}
-	}
-	return T.inner.Send(typ, bytes)
-}
-
-var _ pnet.ReadWriter = (*Consumer)(nil)
diff --git a/lib/middleware/middlewares/eqp/creator.go b/lib/middleware/middlewares/eqp/creator.go
deleted file mode 100644
index 6c1baa692685e1a9420e518fefcd03cc0866a9e1..0000000000000000000000000000000000000000
--- a/lib/middleware/middlewares/eqp/creator.go
+++ /dev/null
@@ -1,143 +0,0 @@
-package eqp
-
-import (
-	"pggat2/lib/pnet"
-	"pggat2/lib/pnet/packet"
-	packets "pggat2/lib/pnet/packet/packets/v3.0"
-	"pggat2/lib/util/decorator"
-	"pggat2/lib/util/ring"
-)
-
-type Creator struct {
-	noCopy decorator.NoCopy
-
-	preparedStatements        map[string]PreparedStatement
-	portals                   map[string]Portal
-	pendingPreparedStatements ring.Ring[string]
-	pendingPortals            ring.Ring[string]
-	inner                     pnet.ReadWriter
-}
-
-func MakeCreator(inner pnet.ReadWriter) Creator {
-	return Creator{
-		preparedStatements: make(map[string]PreparedStatement),
-		portals:            make(map[string]Portal),
-		inner:              inner,
-	}
-}
-
-func NewCreator(inner pnet.ReadWriter) *Creator {
-	c := MakeCreator(inner)
-	return &c
-}
-
-func (T *Creator) Read() (packet.In, error) {
-	for {
-		in, err := T.inner.Read()
-		if err != nil {
-			return packet.In{}, err
-		}
-		switch in.Type() {
-		case packet.Query:
-			// clobber unnamed portal and unnamed prepared statement
-			delete(T.preparedStatements, "")
-			delete(T.portals, "")
-			return in, nil
-		case packet.Parse:
-			destination, query, parameterDataTypes, ok := packets.ReadParse(in)
-			if !ok {
-				return packet.In{}, ErrBadPacketFormat
-			}
-			if destination != "" {
-				if _, ok = T.preparedStatements[destination]; ok {
-					return packet.In{}, ErrPreparedStatementExists
-				}
-			}
-			T.preparedStatements[destination] = PreparedStatement{
-				Query:              query,
-				ParameterDataTypes: parameterDataTypes,
-			}
-			T.pendingPreparedStatements.PushBack(destination)
-
-			// send parse complete
-			out := T.inner.Write()
-			out.Type(packet.ParseComplete)
-			err = T.inner.Send(out.Finish())
-			if err != nil {
-				return packet.In{}, err
-			}
-		case packet.Bind:
-			destination, source, parameterFormatCodes, parameterValues, resultFormatCodes, ok := packets.ReadBind(in)
-			if !ok {
-				return packet.In{}, ErrBadPacketFormat
-			}
-			if destination != "" {
-				if _, ok = T.portals[destination]; ok {
-					return packet.In{}, ErrPortalExists
-				}
-			}
-			T.portals[destination] = Portal{
-				Source:               source,
-				ParameterFormatCodes: parameterFormatCodes,
-				ParameterValues:      parameterValues,
-				ResultFormatCodes:    resultFormatCodes,
-			}
-			T.pendingPortals.PushBack(destination)
-
-			// send bind complete
-			out := T.inner.Write()
-			out.Type(packet.BindComplete)
-			err = T.inner.Send(out.Finish())
-			if err != nil {
-				return packet.In{}, err
-			}
-		case packet.Close:
-			which, target, ok := packets.ReadClose(in)
-			if !ok {
-				return packet.In{}, ErrBadPacketFormat
-			}
-			switch which {
-			case 'S':
-				delete(T.preparedStatements, target)
-			case 'P':
-				delete(T.portals, target)
-			default:
-				return packet.In{}, ErrBadPacketFormat
-			}
-		default:
-			return in, nil
-		}
-	}
-}
-
-func (T *Creator) ReadUntyped() (packet.In, error) {
-	return T.inner.ReadUntyped()
-}
-
-func (T *Creator) Write() packet.Out {
-	return T.inner.Write()
-}
-
-func (T *Creator) WriteByte(b byte) error {
-	return T.inner.WriteByte(b)
-}
-
-func (T *Creator) Send(typ packet.Type, payload []byte) error {
-	switch typ {
-	case packet.ParseComplete:
-		T.pendingPreparedStatements.PopFront()
-	case packet.BindComplete:
-		T.pendingPortals.PopFront()
-	case packet.ReadyForQuery:
-		// remove all pending, they were not added.
-		for pending, ok := T.pendingPreparedStatements.PopFront(); ok; pending, ok = T.pendingPreparedStatements.PopFront() {
-			delete(T.preparedStatements, pending)
-		}
-		for pending, ok := T.pendingPortals.PopFront(); ok; pending, ok = T.pendingPortals.PopFront() {
-			delete(T.portals, pending)
-		}
-	}
-	return T.inner.Send(typ, payload)
-}
-
-var _ pnet.ReadWriter = (*Creator)(nil)
diff --git a/lib/middleware/middlewares/eqp/errors.go b/lib/middleware/middlewares/eqp/errors.go
deleted file mode 100644
index d74b38814e9ab0e4f367d43859c812577035950a..0000000000000000000000000000000000000000
--- a/lib/middleware/middlewares/eqp/errors.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package eqp
-
-import "errors"
-
-var (
-	ErrBadPacketFormat         = errors.New("bad packet format")
-	ErrPreparedStatementExists = errors.New("prepared statement already exists")
-	ErrPortalExists            = errors.New("portal already exists")
-	ErrUnknownCloseTarget      = errors.New("unknown close target")
-)
diff --git a/lib/middleware/middlewares/eqp/portal.go b/lib/middleware/middlewares/eqp/portal.go
deleted file mode 100644
index 7ec58e4d2253cf47ef640977d3d40788420e57dc..0000000000000000000000000000000000000000
--- a/lib/middleware/middlewares/eqp/portal.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package eqp
-
-import "pggat2/lib/util/slices"
-
-type Portal struct {
-	Source               string
-	ParameterFormatCodes []int16
-	ParameterValues      [][]byte
-	ResultFormatCodes    []int16
-}
-
-func (T Portal) Equals(rhs Portal) bool {
-	if T.Source != rhs.Source {
-		return false
-	}
-	if !slices.Equal(T.ParameterFormatCodes, rhs.ParameterFormatCodes) {
-		return false
-	}
-	if len(T.ParameterValues) != len(rhs.ParameterValues) {
-		return false
-	}
-	for i := range T.ParameterValues {
-		if !slices.Equal(T.ParameterValues[i], rhs.ParameterValues[i]) {
-			return false
-		}
-	}
-	if !slices.Equal(T.ResultFormatCodes, rhs.ResultFormatCodes) {
-		return false
-	}
-	return true
-}
diff --git a/lib/middleware/middlewares/eqp/preparedStatement.go b/lib/middleware/middlewares/eqp/preparedStatement.go
deleted file mode 100644
index a710b61b8139ba7b6a7c54a6bc51760bcc3cd5f3..0000000000000000000000000000000000000000
--- a/lib/middleware/middlewares/eqp/preparedStatement.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package eqp
-
-import "pggat2/lib/util/slices"
-
-type PreparedStatement struct {
-	Query              string
-	ParameterDataTypes []int32
-}
-
-func (T PreparedStatement) Equals(rhs PreparedStatement) bool {
-	if T.Query != rhs.Query {
-		return false
-	}
-	if !slices.Equal(T.ParameterDataTypes, rhs.ParameterDataTypes) {
-		return false
-	}
-	return true
-}
diff --git a/lib/middleware/middlewares/eqp/stealer.go b/lib/middleware/middlewares/eqp/stealer.go
deleted file mode 100644
index 3d59d7829825bedc2a2d0b23b9f6035ce46a78ea..0000000000000000000000000000000000000000
--- a/lib/middleware/middlewares/eqp/stealer.go
+++ /dev/null
@@ -1,171 +0,0 @@
-package eqp
-
-import (
-	"errors"
-
-	"pggat2/lib/pnet"
-	"pggat2/lib/pnet/packet"
-	packets "pggat2/lib/pnet/packet/packets/v3.0"
-)
-
-// Stealer wraps a Consumer and duplicates the underlying Consumer's portals and prepared statements on use.
-type Stealer struct {
-	creator  *Creator
-	consumer *Consumer
-
-	// need a second buf because we cannot use the underlying Consumer's buf (or it would overwrite the outgoing packet)
-	buf packet.OutBuf
-}
-
-func NewStealer(consumer *Consumer, creator *Creator) *Stealer {
-	return &Stealer{
-		creator:  creator,
-		consumer: consumer,
-	}
-}
-
-func (T *Stealer) Read() (packet.In, error) {
-	for {
-		in, err := T.consumer.Read()
-		if err != nil {
-			return packet.In{}, err
-		}
-		switch in.Type() {
-		case packet.ParseComplete:
-			// previous parse was successful
-		case packet.BindComplete:
-			// previous bind was successful
-		default:
-			// forward
-			return in, nil
-		}
-	}
-}
-
-func (T *Stealer) ReadUntyped() (packet.In, error) {
-	return T.consumer.ReadUntyped()
-}
-
-func (T *Stealer) Write() packet.Out {
-	return T.consumer.Write()
-}
-
-func (T *Stealer) WriteByte(b byte) error {
-	return T.consumer.WriteByte(b)
-}
-
-func (T *Stealer) bindPreparedStatement(target string, preparedStatement PreparedStatement) error {
-	T.buf.Reset()
-	out := packet.MakeOut(&T.buf)
-	packets.WriteParse(out, target, preparedStatement.Query, preparedStatement.ParameterDataTypes)
-	return T.consumer.Send(out.Finish())
-}
-
-func (T *Stealer) bindPortal(target string, portal Portal) error {
-	T.buf.Reset()
-	out := packet.MakeOut(&T.buf)
-	packets.WriteBind(out, target, portal.Source, portal.ParameterFormatCodes, portal.ParameterValues, portal.ResultFormatCodes)
-	return T.consumer.Send(out.Finish())
-}
-
-func (T *Stealer) closePreparedStatement(target string) error {
-	if _, ok := T.consumer.preparedStatements[target]; !ok {
-		// doesn't exist
-		return nil
-	}
-	T.buf.Reset()
-	out := packet.MakeOut(&T.buf)
-	packets.WriteClose(out, 'S', target)
-	return T.consumer.Send(out.Finish())
-}
-
-func (T *Stealer) closePortal(target string) error {
-	if _, ok := T.consumer.portals[target]; !ok {
-		// doesn't exist
-		return nil
-	}
-	T.buf.Reset()
-	out := packet.MakeOut(&T.buf)
-	packets.WriteClose(out, 'P', target)
-	return T.consumer.Send(out.Finish())
-}
-
-func (T *Stealer) syncPreparedStatement(target string) error {
-	creatorStatement, ok := T.creator.preparedStatements[target]
-	if !ok {
-		return T.closePreparedStatement(target)
-	}
-	consumerStatement, prev := T.consumer.preparedStatements[target]
-	if creatorStatement.Equals(consumerStatement) {
-		return nil
-	}
-	// clean up prev
-	if prev {
-		err := T.closePreparedStatement(target)
-		if err != nil {
-			return err
-		}
-	}
-	// send prepared statement
-	return T.bindPreparedStatement(target, creatorStatement)
-}
-
-func (T *Stealer) syncPortal(target string) error {
-	creatorPortal, ok := T.creator.portals[target]
-	if !ok {
-		return T.closePortal(target)
-	}
-	consumerPortal, prev := T.consumer.portals[target]
-	if creatorPortal.Equals(consumerPortal) {
-		return nil
-	}
-	// clean up prev
-	if prev {
-		err := T.closePortal(target)
-		if err != nil {
-			return err
-		}
-	}
-	// send portal
-	return T.bindPortal(target, creatorPortal)
-}
-
-func (T *Stealer) Send(typ packet.Type, bytes []byte) error {
-	// check if we are using a prepared statement or portal that we need to steal
-	buf := packet.MakeInBuf(typ, bytes)
-	in := packet.MakeIn(&buf)
-	switch typ {
-	case packet.Describe:
-		which, target, ok := packets.ReadDescribe(in)
-		if !ok {
-			return errors.New("bad packet format")
-		}
-		switch which {
-		case 'S':
-			err := T.syncPreparedStatement(target)
-			if err != nil {
-				return err
-			}
-		case 'P':
-			err := T.syncPortal(target)
-			if err != nil {
-				return err
-			}
-		default:
-			return errors.New("unknown describe target")
-		}
-	case packet.Execute:
-		target, _, ok := packets.ReadExecute(in)
-		if !ok {
-			return errors.New("bad packet format")
-		}
-		err := T.syncPortal(target)
-		if err != nil {
-			return err
-		}
-	}
-
-	return T.consumer.Send(typ, bytes)
-}
-
-var _ pnet.ReadWriter = (*Stealer)(nil)
diff --git a/lib/middleware/middlewares/unread/unread.go b/lib/middleware/middlewares/unread/unread.go
index 4b41bb2f82074b8c0fb753f2dbab7c8322031d09..c5a860215c675e2607fe530c25c06c8d1316db7f 100644
--- a/lib/middleware/middlewares/unread/unread.go
+++ b/lib/middleware/middlewares/unread/unread.go
@@ -1,17 +1,16 @@
 package unread
 
 import (
-	"pggat2/lib/pnet"
-	"pggat2/lib/pnet/packet"
+	"pggat2/lib/zap"
 )
 
 type Unread struct {
-	in   packet.In
+	in   zap.In
 	read bool
-	pnet.ReadWriter
+	zap.ReadWriter
 }
 
-func NewUnread(inner pnet.ReadWriter) (*Unread, error) {
+func NewUnread(inner zap.ReadWriter) (*Unread, error) {
 	in, err := inner.Read()
 	if err != nil {
 		return nil, err
@@ -22,7 +21,7 @@ func NewUnread(inner pnet.ReadWriter) (*Unread, error) {
 	}, nil
 }
 
-func NewUnreadUntyped(inner pnet.ReadWriter) (*Unread, error) {
+func NewUnreadUntyped(inner zap.ReadWriter) (*Unread, error) {
 	in, err := inner.ReadUntyped()
 	if err != nil {
 		return nil, err
@@ -33,7 +32,7 @@ func NewUnreadUntyped(inner pnet.ReadWriter) (*Unread, error) {
 	}, nil
 }
 
-func (T *Unread) Read() (packet.In, error) {
+func (T *Unread) Read() (zap.In, error) {
 	if !T.read {
 		T.read = true
 		return T.in, nil
@@ -41,7 +40,7 @@ func (T *Unread) Read() (packet.In, error) {
 	return T.ReadWriter.Read()
 }
 
-func (T *Unread) ReadUntyped() (packet.In, error) {
+func (T *Unread) ReadUntyped() (zap.In, error) {
 	if !T.read {
 		T.read = true
 		return T.in, nil
@@ -49,4 +48,4 @@ func (T *Unread) ReadUntyped() (packet.In, error) {
 	return T.ReadWriter.ReadUntyped()
 }
 
-var _ pnet.ReadWriter = (*Unread)(nil)
+var _ zap.ReadWriter = (*Unread)(nil)
diff --git a/lib/middleware/middlewares/unterminate/unterminate.go b/lib/middleware/middlewares/unterminate/unterminate.go
index b08aa27468fae8019ca68a5d5c3376c3aa168f1e..5d818b4ac1abce233a53cf98966e0dd1f3bbe4dc 100644
--- a/lib/middleware/middlewares/unterminate/unterminate.go
+++ b/lib/middleware/middlewares/unterminate/unterminate.go
@@ -3,29 +3,29 @@ package unterminate
 import (
 	"io"
 
-	"pggat2/lib/pnet"
-	"pggat2/lib/pnet/packet"
+	"pggat2/lib/zap"
+	packets "pggat2/lib/zap/packets/v3.0"
 )
 
 type Unterminate struct {
-	pnet.ReadWriter
+	zap.ReadWriter
 }
 
-func MakeUnterminate(inner pnet.ReadWriter) Unterminate {
+func MakeUnterminate(inner zap.ReadWriter) Unterminate {
 	return Unterminate{
 		ReadWriter: inner,
 	}
 }
 
-func (T Unterminate) Read() (packet.In, error) {
+func (T Unterminate) Read() (zap.In, error) {
 	in, err := T.ReadWriter.Read()
 	if err != nil {
-		return packet.In{}, err
+		return zap.In{}, err
 	}
-	if in.Type() == packet.Terminate {
-		return packet.In{}, io.EOF
+	if in.Type() == packets.Terminate {
+		return zap.In{}, io.EOF
 	}
 	return in, nil
 }
 
-var _ pnet.ReadWriter = Unterminate{}
+var _ zap.ReadWriter = Unterminate{}
diff --git a/lib/pnet/errors.go b/lib/pnet/errors.go
deleted file mode 100644
index d094bd7ffaf36afa9648908e0f7203184e6fa12b..0000000000000000000000000000000000000000
--- a/lib/pnet/errors.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package pnet
-
-import "pggat2/lib/perror"
-
-var ErrBadPacketFormat = perror.New(
-	perror.FATAL,
-	perror.ProtocolViolation,
-	"Bad packet format",
-)
-
-var ErrProtocolError = perror.New(
-	perror.FATAL,
-	perror.ProtocolViolation,
-	"Unexpected packet",
-)
diff --git a/lib/pnet/intercepted.go b/lib/pnet/intercepted.go
deleted file mode 100644
index 2e3ebc04b971eb4fc168c553e6db555adbcc54c6..0000000000000000000000000000000000000000
--- a/lib/pnet/intercepted.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package pnet
-
-import (
-	"pggat2/lib/middleware"
-	"pggat2/lib/pnet/packet"
-)
-
-type Intercepted struct {
-	ReadWriter
-	Middlewares []middleware.Middleware
-}
-
-func (T Intercepted) interceptRead(in packet.In) (forward bool, err error) {
-	for _, mw := range T.Middlewares {
-		forward, err = mw.Read(in)
-		if err != nil || !forward {
-			return
-		}
-	}
-	return true, nil
-}
-
-func (T Intercepted) Read() (packet.In, error) {
-	for {
-		in, err := T.ReadWriter.Read()
-		if err != nil {
-			return packet.In{}, err
-		}
-		var forward bool
-		forward, err = T.interceptRead(in)
-		if err != nil {
-			return packet.In{}, err
-		}
-		if forward {
-			return in, nil
-		}
-	}
-}
-
-func (T Intercepted) ReadUntyped() (packet.In, error) {
-	for {
-		in, err := T.ReadWriter.ReadUntyped()
-		if err != nil {
-			return packet.In{}, err
-		}
-		var forward bool
-		forward, err = T.interceptRead(in)
-		if err != nil {
-			return packet.In{}, err
-		}
-		if forward {
-			return in, nil
-		}
-	}
-}
-
-func (T Intercepted) Send(typ packet.Type, payload []byte) error {
-	inBuf := packet.MakeInBuf(typ, payload)
-	in := packet.MakeIn(&inBuf)
-	for _, mw := range T.Middlewares {
-		forward, err := mw.Write(in)
-		if err != nil {
-			return err
-		}
-		if !forward {
-			return nil
-		}
-	}
-	return T.ReadWriter.Send(typ, payload)
-}
-
-var _ ReadWriter = Intercepted{}
diff --git a/lib/pnet/packet/in.go b/lib/pnet/packet/in.go
deleted file mode 100644
index 5c96fe56786eb21655445150b8052bc8c435fdc3..0000000000000000000000000000000000000000
--- a/lib/pnet/packet/in.go
+++ /dev/null
@@ -1,182 +0,0 @@
-package packet
-
-import (
-	"encoding/binary"
-	"math"
-
-	"pggat2/lib/util/decorator"
-)
-
-type InBuf struct {
-	noCopy decorator.NoCopy
-	typ    Type
-	buf    []byte
-	pos    int
-	rev    int
-}
-
-func MakeInBuf(typ Type, buf []byte) InBuf {
-	return InBuf{
-		typ: typ,
-		buf: buf,
-	}
-}
-
-func (T *InBuf) Reset(
-	typ Type,
-	buf []byte,
-) {
-	T.typ = typ
-	T.buf = buf
-	T.pos = 0
-	T.rev++
-}
-
-type In struct {
-	buf *InBuf
-	rev int
-}
-
-func MakeIn(
-	buf *InBuf,
-) In {
-	return In{
-		buf: buf,
-		rev: buf.rev,
-	}
-}
-
-func (T In) done() bool {
-	return T.rev != T.buf.rev
-}
-
-func (T In) Type() Type {
-	if T.done() {
-		panic("Read after Done")
-	}
-	return T.buf.typ
-}
-
-// Full returns the full payload of the packet.
-// NOTE: Full will be invalid after Done is called
-func (T In) Full() []byte {
-	if T.done() {
-		panic("Read after Done")
-	}
-	return T.buf.buf
-}
-
-// Remaining returns the remaining payload of the packet.
-// NOTE: Remaining will be invalid after Done is called
-func (T In) Remaining() []byte {
-	full := T.Full()
-	return full[T.buf.pos:]
-}
-
-func (T In) Reset() {
-	if T.done() {
-		panic("Read after Done")
-	}
-	T.buf.pos = 0
-}
-
-func (T In) Int8() (int8, bool) {
-	v, ok := T.Uint8()
-	return int8(v), ok
-}
-
-func (T In) Int16() (int16, bool) {
-	v, ok := T.Uint16()
-	return int16(v), ok
-}
-
-func (T In) Int32() (int32, bool) {
-	v, ok := T.Uint32()
-	return int32(v), ok
-}
-
-func (T In) Int64() (int64, bool) {
-	v, ok := T.Uint64()
-	return int64(v), ok
-}
-
-func (T In) Uint8() (uint8, bool) {
-	rem := T.Remaining()
-	if len(rem) < 1 {
-		return 0, false
-	}
-	v := rem[0]
-	T.buf.pos += 1
-	return v, true
-}
-
-func (T In) Uint16() (uint16, bool) {
-	rem := T.Remaining()
-	if len(rem) < 2 {
-		return 0, false
-	}
-	v := binary.BigEndian.Uint16(rem)
-	T.buf.pos += 2
-	return v, true
-}
-
-func (T In) Uint32() (uint32, bool) {
-	rem := T.Remaining()
-	if len(rem) < 4 {
-		return 0, false
-	}
-	v := binary.BigEndian.Uint32(rem)
-	T.buf.pos += 4
-	return v, true
-}
-
-func (T In) Uint64() (uint64, bool) {
-	rem := T.Remaining()
-	if len(rem) < 8 {
-		return 0, false
-	}
-	v := binary.BigEndian.Uint64(rem)
-	T.buf.pos += 8
-	return v, true
-}
-
-func (T In) Float32() (float32, bool) {
-	v, ok := T.Uint32()
-	return math.Float32frombits(v), ok
-}
-
-func (T In) Float64() (float64, bool) {
-	v, ok := T.Uint64()
-	return math.Float64frombits(v), ok
-}
-
-func (T In) String() (string, bool) {
-	rem := T.Remaining()
-	for i, c := range rem {
-		if c == 0 {
-			v := string(rem[:i])
-			T.buf.pos += i + 1
-			return v, true
-		}
-	}
-	return "", false
-}
-
-func (T In) Bytes(b []byte) bool {
-	rem := T.Remaining()
-	if len(b) > len(rem) {
-		return false
-	}
-	copy(b, rem)
-	T.buf.pos += len(b)
-	return true
-}
-
-// UnsafeBytes returns a byte slice without copying. Use this only if you plan to be done with the slice when the In is reset or the data will be replaced with garbage.
-func (T In) UnsafeBytes(count int) ([]byte, bool) {
-	rem := T.Remaining()
-	if count > len(rem) {
-		return nil, false
-	}
-	return rem[:count], true
-}
diff --git a/lib/pnet/packet/out.go b/lib/pnet/packet/out.go
deleted file mode 100644
index a030b71f6950c5b22676231f772137cf5107c147..0000000000000000000000000000000000000000
--- a/lib/pnet/packet/out.go
+++ /dev/null
@@ -1,133 +0,0 @@
-package packet
-
-import (
-	"encoding/binary"
-	"math"
-
-	"pggat2/lib/util/decorator"
-)
-
-type OutBuf struct {
-	noCopy decorator.NoCopy
-	typ    Type
-	buf    []byte
-	rev    int
-}
-
-func MakeOutBuf(buf []byte) OutBuf {
-	return OutBuf{
-		buf: buf,
-	}
-}
-
-func (T *OutBuf) Reset() {
-	T.typ = None
-	T.buf = T.buf[:0]
-	T.rev++
-}
-
-type Out struct {
-	buf *OutBuf
-	rev int
-}
-
-func MakeOut(
-	buf *OutBuf,
-) Out {
-	return Out{
-		buf: buf,
-		rev: buf.rev,
-	}
-}
-
-func (T Out) done() bool {
-	return T.rev != T.buf.rev
-}
-
-func (T Out) Finish() (Type, []byte) {
-	if T.done() {
-		panic("Write after Send")
-	}
-	return T.buf.typ, T.buf.buf
-}
-
-func (T Out) Type(typ Type) {
-	if T.done() {
-		panic("Write after Send")
-	}
-	T.buf.typ = typ
-}
-
-func (T Out) Reset() {
-	if T.done() {
-		panic("Write after Send")
-	}
-	T.buf.buf = T.buf.buf[:0]
-}
-
-func (T Out) Int8(v int8) {
-	T.Uint8(uint8(v))
-}
-
-func (T Out) Int16(v int16) {
-	T.Uint16(uint16(v))
-}
-
-func (T Out) Int32(v int32) {
-	T.Uint32(uint32(v))
-}
-
-func (T Out) Int64(v int64) {
-	T.Uint64(uint64(v))
-}
-
-func (T Out) Uint8(v uint8) {
-	if T.done() {
-		panic("Write after Send")
-	}
-	T.buf.buf = append(T.buf.buf, v)
-}
-
-func (T Out) Uint16(v uint16) {
-	if T.done() {
-		panic("Write after Send")
-	}
-	T.buf.buf = binary.BigEndian.AppendUint16(T.buf.buf, v)
-}
-
-func (T Out) Uint32(v uint32) {
-	if T.done() {
-		panic("Write after Send")
-	}
-	T.buf.buf = binary.BigEndian.AppendUint32(T.buf.buf, v)
-}
-
-func (T Out) Uint64(v uint64) {
-	if T.done() {
-		panic("Write after Send")
-	}
-	T.buf.buf = binary.BigEndian.AppendUint64(T.buf.buf, v)
-}
-
-func (T Out) Float32(v float32) {
-	T.Uint32(math.Float32bits(v))
-}
-
-func (T Out) Float64(v float64) {
-	T.Uint64(math.Float64bits(v))
-}
-
-func (T Out) String(v string) {
-	if T.done() {
-		panic("Write after Send")
-	}
-	T.buf.buf = append(T.buf.buf, v...)
-	T.Uint8(0)
-}
-
-func (T Out) Bytes(v []byte) {
-	if T.done() {
-		panic("Write after Send")
-	}
-	T.buf.buf = append(T.buf.buf, v...)
-}
diff --git a/lib/pnet/packet/packets/v3.0/authenticationcleartext.go b/lib/pnet/packet/packets/v3.0/authenticationcleartext.go
deleted file mode 100644
index 2464c5c60872f0309be025fb34e117de1f685c87..0000000000000000000000000000000000000000
--- a/lib/pnet/packet/packets/v3.0/authenticationcleartext.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package packets
-
-import "pggat2/lib/pnet/packet"
-
-func ReadAuthenticationCleartext(in packet.In) bool {
-	in.Reset()
-	if in.Type() != packet.Authentication {
-		return false
-	}
-	method, ok := in.Int32()
-	if !ok {
-		return false
-	}
-	if method != 3 {
-		return false
-	}
-	return true
-}
-
-func WriteAuthenticationCleartext(out packet.Out) {
-	out.Reset()
-	out.Type(packet.Authentication)
-	out.Int32(3)
-}
diff --git a/lib/pnet/packet/packets/v3.0/authenticationok.go b/lib/pnet/packet/packets/v3.0/authenticationok.go
deleted file mode 100644
index 26d6f8261c6196310a855c544fdb1fbe71d216fd..0000000000000000000000000000000000000000
--- a/lib/pnet/packet/packets/v3.0/authenticationok.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package packets
-
-import "pggat2/lib/pnet/packet"
-
-func ReadAuthenticationOk(in packet.In) bool {
-	in.Reset()
-	if in.Type() != packet.Authentication {
-		return false
-	}
-	method, ok := in.Int32()
-	if !ok {
-		return false
-	}
-	if method != 0 {
-		return false
-	}
-	return true
-}
-
-func WriteAuthenticationOk(out packet.Out) {
-	out.Reset()
-	out.Type(packet.Authentication)
-	out.Int32(0)
-}
diff --git a/lib/pnet/packet/packets/v3.0/authenticationresponse.go b/lib/pnet/packet/packets/v3.0/authenticationresponse.go
deleted file mode 100644
index 221cf19b7496ed8bf083a70e1f751e3dd14758a2..0000000000000000000000000000000000000000
--- a/lib/pnet/packet/packets/v3.0/authenticationresponse.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package packets
-
-import "pggat2/lib/pnet/packet"
-
-func ReadAuthenticationResponse(in packet.In) ([]byte, bool) {
-	in.Reset()
-	if in.Type() != packet.AuthenticationResponse {
-		return nil, false
-	}
-	return in.Full(), true
-}
-
-func WriteAuthenticationResponse(out packet.Out, resp []byte) {
-	out.Reset()
-	out.Type(packet.AuthenticationResponse)
-	out.Bytes(resp)
-}
diff --git a/lib/pnet/packet/packets/v3.0/authenticationsaslcontinue.go b/lib/pnet/packet/packets/v3.0/authenticationsaslcontinue.go
deleted file mode 100644
index 69691e9c1a938188f5a0738ab1474eedd7201642..0000000000000000000000000000000000000000
--- a/lib/pnet/packet/packets/v3.0/authenticationsaslcontinue.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package packets
-
-import "pggat2/lib/pnet/packet"
-
-func ReadAuthenticationSASLContinue(in packet.In) ([]byte, bool) {
-	in.Reset()
-	if in.Type() != packet.Authentication {
-		return nil, false
-	}
-	method, ok := in.Int32()
-	if !ok {
-		return nil, false
-	}
-	if method != 11 {
-		return nil, false
-	}
-	return in.Full(), true
-}
-
-func WriteAuthenticationSASLContinue(out packet.Out, resp []byte) {
-	out.Reset()
-	out.Type(packet.Authentication)
-	out.Int32(11)
-	out.Bytes(resp)
-}
diff --git a/lib/pnet/packet/packets/v3.0/authenticationsaslfinal.go b/lib/pnet/packet/packets/v3.0/authenticationsaslfinal.go
deleted file mode 100644
index 2a7d3594fe3e85dab9cd974090b48f71e47991f1..0000000000000000000000000000000000000000
--- a/lib/pnet/packet/packets/v3.0/authenticationsaslfinal.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package packets
-
-import "pggat2/lib/pnet/packet"
-
-func ReadAuthenticationSASLFinal(in packet.In) ([]byte, bool) {
-	in.Reset()
-	if in.Type() != packet.Authentication {
-		return nil, false
-	}
-	method, ok := in.Int32()
-	if !ok {
-		return nil, false
-	}
-	if method != 12 {
-		return nil, false
-	}
-	return in.Full(), true
-}
-
-func WriteAuthenticationSASLFinal(out packet.Out, resp []byte) {
-	out.Reset()
-	out.Type(packet.Authentication)
-	out.Int32(12)
-	out.Bytes(resp)
-}
diff --git a/lib/pnet/packet/packets/v3.0/close.go b/lib/pnet/packet/packets/v3.0/close.go
deleted file mode 100644
index f20b0349db247fb6cdc6c5de0c2718d692c6465c..0000000000000000000000000000000000000000
--- a/lib/pnet/packet/packets/v3.0/close.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package packets
-
-import "pggat2/lib/pnet/packet"
-
-func ReadClose(in packet.In) (which uint8, target string, ok bool) {
-	in.Reset()
-	if in.Type() != packet.Close {
-		return
-	}
-	which, ok = in.Uint8()
-	if !ok {
-		return
-	}
-	target, ok = in.String()
-	if !ok {
-		return
-	}
-	return
-}
-
-func WriteClose(out packet.Out, which uint8, target string) {
-	out.Reset()
-	out.Type(packet.Close)
-	out.Uint8(which)
-	out.String(target)
-}
diff --git a/lib/pnet/packet/packets/v3.0/describe.go b/lib/pnet/packet/packets/v3.0/describe.go
deleted file mode 100644
index 6d9623fdfb003f17bffa32553f928daead671d20..0000000000000000000000000000000000000000
--- a/lib/pnet/packet/packets/v3.0/describe.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package packets
-
-import "pggat2/lib/pnet/packet"
-
-func ReadDescribe(in packet.In) (which uint8, target string, ok bool) {
-	in.Reset()
-	if in.Type() != packet.Describe {
-		return
-	}
-	which, ok = in.Uint8()
-	if !ok {
-		return
-	}
-	target, ok = in.String()
-	if !ok {
-		return
-	}
-	return
-}
-
-func WriteDescribe(out packet.Out, which uint8, target string) {
-	out.Reset()
-	out.Type(packet.Describe)
-	out.Uint8(which)
-	out.String(target)
-}
diff --git a/lib/pnet/packet/packets/v3.0/execute.go b/lib/pnet/packet/packets/v3.0/execute.go
deleted file mode 100644
index e012a6568cec16930004befbe95797976d700f4e..0000000000000000000000000000000000000000
--- a/lib/pnet/packet/packets/v3.0/execute.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package packets
-
-import "pggat2/lib/pnet/packet"
-
-func ReadExecute(in packet.In) (target string, maxRows int32, ok bool) {
-	in.Reset()
-	if in.Type() != packet.Execute {
-		return
-	}
-	target, ok = in.String()
-	if !ok {
-		return
-	}
-	maxRows, ok = in.Int32()
-	if !ok {
-		return
-	}
-	return
-}
-
-func WriteExecute(out packet.Out, target string, maxRows int32) {
-	out.Reset()
-	out.Type(packet.Execute)
-	out.String(target)
-	out.Int32(maxRows)
-}
diff --git a/lib/pnet/packet/packets/v3.0/parameterstatus.go b/lib/pnet/packet/packets/v3.0/parameterstatus.go
deleted file mode 100644
index bac3411b64acd2664128ce47a409aa6067827d0b..0000000000000000000000000000000000000000
--- a/lib/pnet/packet/packets/v3.0/parameterstatus.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package packets
-
-import "pggat2/lib/pnet/packet"
-
-func ReadParameterStatus(in packet.In) (key, value string, ok bool) {
-	in.Reset()
-	if in.Type() != packet.ParameterStatus {
-		return
-	}
-	key, ok = in.String()
-	if !ok {
-		return
-	}
-	value, ok = in.String()
-	if !ok {
-		return
-	}
-	return
-}
-
-func WriteParameterStatus(out packet.Out, key, value string) {
-	out.Reset()
-	out.Type(packet.ParameterStatus)
-	out.String(key)
-	out.String(value)
-}
diff --git a/lib/pnet/packet/packets/v3.0/passwordmessage.go b/lib/pnet/packet/packets/v3.0/passwordmessage.go
deleted file mode 100644
index ac01b9ad5a439eb6d48081363d8da57c67e83816..0000000000000000000000000000000000000000
--- a/lib/pnet/packet/packets/v3.0/passwordmessage.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package packets
-
-import "pggat2/lib/pnet/packet"
-
-func ReadPasswordMessage(in packet.In) (string, bool) {
-	in.Reset()
-	if in.Type() != packet.AuthenticationResponse {
-		return "", false
-	}
-	password, ok := in.String()
-	if !ok {
-		return "", false
-	}
-	return password, true
-}
-
-func WritePasswordMessage(out packet.Out, password string) {
-	out.Reset()
-	out.Type(packet.AuthenticationResponse)
-	out.String(password)
-}
diff --git a/lib/pnet/packet/packets/v3.0/readyforquery.go b/lib/pnet/packet/packets/v3.0/readyforquery.go
deleted file mode 100644
index 4035428bc7b13259041ef73bcfdaff3e3fbff3ba..0000000000000000000000000000000000000000
--- a/lib/pnet/packet/packets/v3.0/readyforquery.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package packets
-
-import "pggat2/lib/pnet/packet"
-
-func ReadReadyForQuery(in packet.In) (byte, bool) {
-	in.Reset()
-	if in.Type() != packet.ReadyForQuery {
-		return 0, false
-	}
-	state, ok := in.Uint8()
-	if !ok {
-		return 0, false
-	}
-	return state, true
-}
-
-func WriteReadyForQuery(out packet.Out, state uint8) {
-	out.Reset()
-	out.Type(packet.ReadyForQuery)
-	out.Uint8(state)
-}
diff --git a/lib/pnet/packet/proxy.go b/lib/pnet/packet/proxy.go
deleted file mode 100644
index 706b9163a5726ba942c2b2e0f2bd51d9ee058cdf..0000000000000000000000000000000000000000
--- a/lib/pnet/packet/proxy.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package packet
-
-func Proxy(out Out, in In) {
-	out.Type(in.Type())
-	out.Bytes(in.Full())
-}
diff --git a/lib/pnet/packet/type.go b/lib/pnet/packet/type.go
deleted file mode 100644
index 25b3ab87311708a57c1ddea11ed72f9d19eba7f3..0000000000000000000000000000000000000000
--- a/lib/pnet/packet/type.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package packet
-
-type Type byte
-
-const (
-	None                     Type = 0
-	Authentication           Type = 'R'
-	BackendKeyData           Type = 'K'
-	Bind                     Type = 'B'
-	BindComplete             Type = '2'
-	Close                    Type = 'C'
-	CloseComplete            Type = '3'
-	CommandComplete          Type = 'C'
-	CopyData                 Type = 'd'
-	CopyDone                 Type = 'c'
-	CopyFail                 Type = 'f'
-	CopyInResponse           Type = 'G'
-	CopyOutResponse          Type = 'H'
-	CopyBothResponse         Type = 'W'
-	DataRow                  Type = 'D'
-	Describe                 Type = 'D'
-	EmptyQueryResponse       Type = 'I'
-	ErrorResponse            Type = 'E'
-	Execute                  Type = 'E'
-	Flush                    Type = 'H'
-	FunctionCall             Type = 'F'
-	FunctionCallResponse     Type = 'V'
-	AuthenticationResponse   Type = 'p'
-	NegotiateProtocolVersion Type = 'v'
-	NoData                   Type = 'n'
-	NoticeResponse           Type = 'N'
-	NotificationResponse     Type = 'A'
-	ParameterDescription     Type = 't'
-	ParameterStatus          Type = 'S'
-	Parse                    Type = 'P'
-	ParseComplete            Type = '1'
-	PortalSuspended          Type = 's'
-	Query                    Type = 'Q'
-	ReadyForQuery            Type = 'Z'
-	RowDescription           Type = 'T'
-	Sync                     Type = 'S'
-	Terminate                Type = 'X'
-)
diff --git a/lib/pnet/pio/reader.go b/lib/pnet/pio/reader.go
deleted file mode 100644
index 1939881e4ce5255594ce3173a3892c4fccba5c7e..0000000000000000000000000000000000000000
--- a/lib/pnet/pio/reader.go
+++ /dev/null
@@ -1,106 +0,0 @@
-package pio
-
-import (
-	"encoding/binary"
-	"io"
-
-	"pggat2/lib/pnet"
-	"pggat2/lib/pnet/packet"
-	"pggat2/lib/util/decorator"
-	"pggat2/lib/util/slices"
-)
-
-type Reader struct {
-	noCopy decorator.NoCopy
-	reader io.Reader
-	// header buffer for reading packet headers
-	// (allocating within Read would escape to heap)
-	header [5]byte
-
-	buf     packet.InBuf
-	payload []byte
-}
-
-func MakeReader(reader io.Reader) Reader {
-	return Reader{
-		reader: reader,
-	}
-}
-
-func NewReader(reader io.Reader) *Reader {
-	v := MakeReader(reader)
-	return &v
-}
-
-// Read fetches the next packet from the underlying io.Reader and gives you a packet.In
-// Calling Read will invalidate all other packet.In's for this Reader
-func (T *Reader) Read() (packet.In, error) {
-	// read header
-	_, err := io.ReadFull(T.reader, T.header[:])
-	if err != nil {
-		return packet.In{}, err
-	}
-
-	err = T.readPayload()
-	if err != nil {
-		return packet.In{}, err
-	}
-
-	T.buf.Reset(
-		packet.Type(T.header[0]),
-		T.payload,
-	)
-
-	// log.Printf("read typed packet %c %v\n", typ, T.payload)
-
-	return packet.MakeIn(
-		&T.buf,
-	), nil
-}
-
-// ReadUntyped is similar to Read, but it doesn't read a packet.Type
-func (T *Reader) ReadUntyped() (packet.In, error) {
-	// read header
-	_, err := io.ReadFull(T.reader, T.header[1:])
-	if err != nil {
-		return packet.In{}, err
-	}
-
-	err = T.readPayload()
-	if err != nil {
-		return packet.In{}, err
-	}
-
-	T.buf.Reset(
-		packet.None,
-		T.payload,
-	)
-
-	// log.Println("read untyped packet", T.payload)
-
-	return packet.MakeIn(
-		&T.buf,
-	), nil
-}
-
-func (T *Reader) readPayload() error {
-	length := binary.BigEndian.Uint32(T.header[1:]) - 4
-
-	// resize body to length
-	T.payload = slices.Resize(T.payload, int(length))
-	// read body
-	_, err := io.ReadFull(T.reader, T.payload)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func (T *Reader) ReadByte() (byte, error) {
-	T.header[0] = 0
-	_, err := io.ReadFull(T.reader, T.header[:1])
-	return T.header[0], err
-}
-
-var _ pnet.Reader = (*Reader)(nil)
diff --git a/lib/pnet/pio/readwriter.go b/lib/pnet/pio/readwriter.go
deleted file mode 100644
index 43ce4561759a216ccc8a024a2ab2b466e221aafb..0000000000000000000000000000000000000000
--- a/lib/pnet/pio/readwriter.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package pio
-
-import (
-	"io"
-
-	"pggat2/lib/pnet"
-)
-
-type ReadWriter struct {
-	Reader
-	Writer
-}
-
-func MakeReadWriter(inner io.ReadWriter) ReadWriter {
-	return ReadWriter{
-		Reader: MakeReader(inner),
-		Writer: MakeWriter(inner),
-	}
-}
-
-func NewReadWriter(inner io.ReadWriter) *ReadWriter {
-	rw := MakeReadWriter(inner)
-	return &rw
-}
-
-var _ pnet.ReadWriter = (*ReadWriter)(nil)
diff --git a/lib/pnet/pio/writer.go b/lib/pnet/pio/writer.go
deleted file mode 100644
index 8bdcd5e3f30999baf3d23ce72b3d35354d11e876..0000000000000000000000000000000000000000
--- a/lib/pnet/pio/writer.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package pio
-
-import (
-	"encoding/binary"
-	"io"
-
-	"pggat2/lib/pnet"
-	"pggat2/lib/pnet/packet"
-	"pggat2/lib/util/decorator"
-)
-
-type Writer struct {
-	noCopy decorator.NoCopy
-	writer io.Writer
-	// header buffer for writing packet headers
-	// (allocating within Write would escape to heap)
-	header [5]byte
-
-	buf packet.OutBuf
-}
-
-func MakeWriter(writer io.Writer) Writer {
-	return Writer{
-		writer: writer,
-	}
-}
-
-func NewWriter(writer io.Writer) *Writer {
-	v := MakeWriter(writer)
-	return &v
-}
-
-// Write gives you a packet.Out
-// Calling Write will invalidate all other packet.Out's for this Writer
-func (T *Writer) Write() packet.Out {
-	T.buf.Reset()
-
-	return packet.MakeOut(
-		&T.buf,
-	)
-}
-
-func (T *Writer) Send(typ packet.Type, payload []byte) error {
-	/* if typ != packet.None {
-		log.Printf("write typed packet %c %v\n", typ, payload)
-	} else {
-		log.Println("write untyped packet", payload)
-	} */
-
-	// prepare header
-	T.header[0] = byte(typ)
-	binary.BigEndian.PutUint32(T.header[1:], uint32(len(payload)+4))
-
-	// write header
-	if typ != packet.None {
-		_, err := T.writer.Write(T.header[:])
-		if err != nil {
-			return err
-		}
-	} else {
-		_, err := T.writer.Write(T.header[1:])
-		if err != nil {
-			return err
-		}
-	}
-
-	// write payload
-	_, err := T.writer.Write(payload)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func (T *Writer) WriteByte(b byte) error {
-	T.header[0] = b
-	_, err := T.writer.Write(T.header[:1])
-	return err
-}
-
-var _ pnet.Writer = (*Writer)(nil)
diff --git a/lib/pnet/proxy.go b/lib/pnet/proxy.go
deleted file mode 100644
index bebee64ba0ae906199c3778b6895aecbc4735e27..0000000000000000000000000000000000000000
--- a/lib/pnet/proxy.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package pnet
-
-import "pggat2/lib/pnet/packet"
-
-func ProxyPacket(writer Writer, in packet.In) error {
-	out := writer.Write()
-	packet.Proxy(out, in)
-	return writer.Send(out.Finish())
-}
-
-func Proxy(writer Writer, reader Reader) error {
-	in, err := reader.Read()
-	if err != nil {
-		return err
-	}
-	return ProxyPacket(writer, in)
-}
diff --git a/lib/pnet/reader.go b/lib/pnet/reader.go
deleted file mode 100644
index 259cc33bcdddb9880ab354cba63b5d463c7d3624..0000000000000000000000000000000000000000
--- a/lib/pnet/reader.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package pnet
-
-import "pggat2/lib/pnet/packet"
-
-type Reader interface {
-	Read() (packet.In, error)
-	ReadUntyped() (packet.In, error)
-}
diff --git a/lib/pnet/writer.go b/lib/pnet/writer.go
deleted file mode 100644
index bd92c1f566931c54f7e6ae642acace146d3052c0..0000000000000000000000000000000000000000
--- a/lib/pnet/writer.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package pnet
-
-import (
-	"io"
-
-	"pggat2/lib/pnet/packet"
-)
-
-type Writer interface {
-	io.ByteWriter
-
-	Write() packet.Out
-	Send(packet.Type, []byte) error
-}
diff --git a/lib/util/slices/clear.go b/lib/util/slices/clear.go
new file mode 100644
index 0000000000000000000000000000000000000000..972b344e17a12fc785276b5c5948685ebe1fa1e8
--- /dev/null
+++ b/lib/util/slices/clear.go
@@ -0,0 +1,7 @@
+package slices
+
+func Clear[T any](slice []T) {
+	for i := 0; i < len(slice); i++ {
+		slice[i] = *new(T)
+	}
+}
diff --git a/lib/zap/buf.go b/lib/zap/buf.go
new file mode 100644
index 0000000000000000000000000000000000000000..3145731f5e7b3c0d3ad589a475429e6a296483af
--- /dev/null
+++ b/lib/zap/buf.go
@@ -0,0 +1,297 @@
+package zap
+
+import (
+	"encoding/binary"
+	"io"
+	"log"
+	"math"
+
+	"pggat2/lib/util/decorator"
+	"pggat2/lib/util/slices"
+)
+
+type Buf struct {
+	noCopy decorator.NoCopy
+
+	pos int
+	buf []byte
+	rev int
+}
+
+func (T *Buf) assertRev(rev int) {
+	// this check can be turned off when in production mode (for dev, this is helpful though)
+	if T.rev != rev {
+		panic("use after resource release")
+	}
+}
+
+func (T *Buf) ReadByte(reader io.Reader) (byte, error) {
+	T.rev++
+	T.pos = 0
+
+	T.buf = slices.Resize(T.buf, 1)
+	_, err := io.ReadFull(reader, T.buf)
+	if err != nil {
+		return 0, err
+	}
+	return T.buf[0], nil
+}
+
+func (T *Buf) Read(reader io.Reader, typed bool) (In, error) {
+	T.rev++
+	T.pos = 0
+
+	// read header
+	T.buf = slices.Resize(T.buf, 5)
+	var err error
+	if typed {
+		_, err = io.ReadFull(reader, T.buf)
+	} else {
+		_, err = io.ReadFull(reader, T.buf[1:])
+	}
+	if err != nil {
+		return In{}, err
+	}
+
+	// extract length
+	length := binary.BigEndian.Uint32(T.buf[1:])
+
+	// read payload
+	T.buf = slices.Resize(T.buf, int(length)+5)
+	_, err = io.ReadFull(reader, T.buf[5:])
+	if err != nil {
+		return In{}, err
+	}
+
+	log.Printf("read packet %c %v\n", T.readType(), T.remaining())
+
+	return In{
+		buf: T,
+		rev: T.rev,
+	}, nil
+}
+
+func (T *Buf) WriteByte(writer io.Writer, b byte) error {
+	T.rev++
+	T.pos = 0
+
+	T.buf = slices.Resize(T.buf, 1)
+	T.buf[0] = b
+	_, err := writer.Write(T.buf)
+	return err
+}
+
+func (T *Buf) Write() Out {
+	T.rev++
+	T.pos = 0
+
+	T.buf = slices.Resize(T.buf, 5)
+
+	return Out{
+		buf: T,
+		rev: T.rev,
+	}
+}
+
+func (T *Buf) full() []byte {
+	// put length
+	binary.BigEndian.PutUint32(T.buf[1:], uint32(len(T.buf)-1))
+
+	log.Printf("write packet %c %v\n", T.readType(), T.buf[5:])
+
+	if T.readType() == 0 {
+		// untyped
+		return T.buf[1:]
+	} else {
+		// typed
+		return T.buf
+	}
+}
+
+// read methods
+
+func (T *Buf) resetRead() {
+	T.pos = 0
+}
+
+func (T *Buf) readType() Type {
+	return Type(T.buf[0])
+}
+
+func (T *Buf) remaining() []byte {
+	return T.buf[T.pos+5:]
+}
+
+func (T *Buf) readUint8() (uint8, bool) {
+	rem := T.remaining()
+	if len(rem) < 1 {
+		return 0, false
+	}
+	T.pos += 1
+	return rem[0], true
+}
+
+func (T *Buf) readUint16() (uint16, bool) {
+	rem := T.remaining()
+	if len(rem) < 2 {
+		return 0, false
+	}
+	T.pos += 2
+	return binary.BigEndian.Uint16(rem), true
+}
+
+func (T *Buf) readUint32() (uint32, bool) {
+	rem := T.remaining()
+	if len(rem) < 4 {
+		return 0, false
+	}
+	T.pos += 4
+	return binary.BigEndian.Uint32(rem), true
+}
+
+func (T *Buf) readUint64() (uint64, bool) {
+	rem := T.remaining()
+	if len(rem) < 8 {
+		return 0, false
+	}
+	T.pos += 8
+	return binary.BigEndian.Uint64(rem), true
+}
+
+func (T *Buf) readInt8() (int8, bool) {
+	v, ok := T.readUint8()
+	if !ok {
+		return 0, false
+	}
+	return int8(v), true
+}
+
+func (T *Buf) readInt16() (int16, bool) {
+	v, ok := T.readUint16()
+	if !ok {
+		return 0, false
+	}
+	return int16(v), true
+}
+
+func (T *Buf) readInt32() (int32, bool) {
+	v, ok := T.readUint32()
+	if !ok {
+		return 0, false
+	}
+	return int32(v), true
+}
+
+func (T *Buf) readInt64() (int64, bool) {
+	v, ok := T.readUint64()
+	if !ok {
+		return 0, false
+	}
+	return int64(v), true
+}
+
+func (T *Buf) readFloat32() (float32, bool) {
+	v, ok := T.readUint32()
+	if !ok {
+		return 0, false
+	}
+	return math.Float32frombits(v), true
+}
+
+func (T *Buf) readFloat64() (float64, bool) {
+	v, ok := T.readUint64()
+	if !ok {
+		return 0, false
+	}
+	return math.Float64frombits(v), true
+}
+
+func (T *Buf) readString() (string, bool) {
+	rem := T.remaining()
+	for i, c := range rem {
+		if c == 0 {
+			T.pos += i + 1
+			return string(rem[:i]), true
+		}
+	}
+	return "", false
+}
+
+func (T *Buf) readBytes(b []byte) bool {
+	rem := T.remaining()
+	if len(rem) < len(b) {
+		return false
+	}
+	T.pos += len(b)
+	copy(b, rem)
+	return true
+}
+
+func (T *Buf) readUnsafeBytes(count int) ([]byte, bool) {
+	rem := T.remaining()
+	if len(rem) < count {
+		return nil, false
+	}
+	T.pos += count
+	return rem[:count], true
+}
+
+// write methods
+
+func (T *Buf) resetWrite() {
+	T.buf = slices.Resize(T.buf, 5)
+	slices.Clear(T.buf)
+}
+
+func (T *Buf) writeType(typ Type) {
+	T.buf[0] = byte(typ)
+}
+
+func (T *Buf) writeUint8(v uint8) {
+	T.buf = append(T.buf, v)
+}
+
+func (T *Buf) writeUint16(v uint16) {
+	T.buf = binary.BigEndian.AppendUint16(T.buf, v)
+}
+
+func (T *Buf) writeUint32(v uint32) {
+	T.buf = binary.BigEndian.AppendUint32(T.buf, v)
+}
+
+func (T *Buf) writeUint64(v uint64) {
+	T.buf = binary.BigEndian.AppendUint64(T.buf, v)
+}
+
+func (T *Buf) writeInt8(v int8) {
+	T.writeUint8(uint8(v))
+}
+
+func (T *Buf) writeInt16(v int16) {
+	T.writeUint16(uint16(v))
+}
+
+func (T *Buf) writeInt32(v int32) {
+	T.writeUint32(uint32(v))
+}
+
+func (T *Buf) writeInt64(v int64) {
+	T.writeUint64(uint64(v))
+}
+
+func (T *Buf) writeFloat32(v float32) {
+	T.writeUint32(math.Float32bits(v))
+}
+
+func (T *Buf) writeFloat64(v float64) {
+	T.writeUint64(math.Float64bits(v))
+}
+
+func (T *Buf) writeString(v string) {
+	T.buf = append(T.buf, v...)
+	T.buf = append(T.buf, 0)
+}
+
+func (T *Buf) writeBytes(v []byte) {
+	T.buf = append(T.buf, v...)
+}
diff --git a/lib/zap/in.go b/lib/zap/in.go
new file mode 100644
index 0000000000000000000000000000000000000000..6cc3b9f62a58a3a412aa723b7db551ff509d502e
--- /dev/null
+++ b/lib/zap/in.go
@@ -0,0 +1,86 @@
+package zap
+
+type In struct {
+	buf *Buf
+	rev int
+}
+
+func (T In) Reset() {
+	T.buf.assertRev(T.rev)
+	T.buf.resetRead()
+}
+
+func (T In) Remaining() []byte {
+	T.buf.assertRev(T.rev)
+	return T.buf.remaining()
+}
+
+func (T In) Type() Type {
+	T.buf.assertRev(T.rev)
+	return T.buf.readType()
+}
+
+func (T In) Int8() (int8, bool) {
+	T.buf.assertRev(T.rev)
+	return T.buf.readInt8()
+}
+
+func (T In) Int16() (int16, bool) {
+	T.buf.assertRev(T.rev)
+	return T.buf.readInt16()
+}
+
+func (T In) Int32() (int32, bool) {
+	T.buf.assertRev(T.rev)
+	return T.buf.readInt32()
+}
+
+func (T In) Int64() (int64, bool) {
+	T.buf.assertRev(T.rev)
+	return T.buf.readInt64()
+}
+
+func (T In) Uint8() (uint8, bool) {
+	T.buf.assertRev(T.rev)
+	return T.buf.readUint8()
+}
+
+func (T In) Uint16() (uint16, bool) {
+	T.buf.assertRev(T.rev)
+	return T.buf.readUint16()
+}
+
+func (T In) Uint32() (uint32, bool) {
+	T.buf.assertRev(T.rev)
+	return T.buf.readUint32()
+}
+
+func (T In) Uint64() (uint64, bool) {
+	T.buf.assertRev(T.rev)
+	return T.buf.readUint64()
+}
+
+func (T In) Float32() (float32, bool) {
+	T.buf.assertRev(T.rev)
+	return T.buf.readFloat32()
+}
+
+func (T In) Float64() (float64, bool) {
+	T.buf.assertRev(T.rev)
+	return T.buf.readFloat64()
+}
+
+func (T In) String() (string, bool) {
+	T.buf.assertRev(T.rev)
+	return T.buf.readString()
+}
+
+func (T In) Bytes(b []byte) bool {
+	T.buf.assertRev(T.rev)
+	return T.buf.readBytes(b)
+}
+
+func (T In) UnsafeBytes(count int) ([]byte, bool) {
+	T.buf.assertRev(T.rev)
+	return T.buf.readUnsafeBytes(count)
+}
diff --git a/lib/zap/out.go b/lib/zap/out.go
new file mode 100644
index 0000000000000000000000000000000000000000..9aec94afd08cd8aaf7a9a3c95a28aa59b5a6de1a
--- /dev/null
+++ b/lib/zap/out.go
@@ -0,0 +1,81 @@
+package zap
+
+type Out struct {
+	buf *Buf
+	rev int
+}
+
+func (T Out) Reset() {
+	T.buf.assertRev(T.rev)
+	T.buf.resetWrite()
+}
+
+func (T Out) Full() []byte {
+	T.buf.assertRev(T.rev)
+	return T.buf.full()
+}
+
+func (T Out) Type(typ Type) {
+	T.buf.assertRev(T.rev)
+	T.buf.writeType(typ)
+}
+
+func (T Out) Int8(v int8) {
+	T.buf.assertRev(T.rev)
+	T.buf.writeInt8(v)
+}
+
+func (T Out) Int16(v int16) {
+	T.buf.assertRev(T.rev)
+	T.buf.writeInt16(v)
+}
+
+func (T Out) Int32(v int32) {
+	T.buf.assertRev(T.rev)
+	T.buf.writeInt32(v)
+}
+
+func (T Out) Int64(v int64) {
+	T.buf.assertRev(T.rev)
+	T.buf.writeInt64(v)
+}
+
+func (T Out) Uint8(v uint8) {
+	T.buf.assertRev(T.rev)
+	T.buf.writeUint8(v)
+}
+
+func (T Out) Uint16(v uint16) {
+	T.buf.assertRev(T.rev)
+	T.buf.writeUint16(v)
+}
+
+func (T Out) Uint32(v uint32) {
+	T.buf.assertRev(T.rev)
+	T.buf.writeUint32(v)
+}
+
+func (T Out) Uint64(v uint64) {
+	T.buf.assertRev(T.rev)
+	T.buf.writeUint64(v)
+}
+
+func (T Out) Float32(v float32) {
+	T.buf.assertRev(T.rev)
+	T.buf.writeFloat32(v)
+}
+
+func (T Out) Float64(v float64) {
+	T.buf.assertRev(T.rev)
+	T.buf.writeFloat64(v)
+}
+
+func (T Out) String(v string) {
+	T.buf.assertRev(T.rev)
+	T.buf.writeString(v)
+}
+
+func (T Out) Bytes(v []byte) {
+	T.buf.assertRev(T.rev)
+	T.buf.writeBytes(v)
+}
diff --git a/lib/zap/packets/v3.0/authenticationcleartext.go b/lib/zap/packets/v3.0/authenticationcleartext.go
new file mode 100644
index 0000000000000000000000000000000000000000..9b3ebdbc407014b18ae819bc278d301f60f0b79d
--- /dev/null
+++ b/lib/zap/packets/v3.0/authenticationcleartext.go
@@ -0,0 +1,26 @@
+package packets
+
+import (
+	"pggat2/lib/zap"
+)
+
+func ReadAuthenticationCleartext(in zap.In) bool {
+	in.Reset()
+	if in.Type() != Authentication {
+		return false
+	}
+	method, ok := in.Int32()
+	if !ok {
+		return false
+	}
+	if method != 3 {
+		return false
+	}
+	return true
+}
+
+func WriteAuthenticationCleartext(out zap.Out) {
+	out.Reset()
+	out.Type(Authentication)
+	out.Int32(3)
+}
diff --git a/lib/pnet/packet/packets/v3.0/authenticationmd5.go b/lib/zap/packets/v3.0/authenticationmd5.go
similarity index 58%
rename from lib/pnet/packet/packets/v3.0/authenticationmd5.go
rename to lib/zap/packets/v3.0/authenticationmd5.go
index bde2d1968d22ed9ce9aa108bb04dc641801f8a89..2bcef712c0335e3774aabb430ff0662fd1c83c13 100644
--- a/lib/pnet/packet/packets/v3.0/authenticationmd5.go
+++ b/lib/zap/packets/v3.0/authenticationmd5.go
@@ -1,10 +1,12 @@
 package packets
 
-import "pggat2/lib/pnet/packet"
+import (
+	"pggat2/lib/zap"
+)
 
-func ReadAuthenticationMD5(in packet.In) ([4]byte, bool) {
+func ReadAuthenticationMD5(in zap.In) ([4]byte, bool) {
 	in.Reset()
-	if in.Type() != packet.Authentication {
+	if in.Type() != Authentication {
 		return [4]byte{}, false
 	}
 	method, ok := in.Int32()
@@ -22,9 +24,9 @@ func ReadAuthenticationMD5(in packet.In) ([4]byte, bool) {
 	return salt, true
 }
 
-func WriteAuthenticationMD5(out packet.Out, salt [4]byte) {
+func WriteAuthenticationMD5(out zap.Out, salt [4]byte) {
 	out.Reset()
-	out.Type(packet.Authentication)
+	out.Type(Authentication)
 	out.Uint32(5)
 	out.Bytes(salt[:])
 }
diff --git a/lib/zap/packets/v3.0/authenticationok.go b/lib/zap/packets/v3.0/authenticationok.go
new file mode 100644
index 0000000000000000000000000000000000000000..cd682ec5a31f4bb49cb0fa3d6c82b275ee46ec16
--- /dev/null
+++ b/lib/zap/packets/v3.0/authenticationok.go
@@ -0,0 +1,26 @@
+package packets
+
+import (
+	"pggat2/lib/zap"
+)
+
+func ReadAuthenticationOk(in zap.In) bool {
+	in.Reset()
+	if in.Type() != Authentication {
+		return false
+	}
+	method, ok := in.Int32()
+	if !ok {
+		return false
+	}
+	if method != 0 {
+		return false
+	}
+	return true
+}
+
+func WriteAuthenticationOk(out zap.Out) {
+	out.Reset()
+	out.Type(Authentication)
+	out.Int32(0)
+}
diff --git a/lib/zap/packets/v3.0/authenticationresponse.go b/lib/zap/packets/v3.0/authenticationresponse.go
new file mode 100644
index 0000000000000000000000000000000000000000..1dd93e0247ee1d66426f1c56bf356ae2742f3f88
--- /dev/null
+++ b/lib/zap/packets/v3.0/authenticationresponse.go
@@ -0,0 +1,19 @@
+package packets
+
+import (
+	"pggat2/lib/zap"
+)
+
+func ReadAuthenticationResponse(in zap.In) ([]byte, bool) {
+	in.Reset()
+	if in.Type() != AuthenticationResponse {
+		return nil, false
+	}
+	return in.Remaining(), true
+}
+
+func WriteAuthenticationResponse(out zap.Out, resp []byte) {
+	out.Reset()
+	out.Type(AuthenticationResponse)
+	out.Bytes(resp)
+}
diff --git a/lib/pnet/packet/packets/v3.0/authenticationsasl.go b/lib/zap/packets/v3.0/authenticationsasl.go
similarity index 75%
rename from lib/pnet/packet/packets/v3.0/authenticationsasl.go
rename to lib/zap/packets/v3.0/authenticationsasl.go
index 9c5589b2b127fabf69158d6909d33974a93f6d30..5d6ba59fab4be8c8402f394ee3ba2117fb83ebef 100644
--- a/lib/pnet/packet/packets/v3.0/authenticationsasl.go
+++ b/lib/zap/packets/v3.0/authenticationsasl.go
@@ -1,10 +1,12 @@
 package packets
 
-import "pggat2/lib/pnet/packet"
+import (
+	"pggat2/lib/zap"
+)
 
-func ReadAuthenticationSASL(in packet.In) ([]string, bool) {
+func ReadAuthenticationSASL(in zap.In) ([]string, bool) {
 	in.Reset()
-	if in.Type() != packet.Authentication {
+	if in.Type() != Authentication {
 		return nil, false
 	}
 
@@ -44,9 +46,9 @@ func ReadAuthenticationSASL(in packet.In) ([]string, bool) {
 	return mechanisms, true
 }
 
-func WriteAuthenticationSASL(out packet.Out, mechanisms []string) {
+func WriteAuthenticationSASL(out zap.Out, mechanisms []string) {
 	out.Reset()
-	out.Type(packet.Authentication)
+	out.Type(Authentication)
 	out.Int32(10)
 	for _, mechanism := range mechanisms {
 		out.String(mechanism)
diff --git a/lib/zap/packets/v3.0/authenticationsaslcontinue.go b/lib/zap/packets/v3.0/authenticationsaslcontinue.go
new file mode 100644
index 0000000000000000000000000000000000000000..269a373feb431f9050e088fbc45f7fcf1574ba42
--- /dev/null
+++ b/lib/zap/packets/v3.0/authenticationsaslcontinue.go
@@ -0,0 +1,27 @@
+package packets
+
+import (
+	"pggat2/lib/zap"
+)
+
+func ReadAuthenticationSASLContinue(in zap.In) ([]byte, bool) {
+	in.Reset()
+	if in.Type() != Authentication {
+		return nil, false
+	}
+	method, ok := in.Int32()
+	if !ok {
+		return nil, false
+	}
+	if method != 11 {
+		return nil, false
+	}
+	return in.Remaining(), true
+}
+
+func WriteAuthenticationSASLContinue(out zap.Out, resp []byte) {
+	out.Reset()
+	out.Type(Authentication)
+	out.Int32(11)
+	out.Bytes(resp)
+}
diff --git a/lib/zap/packets/v3.0/authenticationsaslfinal.go b/lib/zap/packets/v3.0/authenticationsaslfinal.go
new file mode 100644
index 0000000000000000000000000000000000000000..747a95091d0e0d87d67f679dad1a0785dd51642f
--- /dev/null
+++ b/lib/zap/packets/v3.0/authenticationsaslfinal.go
@@ -0,0 +1,27 @@
+package packets
+
+import (
+	"pggat2/lib/zap"
+)
+
+func ReadAuthenticationSASLFinal(in zap.In) ([]byte, bool) {
+	in.Reset()
+	if in.Type() != Authentication {
+		return nil, false
+	}
+	method, ok := in.Int32()
+	if !ok {
+		return nil, false
+	}
+	if method != 12 {
+		return nil, false
+	}
+	return in.Remaining(), true
+}
+
+func WriteAuthenticationSASLFinal(out zap.Out, resp []byte) {
+	out.Reset()
+	out.Type(Authentication)
+	out.Int32(12)
+	out.Bytes(resp)
+}
diff --git a/lib/pnet/packet/packets/v3.0/backendkeydata.go b/lib/zap/packets/v3.0/backendkeydata.go
similarity index 51%
rename from lib/pnet/packet/packets/v3.0/backendkeydata.go
rename to lib/zap/packets/v3.0/backendkeydata.go
index c41ebd139031b08875840bf4e7efc65fca044f3a..feb05bac9571bfc2c4df9cc10dbf49d6653d70f4 100644
--- a/lib/pnet/packet/packets/v3.0/backendkeydata.go
+++ b/lib/zap/packets/v3.0/backendkeydata.go
@@ -1,10 +1,12 @@
 package packets
 
-import "pggat2/lib/pnet/packet"
+import (
+	"pggat2/lib/zap"
+)
 
-func ReadBackendKeyData(in packet.In) ([8]byte, bool) {
+func ReadBackendKeyData(in zap.In) ([8]byte, bool) {
 	in.Reset()
-	if in.Type() != packet.BackendKeyData {
+	if in.Type() != BackendKeyData {
 		return [8]byte{}, false
 	}
 	var cancellationKey [8]byte
@@ -15,8 +17,8 @@ func ReadBackendKeyData(in packet.In) ([8]byte, bool) {
 	return cancellationKey, true
 }
 
-func WriteBackendKeyData(out packet.Out, cancellationKey [8]byte) {
+func WriteBackendKeyData(out zap.Out, cancellationKey [8]byte) {
 	out.Reset()
-	out.Type(packet.BackendKeyData)
+	out.Type(BackendKeyData)
 	out.Bytes(cancellationKey[:])
 }
diff --git a/lib/pnet/packet/packets/v3.0/bind.go b/lib/zap/packets/v3.0/bind.go
similarity index 83%
rename from lib/pnet/packet/packets/v3.0/bind.go
rename to lib/zap/packets/v3.0/bind.go
index cb28e3e96ebda0a0267a78ef1ffda50901536877..caa0d871620b1d1789c171e1ebc5eb311194d340 100644
--- a/lib/pnet/packet/packets/v3.0/bind.go
+++ b/lib/zap/packets/v3.0/bind.go
@@ -1,10 +1,12 @@
 package packets
 
-import "pggat2/lib/pnet/packet"
+import (
+	"pggat2/lib/zap"
+)
 
-func ReadBind(in packet.In) (destination string, source string, parameterFormatCodes []int16, parameterValues [][]byte, resultFormatCodes []int16, ok bool) {
+func ReadBind(in zap.In) (destination string, source string, parameterFormatCodes []int16, parameterValues [][]byte, resultFormatCodes []int16, ok bool) {
 	in.Reset()
-	if in.Type() != packet.Bind {
+	if in.Type() != Bind {
 		return
 	}
 	destination, ok = in.String()
@@ -65,9 +67,9 @@ func ReadBind(in packet.In) (destination string, source string, parameterFormatC
 	return
 }
 
-func WriteBind(out packet.Out, destination, source string, parameterFormatCodes []int16, parameterValues [][]byte, resultFormatCodes []int16) {
+func WriteBind(out zap.Out, destination, source string, parameterFormatCodes []int16, parameterValues [][]byte, resultFormatCodes []int16) {
 	out.Reset()
-	out.Type(packet.Bind)
+	out.Type(Bind)
 	out.String(destination)
 	out.String(source)
 	out.Int16(int16(len(parameterFormatCodes)))
diff --git a/lib/zap/packets/v3.0/close.go b/lib/zap/packets/v3.0/close.go
new file mode 100644
index 0000000000000000000000000000000000000000..05e11bf9beb948e5ba639f7dbee8b3d5f9bcd6c6
--- /dev/null
+++ b/lib/zap/packets/v3.0/close.go
@@ -0,0 +1,28 @@
+package packets
+
+import (
+	"pggat2/lib/zap"
+)
+
+func ReadClose(in zap.In) (which uint8, target string, ok bool) {
+	in.Reset()
+	if in.Type() != Close {
+		return
+	}
+	which, ok = in.Uint8()
+	if !ok {
+		return
+	}
+	target, ok = in.String()
+	if !ok {
+		return
+	}
+	return
+}
+
+func WriteClose(out zap.Out, which uint8, target string) {
+	out.Reset()
+	out.Type(Close)
+	out.Uint8(which)
+	out.String(target)
+}
diff --git a/lib/zap/packets/v3.0/describe.go b/lib/zap/packets/v3.0/describe.go
new file mode 100644
index 0000000000000000000000000000000000000000..906abfbce91f265e1b1a729d8d8ece43a7300ccb
--- /dev/null
+++ b/lib/zap/packets/v3.0/describe.go
@@ -0,0 +1,28 @@
+package packets
+
+import (
+	"pggat2/lib/zap"
+)
+
+func ReadDescribe(in zap.In) (which uint8, target string, ok bool) {
+	in.Reset()
+	if in.Type() != Describe {
+		return
+	}
+	which, ok = in.Uint8()
+	if !ok {
+		return
+	}
+	target, ok = in.String()
+	if !ok {
+		return
+	}
+	return
+}
+
+func WriteDescribe(out zap.Out, which uint8, target string) {
+	out.Reset()
+	out.Type(Describe)
+	out.Uint8(which)
+	out.String(target)
+}
diff --git a/lib/pnet/packet/packets/v3.0/errorresponse.go b/lib/zap/packets/v3.0/errorresponse.go
similarity index 81%
rename from lib/pnet/packet/packets/v3.0/errorresponse.go
rename to lib/zap/packets/v3.0/errorresponse.go
index 60ee0ca58507ddf24c975981b842848a074bf3d9..2fae634108448d360bebf29b3c1de3fdcf514d83 100644
--- a/lib/pnet/packet/packets/v3.0/errorresponse.go
+++ b/lib/zap/packets/v3.0/errorresponse.go
@@ -2,12 +2,12 @@ package packets
 
 import (
 	"pggat2/lib/perror"
-	"pggat2/lib/pnet/packet"
+	"pggat2/lib/zap"
 )
 
-func ReadErrorResponse(in packet.In) (perror.Error, bool) {
+func ReadErrorResponse(in zap.In) (perror.Error, bool) {
 	in.Reset()
-	if in.Type() != packet.ErrorResponse {
+	if in.Type() != ErrorResponse {
 		return nil, false
 	}
 
@@ -54,9 +54,9 @@ func ReadErrorResponse(in packet.In) (perror.Error, bool) {
 	), true
 }
 
-func WriteErrorResponse(out packet.Out, err perror.Error) {
+func WriteErrorResponse(out zap.Out, err perror.Error) {
 	out.Reset()
-	out.Type(packet.ErrorResponse)
+	out.Type(ErrorResponse)
 
 	out.Uint8('S')
 	out.String(string(err.Severity()))
diff --git a/lib/zap/packets/v3.0/errors.go b/lib/zap/packets/v3.0/errors.go
new file mode 100644
index 0000000000000000000000000000000000000000..7fe488210c45ea31a7423b59c7de9da07c77515e
--- /dev/null
+++ b/lib/zap/packets/v3.0/errors.go
@@ -0,0 +1,16 @@
+package packets
+
+import "pggat2/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/zap/packets/v3.0/execute.go b/lib/zap/packets/v3.0/execute.go
new file mode 100644
index 0000000000000000000000000000000000000000..84fa064d11d75af53bc9b7c556fdbf481c4d57ef
--- /dev/null
+++ b/lib/zap/packets/v3.0/execute.go
@@ -0,0 +1,28 @@
+package packets
+
+import (
+	"pggat2/lib/zap"
+)
+
+func ReadExecute(in zap.In) (target string, maxRows int32, ok bool) {
+	in.Reset()
+	if in.Type() != Execute {
+		return
+	}
+	target, ok = in.String()
+	if !ok {
+		return
+	}
+	maxRows, ok = in.Int32()
+	if !ok {
+		return
+	}
+	return
+}
+
+func WriteExecute(out zap.Out, target string, maxRows int32) {
+	out.Reset()
+	out.Type(Execute)
+	out.String(target)
+	out.Int32(maxRows)
+}
diff --git a/lib/pnet/packet/packets/v3.0/negotiateprotocolversion.go b/lib/zap/packets/v3.0/negotiateprotocolversion.go
similarity index 65%
rename from lib/pnet/packet/packets/v3.0/negotiateprotocolversion.go
rename to lib/zap/packets/v3.0/negotiateprotocolversion.go
index 4ba56adc24013796b2f44f2346d50f971913c2b8..db2df2b807345d331eb041bc8a976c3ae5f5c93d 100644
--- a/lib/pnet/packet/packets/v3.0/negotiateprotocolversion.go
+++ b/lib/zap/packets/v3.0/negotiateprotocolversion.go
@@ -1,10 +1,12 @@
 package packets
 
-import "pggat2/lib/pnet/packet"
+import (
+	"pggat2/lib/zap"
+)
 
-func ReadNegotiateProtocolVersion(in packet.In) (minorProtocolVersion int32, unrecognizedOptions []string, ok bool) {
+func ReadNegotiateProtocolVersion(in zap.In) (minorProtocolVersion int32, unrecognizedOptions []string, ok bool) {
 	in.Reset()
-	if in.Type() != packet.NegotiateProtocolVersion {
+	if in.Type() != NegotiateProtocolVersion {
 		return
 	}
 	minorProtocolVersion, ok = in.Int32()
@@ -29,9 +31,9 @@ func ReadNegotiateProtocolVersion(in packet.In) (minorProtocolVersion int32, unr
 	return
 }
 
-func WriteNegotiateProtocolVersion(out packet.Out, minorProtocolVersion int32, unrecognizedOptions []string) {
+func WriteNegotiateProtocolVersion(out zap.Out, minorProtocolVersion int32, unrecognizedOptions []string) {
 	out.Reset()
-	out.Type(packet.NegotiateProtocolVersion)
+	out.Type(NegotiateProtocolVersion)
 	out.Int32(minorProtocolVersion)
 	out.Int32(int32(len(unrecognizedOptions)))
 	for _, option := range unrecognizedOptions {
diff --git a/lib/zap/packets/v3.0/parameterstatus.go b/lib/zap/packets/v3.0/parameterstatus.go
new file mode 100644
index 0000000000000000000000000000000000000000..09b304cf4700148761935b123d1077f2b72074aa
--- /dev/null
+++ b/lib/zap/packets/v3.0/parameterstatus.go
@@ -0,0 +1,28 @@
+package packets
+
+import (
+	"pggat2/lib/zap"
+)
+
+func ReadParameterStatus(in zap.In) (key, value string, ok bool) {
+	in.Reset()
+	if in.Type() != ParameterStatus {
+		return
+	}
+	key, ok = in.String()
+	if !ok {
+		return
+	}
+	value, ok = in.String()
+	if !ok {
+		return
+	}
+	return
+}
+
+func WriteParameterStatus(out zap.Out, key, value string) {
+	out.Reset()
+	out.Type(ParameterStatus)
+	out.String(key)
+	out.String(value)
+}
diff --git a/lib/pnet/packet/packets/v3.0/parse.go b/lib/zap/packets/v3.0/parse.go
similarity index 70%
rename from lib/pnet/packet/packets/v3.0/parse.go
rename to lib/zap/packets/v3.0/parse.go
index 3055e77d59bcd2faf7a2c67abf0b4a9ff3498428..087fe350b2a1bda4044575527f5aa5e1ca71d6cc 100644
--- a/lib/pnet/packet/packets/v3.0/parse.go
+++ b/lib/zap/packets/v3.0/parse.go
@@ -1,10 +1,12 @@
 package packets
 
-import "pggat2/lib/pnet/packet"
+import (
+	"pggat2/lib/zap"
+)
 
-func ReadParse(in packet.In) (destination string, query string, parameterDataTypes []int32, ok bool) {
+func ReadParse(in zap.In) (destination string, query string, parameterDataTypes []int32, ok bool) {
 	in.Reset()
-	if in.Type() != packet.Parse {
+	if in.Type() != Parse {
 		return
 	}
 	destination, ok = in.String()
@@ -32,9 +34,9 @@ func ReadParse(in packet.In) (destination string, query string, parameterDataTyp
 	return
 }
 
-func WriteParse(out packet.Out, destination string, query string, parameterDataTypes []int32) {
+func WriteParse(out zap.Out, destination string, query string, parameterDataTypes []int32) {
 	out.Reset()
-	out.Type(packet.Parse)
+	out.Type(Parse)
 	out.String(destination)
 	out.String(query)
 	out.Int16(int16(len(parameterDataTypes)))
diff --git a/lib/zap/packets/v3.0/passwordmessage.go b/lib/zap/packets/v3.0/passwordmessage.go
new file mode 100644
index 0000000000000000000000000000000000000000..89d16ad41ebf1915f4699c0a8d80bc814bb64fa1
--- /dev/null
+++ b/lib/zap/packets/v3.0/passwordmessage.go
@@ -0,0 +1,23 @@
+package packets
+
+import (
+	"pggat2/lib/zap"
+)
+
+func ReadPasswordMessage(in zap.In) (string, bool) {
+	in.Reset()
+	if in.Type() != AuthenticationResponse {
+		return "", false
+	}
+	password, ok := in.String()
+	if !ok {
+		return "", false
+	}
+	return password, true
+}
+
+func WritePasswordMessage(out zap.Out, password string) {
+	out.Reset()
+	out.Type(AuthenticationResponse)
+	out.String(password)
+}
diff --git a/lib/zap/packets/v3.0/readyforquery.go b/lib/zap/packets/v3.0/readyforquery.go
new file mode 100644
index 0000000000000000000000000000000000000000..b6d5ea82b71a5f2fd7d8df989e3b75f62a58b98b
--- /dev/null
+++ b/lib/zap/packets/v3.0/readyforquery.go
@@ -0,0 +1,23 @@
+package packets
+
+import (
+	"pggat2/lib/zap"
+)
+
+func ReadReadyForQuery(in zap.In) (byte, bool) {
+	in.Reset()
+	if in.Type() != ReadyForQuery {
+		return 0, false
+	}
+	state, ok := in.Uint8()
+	if !ok {
+		return 0, false
+	}
+	return state, true
+}
+
+func WriteReadyForQuery(out zap.Out, state uint8) {
+	out.Reset()
+	out.Type(ReadyForQuery)
+	out.Uint8(state)
+}
diff --git a/lib/pnet/packet/packets/v3.0/saslinitialresponse.go b/lib/zap/packets/v3.0/saslinitialresponse.go
similarity index 60%
rename from lib/pnet/packet/packets/v3.0/saslinitialresponse.go
rename to lib/zap/packets/v3.0/saslinitialresponse.go
index 75cb65040980d0084de544ed36c2a2c0416e9982..4946f9bebd8b963501ae8d36b93069b9ac5bba35 100644
--- a/lib/pnet/packet/packets/v3.0/saslinitialresponse.go
+++ b/lib/zap/packets/v3.0/saslinitialresponse.go
@@ -1,10 +1,12 @@
 package packets
 
-import "pggat2/lib/pnet/packet"
+import (
+	"pggat2/lib/zap"
+)
 
-func ReadSASLInitialResponse(in packet.In) (mechanism string, initialResponse []byte, ok bool) {
+func ReadSASLInitialResponse(in zap.In) (mechanism string, initialResponse []byte, ok bool) {
 	in.Reset()
-	if in.Type() != packet.AuthenticationResponse {
+	if in.Type() != AuthenticationResponse {
 		return
 	}
 
@@ -26,9 +28,9 @@ func ReadSASLInitialResponse(in packet.In) (mechanism string, initialResponse []
 	return
 }
 
-func WriteSASLInitialResponse(out packet.Out, mechanism string, initialResponse []byte) {
+func WriteSASLInitialResponse(out zap.Out, mechanism string, initialResponse []byte) {
 	out.Reset()
-	out.Type(packet.AuthenticationResponse)
+	out.Type(AuthenticationResponse)
 	out.String(mechanism)
 	if initialResponse == nil {
 		out.Int32(-1)
diff --git a/lib/zap/packets/v3.0/types.go b/lib/zap/packets/v3.0/types.go
new file mode 100644
index 0000000000000000000000000000000000000000..68b3740658189194077803a62ce5e3d9e9d545a5
--- /dev/null
+++ b/lib/zap/packets/v3.0/types.go
@@ -0,0 +1,43 @@
+package packets
+
+import "pggat2/lib/zap"
+
+const (
+	None                     zap.Type = 0
+	Authentication           zap.Type = 'R'
+	BackendKeyData           zap.Type = 'K'
+	Bind                     zap.Type = 'B'
+	BindComplete             zap.Type = '2'
+	Close                    zap.Type = 'C'
+	CloseComplete            zap.Type = '3'
+	CommandComplete          zap.Type = 'C'
+	CopyData                 zap.Type = 'd'
+	CopyDone                 zap.Type = 'c'
+	CopyFail                 zap.Type = 'f'
+	CopyInResponse           zap.Type = 'G'
+	CopyOutResponse          zap.Type = 'H'
+	CopyBothResponse         zap.Type = 'W'
+	DataRow                  zap.Type = 'D'
+	Describe                 zap.Type = 'D'
+	EmptyQueryResponse       zap.Type = 'I'
+	ErrorResponse            zap.Type = 'E'
+	Execute                  zap.Type = 'E'
+	Flush                    zap.Type = 'H'
+	FunctionCall             zap.Type = 'F'
+	FunctionCallResponse     zap.Type = 'V'
+	AuthenticationResponse   zap.Type = 'p'
+	NegotiateProtocolVersion zap.Type = 'v'
+	NoData                   zap.Type = 'n'
+	NoticeResponse           zap.Type = 'N'
+	NotificationResponse     zap.Type = 'A'
+	ParameterDescription     zap.Type = 't'
+	ParameterStatus          zap.Type = 'S'
+	Parse                    zap.Type = 'P'
+	ParseComplete            zap.Type = '1'
+	PortalSuspended          zap.Type = 's'
+	Query                    zap.Type = 'Q'
+	ReadyForQuery            zap.Type = 'Z'
+	RowDescription           zap.Type = 'T'
+	Sync                     zap.Type = 'S'
+	Terminate                zap.Type = 'X'
+)
diff --git a/lib/zap/reader.go b/lib/zap/reader.go
new file mode 100644
index 0000000000000000000000000000000000000000..aa4f149623f0546bed8bf70f83426039669e7a02
--- /dev/null
+++ b/lib/zap/reader.go
@@ -0,0 +1,10 @@
+package zap
+
+import "io"
+
+type Reader interface {
+	io.ByteReader
+
+	Read() (In, error)
+	ReadUntyped() (In, error)
+}
diff --git a/lib/pnet/readwriter.go b/lib/zap/readwriter.go
similarity index 78%
rename from lib/pnet/readwriter.go
rename to lib/zap/readwriter.go
index 81d125660a867ab52c11808e17355925925ed053..22b2a309250d2e694b5a5c9ef842a57780499e85 100644
--- a/lib/pnet/readwriter.go
+++ b/lib/zap/readwriter.go
@@ -1,4 +1,4 @@
-package pnet
+package zap
 
 type ReadWriter interface {
 	Reader
diff --git a/lib/zap/regender.go b/lib/zap/regender.go
new file mode 100644
index 0000000000000000000000000000000000000000..9adb2ee7a3471c1b00320be04f6d750b93c28e7c
--- /dev/null
+++ b/lib/zap/regender.go
@@ -0,0 +1,15 @@
+package zap
+
+func InToOut(in In) Out {
+	return Out{
+		buf: in.buf,
+		rev: in.rev,
+	}
+}
+
+func OutToIn(out Out) In {
+	return In{
+		buf: out.buf,
+		rev: out.rev,
+	}
+}
diff --git a/lib/zap/type.go b/lib/zap/type.go
new file mode 100644
index 0000000000000000000000000000000000000000..e26e5d617631afcbe8116694f1b01d219c8862cb
--- /dev/null
+++ b/lib/zap/type.go
@@ -0,0 +1,3 @@
+package zap
+
+type Type byte
diff --git a/lib/zap/writer.go b/lib/zap/writer.go
new file mode 100644
index 0000000000000000000000000000000000000000..90e9354e4b0d2c255b661a943c25052fbf977c31
--- /dev/null
+++ b/lib/zap/writer.go
@@ -0,0 +1,10 @@
+package zap
+
+import "io"
+
+type Writer interface {
+	io.ByteWriter
+
+	Write() Out
+	Send(Out) error
+}
diff --git a/lib/zap/zio/reader.go b/lib/zap/zio/reader.go
new file mode 100644
index 0000000000000000000000000000000000000000..0cd39047df35a6c505e417d46adead69ef311c82
--- /dev/null
+++ b/lib/zap/zio/reader.go
@@ -0,0 +1,32 @@
+package zio
+
+import (
+	"io"
+
+	"pggat2/lib/zap"
+)
+
+type Reader struct {
+	reader io.Reader
+	buf    zap.Buf
+}
+
+func MakeReader(reader io.Reader) Reader {
+	return Reader{
+		reader: reader,
+	}
+}
+
+func (T *Reader) ReadByte() (byte, error) {
+	return T.buf.ReadByte(T.reader)
+}
+
+func (T *Reader) Read() (zap.In, error) {
+	return T.buf.Read(T.reader, true)
+}
+
+func (T *Reader) ReadUntyped() (zap.In, error) {
+	return T.buf.Read(T.reader, false)
+}
+
+var _ zap.Reader = (*Reader)(nil)
diff --git a/lib/zap/zio/readwriter.go b/lib/zap/zio/readwriter.go
new file mode 100644
index 0000000000000000000000000000000000000000..3a4d853de71a71b0b445b8ca421b78d06053286f
--- /dev/null
+++ b/lib/zap/zio/readwriter.go
@@ -0,0 +1,45 @@
+package zio
+
+import (
+	"io"
+
+	"pggat2/lib/zap"
+)
+
+type ReadWriter struct {
+	rw  io.ReadWriter
+	buf zap.Buf
+}
+
+func MakeReadWriter(rw io.ReadWriter) ReadWriter {
+	return ReadWriter{
+		rw: rw,
+	}
+}
+
+func (T *ReadWriter) ReadByte() (byte, error) {
+	return T.buf.ReadByte(T.rw)
+}
+
+func (T *ReadWriter) Read() (zap.In, error) {
+	return T.buf.Read(T.rw, true)
+}
+
+func (T *ReadWriter) ReadUntyped() (zap.In, error) {
+	return T.buf.Read(T.rw, false)
+}
+
+func (T *ReadWriter) WriteByte(b byte) error {
+	return T.buf.WriteByte(T.rw, b)
+}
+
+func (T *ReadWriter) Write() zap.Out {
+	return T.buf.Write()
+}
+
+func (T *ReadWriter) Send(out zap.Out) error {
+	_, err := T.rw.Write(out.Full())
+	return err
+}
+
+var _ zap.ReadWriter = (*ReadWriter)(nil)
diff --git a/lib/zap/zio/writer.go b/lib/zap/zio/writer.go
new file mode 100644
index 0000000000000000000000000000000000000000..8b5c7a3e5b4f663362db1b62b33c1c1b711969bc
--- /dev/null
+++ b/lib/zap/zio/writer.go
@@ -0,0 +1,33 @@
+package zio
+
+import (
+	"io"
+
+	"pggat2/lib/zap"
+)
+
+type Writer struct {
+	writer io.Writer
+	buf    zap.Buf
+}
+
+func MakeWriter(writer io.Writer) Writer {
+	return Writer{
+		writer: writer,
+	}
+}
+
+func (T *Writer) WriteByte(b byte) error {
+	return T.buf.WriteByte(T.writer, b)
+}
+
+func (T *Writer) Write() zap.Out {
+	return T.buf.Write()
+}
+
+func (T *Writer) Send(out zap.Out) error {
+	_, err := T.writer.Write(out.Full())
+	return err
+}
+
+var _ zap.Writer = (*Writer)(nil)