From da43caa8cd81339a14e7abf791be245769eb60c1 Mon Sep 17 00:00:00 2001
From: a <a@tuxpa.in>
Date: Sat, 3 Jun 2023 04:25:06 -0500
Subject: [PATCH] refactor

---
 contrib/codecs/codecs.go                      |  7 +++
 {pkg/codec => contrib}/codecs/http/client.go  | 20 ++++----
 .../codecs/http/client_test.go                |  7 +--
 {pkg/codec => contrib}/codecs/http/codec.go   |  6 +--
 {pkg/codec => contrib}/codecs/http/const.go   |  0
 {pkg/codec => contrib}/codecs/http/handler.go |  5 +-
 .../codecs/http/http_test.go                  |  6 ++-
 .../codec => contrib}/codecs/inproc/client.go | 16 +++---
 .../codec => contrib}/codecs/inproc/inproc.go |  3 +-
 .../codecs/inproc/inproc_test.go              | 12 ++---
 {pkg/codec => contrib}/codecs/ipc/ipc.go      |  0
 {pkg/codec => contrib}/codecs/ipc/ipc_js.go   |  0
 {pkg/codec => contrib}/codecs/ipc/ipc_test.go |  0
 {pkg/codec => contrib}/codecs/ipc/ipc_unix.go |  0
 .../codecs/ipc/ipc_windows.go                 |  0
 .../codecs/stdio/server_test.go               |  0
 {pkg/codec => contrib}/codecs/stdio/stdio.go  |  0
 .../codecs/websocket/client.go                |  3 +-
 .../codecs/websocket/client_example_test.go   |  0
 .../codecs/websocket/codec.go                 |  0
 .../codecs/websocket/const.go                 |  0
 .../codecs/websocket/dial.go                  |  0
 .../codecs/websocket/websocket.go             |  6 +--
 .../codecs/websocket/websocket_test.go        | 37 +++++++-------
 .../codecs/websocket/wsjson/writer_test.go    |  0
 .../codecs/websocket/wsjson/wsjson.go         |  0
 {pkg => contrib}/handlers/argreflect/json.go  |  2 +-
 .../handlers/argreflect/reflect_handler.go    | 12 ++---
 {pkg => contrib}/jmux/mux.go                  | 49 +++++++++----------
 {pkg => contrib}/jmux/router.go               | 20 ++++----
 {pkg => contrib}/jmux/router_chain.go         | 18 ++++---
 {pkg => contrib}/jmux/router_context.go       |  5 +-
 {pkg => contrib}/jmux/router_tree.go          | 19 ++++---
 contrib/middleware/log.go                     |  8 +--
 contrib/middleware/middleware.go              | 10 ++--
 contrib/middleware/recoverer.go               |  9 ++--
 contrib/middleware/request_id.go              |  9 ++--
 contrib/openrpc/out/example/main.go           |  3 +-
 {pkg => contrib}/subscription/conn.go         |  5 +-
 {pkg => contrib}/subscription/engine.go       |  0
 {pkg => contrib}/subscription/subscription.go |  0
 doc.go                                        |  4 ++
 example/echo/main.go                          |  6 ++-
 example/proxy/main.go                         |  6 ++-
 example/subscription/main.go                  |  8 +--
 exports.go                                    | 48 ++++++++++++++++++
 pkg/clientutil/helper.go                      |  4 +-
 pkg/codec/codecs/codecs.go                    |  5 --
 helper.go => pkg/codec/helper.go              |  9 ++--
 jrpc.go => pkg/codec/jrpc.go                  |  2 +-
 pkg/codec/json.go                             |  2 +-
 request.go => pkg/codec/reqresp.go            | 45 ++++++++++++-----
 pkg/jrpctest/server.go                        | 17 +++----
 pkg/jrpctest/services.go                      | 12 ++---
 pkg/jrpctest/suites.go                        | 45 ++++++++---------
 server.go => pkg/server/server.go             | 15 +++---
 readme.md                                     | 40 +++++++++------
 response.go                                   | 26 ----------
 58 files changed, 323 insertions(+), 268 deletions(-)
 create mode 100644 contrib/codecs/codecs.go
 rename {pkg/codec => contrib}/codecs/http/client.go (86%)
 rename {pkg/codec => contrib}/codecs/http/client_test.go (71%)
 rename {pkg/codec => contrib}/codecs/http/codec.go (97%)
 rename {pkg/codec => contrib}/codecs/http/const.go (100%)
 rename {pkg/codec => contrib}/codecs/http/handler.go (83%)
 rename {pkg/codec => contrib}/codecs/http/http_test.go (98%)
 rename {pkg/codec => contrib}/codecs/inproc/client.go (84%)
 rename {pkg/codec => contrib}/codecs/inproc/inproc.go (99%)
 rename {pkg/codec => contrib}/codecs/inproc/inproc_test.go (60%)
 rename {pkg/codec => contrib}/codecs/ipc/ipc.go (100%)
 rename {pkg/codec => contrib}/codecs/ipc/ipc_js.go (100%)
 rename {pkg/codec => contrib}/codecs/ipc/ipc_test.go (100%)
 rename {pkg/codec => contrib}/codecs/ipc/ipc_unix.go (100%)
 rename {pkg/codec => contrib}/codecs/ipc/ipc_windows.go (100%)
 rename {pkg/codec => contrib}/codecs/stdio/server_test.go (100%)
 rename {pkg/codec => contrib}/codecs/stdio/stdio.go (100%)
 rename {pkg/codec => contrib}/codecs/websocket/client.go (90%)
 rename {pkg/codec => contrib}/codecs/websocket/client_example_test.go (100%)
 rename {pkg/codec => contrib}/codecs/websocket/codec.go (100%)
 rename {pkg/codec => contrib}/codecs/websocket/const.go (100%)
 rename {pkg/codec => contrib}/codecs/websocket/dial.go (100%)
 rename {pkg/codec => contrib}/codecs/websocket/websocket.go (95%)
 rename {pkg/codec => contrib}/codecs/websocket/websocket_test.go (83%)
 rename {pkg/codec => contrib}/codecs/websocket/wsjson/writer_test.go (100%)
 rename {pkg/codec => contrib}/codecs/websocket/wsjson/wsjson.go (100%)
 rename {pkg => contrib}/handlers/argreflect/json.go (96%)
 rename {pkg => contrib}/handlers/argreflect/reflect_handler.go (92%)
 rename {pkg => contrib}/jmux/mux.go (89%)
 rename {pkg => contrib}/jmux/router.go (83%)
 rename {pkg => contrib}/jmux/router_chain.go (68%)
 rename {pkg => contrib}/jmux/router_context.go (98%)
 rename {pkg => contrib}/jmux/router_tree.go (97%)
 rename {pkg => contrib}/subscription/conn.go (77%)
 rename {pkg => contrib}/subscription/engine.go (100%)
 rename {pkg => contrib}/subscription/subscription.go (100%)
 create mode 100644 exports.go
 delete mode 100644 pkg/codec/codecs/codecs.go
 rename helper.go => pkg/codec/helper.go (90%)
 rename jrpc.go => pkg/codec/jrpc.go (99%)
 rename request.go => pkg/codec/reqresp.go (72%)
 rename server.go => pkg/server/server.go (97%)
 delete mode 100644 response.go

diff --git a/contrib/codecs/codecs.go b/contrib/codecs/codecs.go
new file mode 100644
index 0000000..f9b42ca
--- /dev/null
+++ b/contrib/codecs/codecs.go
@@ -0,0 +1,7 @@
+package codecs
+
+import (
+	"gfx.cafe/open/jrpc/contrib/codecs/inproc"
+)
+
+var NewInProc = inproc.NewCodec
diff --git a/pkg/codec/codecs/http/client.go b/contrib/codecs/http/client.go
similarity index 86%
rename from pkg/codec/codecs/http/client.go
rename to contrib/codecs/http/client.go
index c31d568..542879e 100644
--- a/pkg/codec/codecs/http/client.go
+++ b/contrib/codecs/http/client.go
@@ -6,16 +6,14 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"gfx.cafe/open/jrpc/pkg/codec"
 	"io"
 	"net/http"
 	"sync/atomic"
 	"time"
 
 	"gfx.cafe/open/jrpc/pkg/clientutil"
-	"gfx.cafe/open/jrpc/pkg/codec"
 	"gfx.cafe/util/go/bufpool"
-
-	"gfx.cafe/open/jrpc"
 )
 
 var (
@@ -32,7 +30,7 @@ const (
 	subscribeTimeout   = 5 * time.Second  // overall timeout eth_subscribe, rpc_modules calls
 )
 
-var _ jrpc.Conn = (*Client)(nil)
+var _ codec.Conn = (*Client)(nil)
 
 // Client represents a connection to an RPC server.
 type Client struct {
@@ -57,7 +55,7 @@ func (c *Client) SetHeader(key string, value string) {
 }
 
 func (c *Client) Do(ctx context.Context, result any, method string, params any) error {
-	req := jrpc.NewRequestInt(ctx, int(c.id.Add(1)), method, params)
+	req := codec.NewRequestInt(ctx, int(c.id.Add(1)), method, params)
 	resp, err := c.post(req)
 	if err != nil {
 		return err
@@ -90,7 +88,7 @@ func (c *Client) Do(ctx context.Context, result any, method string, params any)
 	return nil
 }
 
-func (c *Client) post(req *jrpc.Request) (*http.Response, error) {
+func (c *Client) post(req *codec.Request) (*http.Response, error) {
 	//TODO: use buffer for this
 	buf := bufpool.GetStd()
 	defer bufpool.PutStd(buf)
@@ -112,7 +110,7 @@ func (c *Client) post(req *jrpc.Request) (*http.Response, error) {
 }
 
 func (c *Client) Notify(ctx context.Context, method string, params any) error {
-	req := jrpc.NewNotification(ctx, method, params)
+	req := codec.NewNotification(ctx, method, params)
 	resp, err := c.post(req)
 	if err != nil {
 		return err
@@ -121,16 +119,16 @@ func (c *Client) Notify(ctx context.Context, method string, params any) error {
 	return err
 }
 
-func (c *Client) BatchCall(ctx context.Context, b ...*jrpc.BatchElem) error {
-	reqs := make([]*jrpc.Request, len(b))
+func (c *Client) BatchCall(ctx context.Context, b ...*codec.BatchElem) error {
+	reqs := make([]*codec.Request, len(b))
 	ids := make([]int, 0, len(b))
 	for _, v := range b {
 		if v.IsNotification {
-			reqs = append(reqs, jrpc.NewRequest(ctx, "", v.Method, v.Params))
+			reqs = append(reqs, codec.NewRequest(ctx, "", v.Method, v.Params))
 		} else {
 			id := int(c.id.Add(1))
 			ids = append(ids, id)
-			reqs = append(reqs, jrpc.NewRequestInt(ctx, id, v.Method, v.Params))
+			reqs = append(reqs, codec.NewRequestInt(ctx, id, v.Method, v.Params))
 		}
 	}
 	dat, err := json.Marshal(b)
diff --git a/pkg/codec/codecs/http/client_test.go b/contrib/codecs/http/client_test.go
similarity index 71%
rename from pkg/codec/codecs/http/client_test.go
rename to contrib/codecs/http/client_test.go
index b99934a..822c36c 100644
--- a/pkg/codec/codecs/http/client_test.go
+++ b/contrib/codecs/http/client_test.go
@@ -1,20 +1,21 @@
 package http
 
 import (
+	"gfx.cafe/open/jrpc/pkg/codec"
+	"gfx.cafe/open/jrpc/pkg/server"
 	"net/http/httptest"
 	"testing"
 
-	"gfx.cafe/open/jrpc"
 	"gfx.cafe/open/jrpc/pkg/jrpctest"
 	"github.com/stretchr/testify/require"
 )
 
 func TestBasicSuite(t *testing.T) {
 	jrpctest.RunBasicTestSuite(t, jrpctest.BasicTestSuiteArgs{
-		ServerMaker: func() (*jrpc.Server, jrpctest.ClientMaker, func()) {
+		ServerMaker: func() (*server.Server, jrpctest.ClientMaker, func()) {
 			s := jrpctest.NewServer()
 			hsrv := httptest.NewServer(&Server{Server: s})
-			return s, func() jrpc.Conn {
+			return s, func() codec.Conn {
 				conn, err := DialHTTP(hsrv.URL)
 				require.NoError(t, err)
 				return conn
diff --git a/pkg/codec/codecs/http/codec.go b/contrib/codecs/http/codec.go
similarity index 97%
rename from pkg/codec/codecs/http/codec.go
rename to contrib/codecs/http/codec.go
index 4ebad41..2faf2ba 100644
--- a/pkg/codec/codecs/http/codec.go
+++ b/contrib/codecs/http/codec.go
@@ -7,13 +7,11 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"gfx.cafe/open/jrpc/pkg/codec"
 	"io"
 	"mime"
 	"net/http"
 	"net/url"
-
-	"gfx.cafe/open/jrpc"
-	"gfx.cafe/open/jrpc/pkg/codec"
 )
 
 type Codec struct {
@@ -85,7 +83,7 @@ func (r *Codec) doReadGet() (msgs json.RawMessage, err error) {
 	if id == "" {
 		id = "1"
 	}
-	req := jrpc.NewRequest(r.ctx, id, method_up, json.RawMessage(param))
+	req := codec.NewRequest(r.ctx, id, method_up, json.RawMessage(param))
 	return req.MarshalJSON()
 }
 
diff --git a/pkg/codec/codecs/http/const.go b/contrib/codecs/http/const.go
similarity index 100%
rename from pkg/codec/codecs/http/const.go
rename to contrib/codecs/http/const.go
diff --git a/pkg/codec/codecs/http/handler.go b/contrib/codecs/http/handler.go
similarity index 83%
rename from pkg/codec/codecs/http/handler.go
rename to contrib/codecs/http/handler.go
index c4f74a5..90a4139 100644
--- a/pkg/codec/codecs/http/handler.go
+++ b/contrib/codecs/http/handler.go
@@ -1,13 +1,12 @@
 package http
 
 import (
+	"gfx.cafe/open/jrpc/pkg/server"
 	"net/http"
-
-	"gfx.cafe/open/jrpc"
 )
 
 type Server struct {
-	Server *jrpc.Server
+	Server *server.Server
 }
 
 func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/codec/codecs/http/http_test.go b/contrib/codecs/http/http_test.go
similarity index 98%
rename from pkg/codec/codecs/http/http_test.go
rename to contrib/codecs/http/http_test.go
index 9f8e4b0..91b3d8c 100644
--- a/pkg/codec/codecs/http/http_test.go
+++ b/contrib/codecs/http/http_test.go
@@ -25,8 +25,10 @@ import (
 	"time"
 
 	"gfx.cafe/open/jrpc"
+	"gfx.cafe/open/jrpc/contrib/jmux"
 	"gfx.cafe/open/jrpc/pkg/codec"
-	"gfx.cafe/open/jrpc/pkg/jmux"
+	"gfx.cafe/open/jrpc/pkg/server"
+
 	"gfx.cafe/open/jrpc/pkg/jrpctest"
 )
 
@@ -88,7 +90,7 @@ func TestHTTPErrorResponseWithValidRequest(t *testing.T) {
 
 func confirmHTTPRequestYieldsStatusCode(t *testing.T, method, contentType, body string, expectedStatusCode int) {
 	t.Helper()
-	s := jrpc.NewServer(jmux.NewMux())
+	s := server.NewServer(jmux.NewMux())
 	defer s.Stop()
 	ts := httptest.NewServer(&Server{Server: s})
 	defer ts.Close()
diff --git a/pkg/codec/codecs/inproc/client.go b/contrib/codecs/inproc/client.go
similarity index 84%
rename from pkg/codec/codecs/inproc/client.go
rename to contrib/codecs/inproc/client.go
index 2bdae6f..0d85933 100644
--- a/pkg/codec/codecs/inproc/client.go
+++ b/contrib/codecs/inproc/client.go
@@ -7,18 +7,16 @@ import (
 	"gfx.cafe/open/jrpc/pkg/clientutil"
 	"gfx.cafe/open/jrpc/pkg/codec"
 	"sync"
-
-	"gfx.cafe/open/jrpc"
 )
 
 type Client struct {
 	p *clientutil.IdReply
 	c *Codec
 
-	handler jrpc.Handler
+	handler codec.Handler
 }
 
-func NewClient(c *Codec, handler jrpc.Handler) *Client {
+func NewClient(c *Codec, handler codec.Handler) *Client {
 	cl := &Client{
 		p:       clientutil.NewIdReply(),
 		c:       c,
@@ -52,7 +50,7 @@ func (c *Client) listen() error {
 
 func (c *Client) Do(ctx context.Context, result any, method string, params any) error {
 	id := c.p.NextId()
-	req := jrpc.NewRequestInt(ctx, id, method, params)
+	req := codec.NewRequestInt(ctx, id, method, params)
 	fwd, err := json.Marshal(req)
 	if err != nil {
 		return err
@@ -71,14 +69,14 @@ func (c *Client) Do(ctx context.Context, result any, method string, params any)
 	return nil
 }
 
-func (c *Client) BatchCall(ctx context.Context, b ...*jrpc.BatchElem) error {
+func (c *Client) BatchCall(ctx context.Context, b ...*codec.BatchElem) error {
 	buf := new(bytes.Buffer)
 	enc := json.NewEncoder(buf)
-	reqs := make([]*jrpc.Request, 0, len(b))
+	reqs := make([]*codec.Request, 0, len(b))
 	ids := make([]int, 0, len(b))
 	for _, v := range b {
 		id := c.p.NextId()
-		req := jrpc.NewRequestInt(ctx, id, v.Method, v.Params)
+		req := codec.NewRequestInt(ctx, id, v.Method, v.Params)
 		ids = append(ids, id)
 		reqs = append(reqs, req)
 	}
@@ -121,7 +119,7 @@ func (c *Client) Close() error {
 }
 
 func (c *Client) Notify(ctx context.Context, method string, params any) error {
-	req := jrpc.NewRequest(ctx, "", method, params)
+	req := codec.NewRequest(ctx, "", method, params)
 	fwd, err := json.Marshal(req)
 	if err != nil {
 		return err
diff --git a/pkg/codec/codecs/inproc/inproc.go b/contrib/codecs/inproc/inproc.go
similarity index 99%
rename from pkg/codec/codecs/inproc/inproc.go
rename to contrib/codecs/inproc/inproc.go
index 53b60de..1d86952 100644
--- a/pkg/codec/codecs/inproc/inproc.go
+++ b/contrib/codecs/inproc/inproc.go
@@ -4,9 +4,8 @@ import (
 	"bufio"
 	"context"
 	"encoding/json"
-	"io"
-
 	"gfx.cafe/open/jrpc/pkg/codec"
+	"io"
 )
 
 type Codec struct {
diff --git a/pkg/codec/codecs/inproc/inproc_test.go b/contrib/codecs/inproc/inproc_test.go
similarity index 60%
rename from pkg/codec/codecs/inproc/inproc_test.go
rename to contrib/codecs/inproc/inproc_test.go
index c49ca17..5351566 100644
--- a/pkg/codec/codecs/inproc/inproc_test.go
+++ b/contrib/codecs/inproc/inproc_test.go
@@ -2,22 +2,22 @@ package inproc_test
 
 import (
 	"context"
-	"gfx.cafe/open/jrpc/pkg/codec/codecs/inproc"
-	"gfx.cafe/open/jrpc/pkg/jmux"
+	inproc2 "gfx.cafe/open/jrpc/contrib/codecs/inproc"
+	"gfx.cafe/open/jrpc/contrib/jmux"
+	"gfx.cafe/open/jrpc/pkg/server"
 	"testing"
 
-	"gfx.cafe/open/jrpc"
 	"github.com/stretchr/testify/require"
 )
 
 func TestInprocSetup(t *testing.T) {
 	mux := jmux.NewMux()
-	srv := jrpc.NewServer(mux)
+	srv := server.NewServer(mux)
 
 	ctx := context.Background()
 
-	clientCodec := inproc.NewCodec()
-	client := inproc.NewClient(clientCodec, nil)
+	clientCodec := inproc2.NewCodec()
+	client := inproc2.NewClient(clientCodec, nil)
 	go func() {
 		srv.ServeCodec(ctx, clientCodec)
 	}()
diff --git a/pkg/codec/codecs/ipc/ipc.go b/contrib/codecs/ipc/ipc.go
similarity index 100%
rename from pkg/codec/codecs/ipc/ipc.go
rename to contrib/codecs/ipc/ipc.go
diff --git a/pkg/codec/codecs/ipc/ipc_js.go b/contrib/codecs/ipc/ipc_js.go
similarity index 100%
rename from pkg/codec/codecs/ipc/ipc_js.go
rename to contrib/codecs/ipc/ipc_js.go
diff --git a/pkg/codec/codecs/ipc/ipc_test.go b/contrib/codecs/ipc/ipc_test.go
similarity index 100%
rename from pkg/codec/codecs/ipc/ipc_test.go
rename to contrib/codecs/ipc/ipc_test.go
diff --git a/pkg/codec/codecs/ipc/ipc_unix.go b/contrib/codecs/ipc/ipc_unix.go
similarity index 100%
rename from pkg/codec/codecs/ipc/ipc_unix.go
rename to contrib/codecs/ipc/ipc_unix.go
diff --git a/pkg/codec/codecs/ipc/ipc_windows.go b/contrib/codecs/ipc/ipc_windows.go
similarity index 100%
rename from pkg/codec/codecs/ipc/ipc_windows.go
rename to contrib/codecs/ipc/ipc_windows.go
diff --git a/pkg/codec/codecs/stdio/server_test.go b/contrib/codecs/stdio/server_test.go
similarity index 100%
rename from pkg/codec/codecs/stdio/server_test.go
rename to contrib/codecs/stdio/server_test.go
diff --git a/pkg/codec/codecs/stdio/stdio.go b/contrib/codecs/stdio/stdio.go
similarity index 100%
rename from pkg/codec/codecs/stdio/stdio.go
rename to contrib/codecs/stdio/stdio.go
diff --git a/pkg/codec/codecs/websocket/client.go b/contrib/codecs/websocket/client.go
similarity index 90%
rename from pkg/codec/codecs/websocket/client.go
rename to contrib/codecs/websocket/client.go
index 2f16f1d..86b7f35 100644
--- a/pkg/codec/codecs/websocket/client.go
+++ b/contrib/codecs/websocket/client.go
@@ -2,6 +2,7 @@ package websocket
 
 import (
 	"context"
+	jrpc2 "gfx.cafe/open/jrpc/pkg/codec"
 	"sync"
 
 	"gfx.cafe/open/jrpc"
@@ -32,7 +33,7 @@ func (c *Client) Do(ctx context.Context, result any, method string, params any)
 	panic("not implemented") // TODO: Implement
 }
 
-func (c *Client) BatchCall(ctx context.Context, b ...jrpc.BatchElem) error {
+func (c *Client) BatchCall(ctx context.Context, b ...jrpc2.BatchElem) error {
 	panic("not implemented") // TODO: Implement
 }
 
diff --git a/pkg/codec/codecs/websocket/client_example_test.go b/contrib/codecs/websocket/client_example_test.go
similarity index 100%
rename from pkg/codec/codecs/websocket/client_example_test.go
rename to contrib/codecs/websocket/client_example_test.go
diff --git a/pkg/codec/codecs/websocket/codec.go b/contrib/codecs/websocket/codec.go
similarity index 100%
rename from pkg/codec/codecs/websocket/codec.go
rename to contrib/codecs/websocket/codec.go
diff --git a/pkg/codec/codecs/websocket/const.go b/contrib/codecs/websocket/const.go
similarity index 100%
rename from pkg/codec/codecs/websocket/const.go
rename to contrib/codecs/websocket/const.go
diff --git a/pkg/codec/codecs/websocket/dial.go b/contrib/codecs/websocket/dial.go
similarity index 100%
rename from pkg/codec/codecs/websocket/dial.go
rename to contrib/codecs/websocket/dial.go
diff --git a/pkg/codec/codecs/websocket/websocket.go b/contrib/codecs/websocket/websocket.go
similarity index 95%
rename from pkg/codec/codecs/websocket/websocket.go
rename to contrib/codecs/websocket/websocket.go
index 7ad5c2a..ea2d3ea 100644
--- a/pkg/codec/codecs/websocket/websocket.go
+++ b/contrib/codecs/websocket/websocket.go
@@ -4,13 +4,13 @@ import (
 	"context"
 	"encoding/base64"
 	"encoding/json"
+	"gfx.cafe/open/jrpc/contrib/codecs/websocket/wsjson"
 	codec2 "gfx.cafe/open/jrpc/pkg/codec"
-	"gfx.cafe/open/jrpc/pkg/codec/codecs/websocket/wsjson"
+	"gfx.cafe/open/jrpc/pkg/server"
 	"net/http"
 	"net/url"
 	"time"
 
-	"gfx.cafe/open/jrpc"
 	"nhooyr.io/websocket"
 )
 
@@ -18,7 +18,7 @@ import (
 //
 // allowedOrigins should be a comma-separated list of allowed origin URLs.
 // To allow connections with any origin, pass "*".
-func WebsocketHandler(s *jrpc.Server, allowedOrigins []string) http.Handler {
+func WebsocketHandler(s *server.Server, allowedOrigins []string) http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		conn, err := websocket.Accept(w, r, &websocket.AcceptOptions{
 			OriginPatterns:       allowedOrigins,
diff --git a/pkg/codec/codecs/websocket/websocket_test.go b/contrib/codecs/websocket/websocket_test.go
similarity index 83%
rename from pkg/codec/codecs/websocket/websocket_test.go
rename to contrib/codecs/websocket/websocket_test.go
index 2aec7d1..0009913 100644
--- a/pkg/codec/codecs/websocket/websocket_test.go
+++ b/contrib/codecs/websocket/websocket_test.go
@@ -3,21 +3,20 @@ package websocket_test
 import (
 	"context"
 	"errors"
+	websocket2 "gfx.cafe/open/jrpc/contrib/codecs/websocket"
+	"gfx.cafe/open/jrpc/contrib/jmux"
 	"gfx.cafe/open/jrpc/pkg/codec"
-	"gfx.cafe/open/jrpc/pkg/codec/codecs/websocket"
-	"gfx.cafe/open/jrpc/pkg/jmux"
 	jrpctest2 "gfx.cafe/open/jrpc/pkg/jrpctest"
+	"gfx.cafe/open/jrpc/pkg/server"
 	"net/http/httptest"
 	"strings"
 	"testing"
-
-	"gfx.cafe/open/jrpc"
 )
 
 func TestWebsocketClientHeaders(t *testing.T) {
 	t.Parallel()
 
-	endpoint, header, err := websocket.WsClientHeaders("wss://testuser:test-PASS_01@example.com:1234", "https://example.com")
+	endpoint, header, err := websocket2.WsClientHeaders("wss://testuser:test-PASS_01@example.com:1234", "https://example.com")
 	if err != nil {
 		t.Fatalf("wsGetConfig failed: %s", err)
 	}
@@ -38,24 +37,24 @@ func TestWebsocketOriginCheck(t *testing.T) {
 
 	var (
 		srv     = jrpctest2.NewTestServer()
-		httpsrv = httptest.NewServer(websocket.WebsocketHandler(srv, []string{"http://example.com"}))
+		httpsrv = httptest.NewServer(websocket2.WebsocketHandler(srv, []string{"http://example.com"}))
 		wsURL   = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:")
 	)
 	defer srv.Stop()
 	defer httpsrv.Close()
 
-	client, err := websocket.DialWebsocket(context.Background(), wsURL, "http://ekzample.com")
+	client, err := websocket2.DialWebsocket(context.Background(), wsURL, "http://ekzample.com")
 	if err == nil {
 		client.Close()
 		t.Fatal("no error for wrong origin")
 	}
-	wantErr := websocket.NewHandshakeError(errors.New("403"), "403 Forbidden")
+	wantErr := websocket2.NewHandshakeError(errors.New("403"), "403 Forbidden")
 	if !strings.Contains(err.Error(), wantErr.Error()) {
 		t.Fatalf("wrong error for wrong origin: got: '%q', want: '%s'", err, wantErr)
 	}
 
 	// Connections without origin header should work.
-	client, err = websocket.DialWebsocket(context.Background(), wsURL, "")
+	client, err = websocket2.DialWebsocket(context.Background(), wsURL, "")
 	if err != nil {
 		t.Fatalf("error for empty origin: %v", err)
 	}
@@ -68,13 +67,13 @@ func TestWebsocketLargeCall(t *testing.T) {
 
 	var (
 		srv     = jrpctest2.NewTestServer()
-		httpsrv = httptest.NewServer(websocket.WebsocketHandler(srv, []string{"*"}))
+		httpsrv = httptest.NewServer(websocket2.WebsocketHandler(srv, []string{"*"}))
 		wsURL   = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:")
 	)
 	defer srv.Stop()
 	defer httpsrv.Close()
 
-	client, err := websocket.DialWebsocket(context.Background(), wsURL, "")
+	client, err := websocket2.DialWebsocket(context.Background(), wsURL, "")
 	if err != nil {
 		t.Fatalf("can't dial: %v", err)
 	}
@@ -82,7 +81,7 @@ func TestWebsocketLargeCall(t *testing.T) {
 
 	// This call sends slightly less than the limit and should work.
 	var result jrpctest2.EchoResult
-	arg := strings.Repeat("x", websocket.MaxRequestContentLength-200)
+	arg := strings.Repeat("x", websocket2.MaxRequestContentLength-200)
 	if err := client.Do(nil, &result, "test_echo", []any{arg, 1}); err != nil {
 		t.Fatalf("valid call didn't work: %v", err)
 	}
@@ -91,7 +90,7 @@ func TestWebsocketLargeCall(t *testing.T) {
 	}
 
 	// This call sends twice the allowed size and shouldn't work.
-	arg = strings.Repeat("x", websocket.MaxRequestContentLength*2)
+	arg = strings.Repeat("x", websocket2.MaxRequestContentLength*2)
 	err = client.Do(nil, &result, "test_echo", []any{arg})
 	if err == nil {
 		t.Fatal("no error for too large call")
@@ -101,14 +100,14 @@ func TestWebsocketLargeCall(t *testing.T) {
 func TestWebsocketPeerInfo(t *testing.T) {
 	var (
 		s     = jrpctest2.NewTestServer()
-		ts    = httptest.NewServer(websocket.WebsocketHandler(s, []string{"origin.example.com"}))
+		ts    = httptest.NewServer(websocket2.WebsocketHandler(s, []string{"origin.example.com"}))
 		tsurl = "ws:" + strings.TrimPrefix(ts.URL, "http:")
 	)
 	defer s.Stop()
 	defer ts.Close()
 
 	ctx := context.Background()
-	c, err := websocket.DialWebsocket(ctx, tsurl, "http://origin.example.com")
+	c, err := websocket2.DialWebsocket(ctx, tsurl, "http://origin.example.com")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -137,17 +136,17 @@ func TestWebsocketPeerInfo(t *testing.T) {
 func TestClientWebsocketLargeMessage(t *testing.T) {
 	mux := jmux.NewMux()
 	var (
-		srv     = jrpc.NewServer(mux)
-		httpsrv = httptest.NewServer(websocket.WebsocketHandler(srv, nil))
+		srv     = server.NewServer(mux)
+		httpsrv = httptest.NewServer(websocket2.WebsocketHandler(srv, nil))
 		wsURL   = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:")
 	)
 	defer srv.Stop()
 	defer httpsrv.Close()
 
-	respLength := websocket.WsMessageSizeLimit - 50
+	respLength := websocket2.WsMessageSizeLimit - 50
 	mux.RegisterStruct("test", jrpctest2.LargeRespService{Length: respLength})
 
-	c, err := websocket.DialWebsocket(context.Background(), wsURL, "")
+	c, err := websocket2.DialWebsocket(context.Background(), wsURL, "")
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/pkg/codec/codecs/websocket/wsjson/writer_test.go b/contrib/codecs/websocket/wsjson/writer_test.go
similarity index 100%
rename from pkg/codec/codecs/websocket/wsjson/writer_test.go
rename to contrib/codecs/websocket/wsjson/writer_test.go
diff --git a/pkg/codec/codecs/websocket/wsjson/wsjson.go b/contrib/codecs/websocket/wsjson/wsjson.go
similarity index 100%
rename from pkg/codec/codecs/websocket/wsjson/wsjson.go
rename to contrib/codecs/websocket/wsjson/wsjson.go
diff --git a/pkg/handlers/argreflect/json.go b/contrib/handlers/argreflect/json.go
similarity index 96%
rename from pkg/handlers/argreflect/json.go
rename to contrib/handlers/argreflect/json.go
index 1e9dc3d..a46fe42 100644
--- a/pkg/handlers/argreflect/json.go
+++ b/contrib/handlers/argreflect/json.go
@@ -4,7 +4,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"gfx.cafe/open/jrpc/pkg/codec/codecs/websocket/wsjson"
+	"gfx.cafe/open/jrpc/contrib/codecs/websocket/wsjson"
 	"reflect"
 )
 
diff --git a/pkg/handlers/argreflect/reflect_handler.go b/contrib/handlers/argreflect/reflect_handler.go
similarity index 92%
rename from pkg/handlers/argreflect/reflect_handler.go
rename to contrib/handlers/argreflect/reflect_handler.go
index e4457e5..55e4dc5 100644
--- a/pkg/handlers/argreflect/reflect_handler.go
+++ b/contrib/handlers/argreflect/reflect_handler.go
@@ -7,8 +7,6 @@ import (
 	"reflect"
 	"runtime"
 	"unicode"
-
-	"gfx.cafe/open/jrpc"
 )
 
 var (
@@ -16,16 +14,16 @@ var (
 	errorType   = reflect.TypeOf((*error)(nil)).Elem()
 )
 
-func SuitableCallbacks(receiver reflect.Value) map[string]jrpc.Handler {
+func SuitableCallbacks(receiver reflect.Value) map[string]codec.Handler {
 	return suitableCallbacks(receiver)
 }
 
 // suitableCallbacks iterates over the methods of the given type. It determines if a method
 // satisfies the criteria for a RPC callback or a subscription callback and adds it to the
 // collection of callbacks. See server documentation for a summary of these criteria.
-func suitableCallbacks(receiver reflect.Value) map[string]jrpc.Handler {
+func suitableCallbacks(receiver reflect.Value) map[string]codec.Handler {
 	typ := receiver.Type()
-	callbacks := make(map[string]jrpc.Handler)
+	callbacks := make(map[string]codec.Handler)
 	for m := 0; m < typ.NumMethod(); m++ {
 		method := typ.Method(m)
 		if method.PkgPath != "" {
@@ -51,7 +49,7 @@ type callback struct {
 }
 
 // callback handler implements handler for the original receiver style that geth used
-func (e *callback) ServeRPC(w jrpc.ResponseWriter, r *jrpc.Request) {
+func (e *callback) ServeRPC(w codec.ResponseWriter, r *codec.Request) {
 	argTypes := append([]reflect.Type{}, e.argTypes...)
 	args, err := parsePositionalArguments(r.Params, argTypes)
 	if err != nil {
@@ -90,7 +88,7 @@ func (e *callback) ServeRPC(w jrpc.ResponseWriter, r *jrpc.Request) {
 	w.Send(results[0].Interface(), nil)
 }
 
-func NewCallback(receiver, fn reflect.Value) jrpc.Handler {
+func NewCallback(receiver, fn reflect.Value) codec.Handler {
 	return newCallback(receiver, fn)
 }
 
diff --git a/pkg/jmux/mux.go b/contrib/jmux/mux.go
similarity index 89%
rename from pkg/jmux/mux.go
rename to contrib/jmux/mux.go
index 4b20c2c..4f882d2 100644
--- a/pkg/jmux/mux.go
+++ b/contrib/jmux/mux.go
@@ -4,11 +4,10 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"gfx.cafe/open/jrpc/pkg/handlers/argreflect"
+	"gfx.cafe/open/jrpc/contrib/handlers/argreflect"
+	"gfx.cafe/open/jrpc/pkg/codec"
 	"reflect"
 	"sync"
-
-	"gfx.cafe/open/jrpc"
 )
 
 var _ Router = &Mux{}
@@ -24,13 +23,13 @@ var _ Router = &Mux{}
 type Mux struct {
 	// The computed mux handler made of the chained middleware stack and
 	// the tree router
-	handler jrpc.Handler
+	handler codec.Handler
 
 	// The radix trie router
 	tree *node
 
 	// Custom method not allowed handler
-	methodNotAllowedHandler jrpc.HandlerFunc
+	methodNotAllowedHandler codec.HandlerFunc
 
 	// A reference to the parent mux used by subrouters when mounting
 	// to a parent mux
@@ -40,10 +39,10 @@ type Mux struct {
 	pool *sync.Pool
 
 	// Custom route not found handler
-	notFoundHandler jrpc.HandlerFunc
+	notFoundHandler codec.HandlerFunc
 
 	// The middleware stack
-	middlewares []func(jrpc.Handler) jrpc.Handler
+	middlewares []func(codec.Handler) codec.Handler
 
 	// Controls the behaviour of middleware chain generation when a mux
 	// is registered as an inline group inside another mux.
@@ -92,7 +91,7 @@ func (m *Mux) RegisterFunc(name string, rcvr any) error {
 // ServeRPC is the single method of the Handler interface that makes
 // Mux interoperable with the standard library. It uses a sync.Pool to get and
 // reuse routing contexts for each request.
-func (mx *Mux) ServeRPC(w jrpc.ResponseWriter, r *jrpc.Request) {
+func (mx *Mux) ServeRPC(w codec.ResponseWriter, r *codec.Request) {
 	// Ensure the mux has some routes defined on the mux
 	if mx.handler == nil {
 		mx.NotFoundHandler().ServeRPC(w, r)
@@ -129,7 +128,7 @@ func (mx *Mux) ServeRPC(w jrpc.ResponseWriter, r *jrpc.Request) {
 // route to a specific handler, which provides opportunity to respond early,
 // change the course of the request execution, or set request-scoped values for
 // the next Handler.
-func (mx *Mux) Use(middlewares ...func(jrpc.Handler) jrpc.Handler) {
+func (mx *Mux) Use(middlewares ...func(codec.Handler) codec.Handler) {
 	if mx.handler != nil {
 		panic("chi: all middlewares must be defined before routes on a mux")
 	}
@@ -138,19 +137,19 @@ func (mx *Mux) Use(middlewares ...func(jrpc.Handler) jrpc.Handler) {
 
 // Handle adds the route `pattern` that matches any jrpc method to
 // execute the `handler` Handler.
-func (mx *Mux) Handle(pattern string, handler jrpc.Handler) {
+func (mx *Mux) Handle(pattern string, handler codec.Handler) {
 	mx.handle(pattern, handler)
 }
 
 // HandleFunc adds the route `pattern` that matches any jrpc method to
 // execute the `handlerFn` HandlerFunc.
-func (mx *Mux) HandleFunc(pattern string, handlerFn jrpc.HandlerFunc) {
+func (mx *Mux) HandleFunc(pattern string, handlerFn codec.HandlerFunc) {
 	mx.handle(pattern, handlerFn)
 }
 
 // NotFound sets a custom HandlerFunc for routing paths that could
 // not be found. The default 404 handler is `NotFound`.
-func (mx *Mux) NotFound(handlerFn jrpc.HandlerFunc) {
+func (mx *Mux) NotFound(handlerFn codec.HandlerFunc) {
 	// Build NotFound handler chain
 	m := mx
 	hFn := handlerFn
@@ -170,7 +169,7 @@ func (mx *Mux) NotFound(handlerFn jrpc.HandlerFunc) {
 
 // MethodNotAllowed sets a custom HandlerFunc for routing paths where the
 // method is unresolved. The default handler returns a 405 with an empty body.
-func (mx *Mux) MethodNotAllowed(handlerFn jrpc.HandlerFunc) {
+func (mx *Mux) MethodNotAllowed(handlerFn codec.HandlerFunc) {
 	// Build MethodNotAllowed handler chain
 	m := mx
 	hFn := handlerFn
@@ -189,7 +188,7 @@ func (mx *Mux) MethodNotAllowed(handlerFn jrpc.HandlerFunc) {
 }
 
 // With adds inline middlewares for an endpoint handler.
-func (mx *Mux) With(middlewares ...func(jrpc.Handler) jrpc.Handler) Router {
+func (mx *Mux) With(middlewares ...func(codec.Handler) codec.Handler) Router {
 	// Similarly as in handle(), we must build the mux handler once additional
 	// middleware registration isn't allowed for this stack, like now.
 	if !mx.inline && mx.handler == nil {
@@ -242,7 +241,7 @@ func (mx *Mux) Route(pattern string, fn func(r Router)) Router {
 // Note that Mount() simply sets a wildcard along the `pattern` that will continue
 // routing at the `handler`, which in most cases is another chi.Router. As a result,
 // if you define two Mount() routes on the exact same pattern the mount will panic.
-func (mx *Mux) Mount(pattern string, handler jrpc.Handler) {
+func (mx *Mux) Mount(pattern string, handler codec.Handler) {
 	if handler == nil {
 		panic(fmt.Sprintf("chi: attempting to Mount() a nil handler on '%s'", pattern))
 	}
@@ -262,7 +261,7 @@ func (mx *Mux) Mount(pattern string, handler jrpc.Handler) {
 		subr.MethodNotAllowed(mx.methodNotAllowedHandler)
 	}
 
-	mountHandler := jrpc.HandlerFunc(func(w jrpc.ResponseWriter, r *jrpc.Request) {
+	mountHandler := codec.HandlerFunc(func(w codec.ResponseWriter, r *codec.Request) {
 		rctx := RouteContext(r.Context())
 
 		// shift the url path past the previous subrouter
@@ -318,7 +317,7 @@ func (mx *Mux) Match(rctx *Context, path string) bool {
 
 // NotFoundHandler returns the default Mux 404 responder whenever a route
 // cannot be found.
-func (mx *Mux) NotFoundHandler() jrpc.HandlerFunc {
+func (mx *Mux) NotFoundHandler() codec.HandlerFunc {
 	if mx.notFoundHandler != nil {
 		return mx.notFoundHandler
 	}
@@ -327,7 +326,7 @@ func (mx *Mux) NotFoundHandler() jrpc.HandlerFunc {
 
 // MethodNotAllowedHandler returns the default Mux 405 responder whenever
 // a method cannot be resolved for a route.
-func (mx *Mux) MethodNotAllowedHandler() jrpc.HandlerFunc {
+func (mx *Mux) MethodNotAllowedHandler() codec.HandlerFunc {
 	if mx.methodNotAllowedHandler != nil {
 		return mx.methodNotAllowedHandler
 	}
@@ -336,7 +335,7 @@ func (mx *Mux) MethodNotAllowedHandler() jrpc.HandlerFunc {
 
 // handle registers a Handler in the routing tree for a particular jrpc method
 // and routing pattern.
-func (mx *Mux) handle(pattern string, handler jrpc.Handler) *node {
+func (mx *Mux) handle(pattern string, handler codec.Handler) *node {
 	if len(pattern) == 0 {
 		panic(fmt.Sprintf("rpc: routing pattern must not be empty in '%s'", pattern))
 	}
@@ -347,9 +346,9 @@ func (mx *Mux) handle(pattern string, handler jrpc.Handler) *node {
 	}
 
 	// Build endpoint handler with inline middlewares for the route
-	var h jrpc.Handler
+	var h codec.Handler
 	if mx.inline {
-		mx.handler = jrpc.HandlerFunc(mx.routeRPC)
+		mx.handler = codec.HandlerFunc(mx.routeRPC)
 		h = Chain(mx.middlewares...).Handler(handler)
 	} else {
 		h = handler
@@ -361,7 +360,7 @@ func (mx *Mux) handle(pattern string, handler jrpc.Handler) *node {
 
 // routeJRPC routes a Request through the Mux routing tree to serve
 // the matching handler for a particular jrpc method.
-func (mx *Mux) routeRPC(w jrpc.ResponseWriter, r *jrpc.Request) {
+func (mx *Mux) routeRPC(w codec.ResponseWriter, r *codec.Request) {
 	// Grab the route context object
 	rctx := r.Context().Value(RouteCtxKey).(*Context)
 
@@ -413,15 +412,15 @@ func (mx *Mux) updateSubRoutes(fn func(subMux *Mux)) {
 // point, no other middlewares can be registered on this Mux's stack. But you can still
 // compose additional middlewares via Group()'s or using a chained middleware handler.
 func (mx *Mux) updateRouteHandler() {
-	mx.handler = chain(mx.middlewares, jrpc.HandlerFunc(mx.routeRPC))
+	mx.handler = chain(mx.middlewares, codec.HandlerFunc(mx.routeRPC))
 }
 
 // methodNotAllowedHandler is a helper function to respond with a 405,
 // method not allowed.
-func methodNotAllowedHandler(w jrpc.ResponseWriter, r *jrpc.Request) {
+func methodNotAllowedHandler(w codec.ResponseWriter, r *codec.Request) {
 	w.Send(nil, errors.New("forbidden"))
 }
 
-func NotFound(w jrpc.ResponseWriter, r *jrpc.Request) {
+func NotFound(w codec.ResponseWriter, r *codec.Request) {
 	w.Send(nil, errors.New("not found"))
 }
diff --git a/pkg/jmux/router.go b/contrib/jmux/router.go
similarity index 83%
rename from pkg/jmux/router.go
rename to contrib/jmux/router.go
index 3ad7a78..b1c0845 100644
--- a/pkg/jmux/router.go
+++ b/contrib/jmux/router.go
@@ -1,6 +1,8 @@
 package jmux
 
-import "gfx.cafe/open/jrpc"
+import (
+	"gfx.cafe/open/jrpc/pkg/codec"
+)
 
 // NewRouter returns a new Mux object that implements the Router interface.
 func NewRouter() *Mux {
@@ -19,15 +21,15 @@ type StructReflector interface {
 // Router consisting of the core routing methods used by chi's Mux,
 // adapted to fit json-rpc.
 type Router interface {
-	jrpc.Handler
+	codec.Handler
 	Routes
 	StructReflector
 
 	// Use appends one or more middlewares onto the Router stack.
-	Use(middlewares ...func(jrpc.Handler) jrpc.Handler)
+	Use(middlewares ...func(codec.Handler) codec.Handler)
 
 	// With adds inline middlewares for an endpoint handler.
-	With(middlewares ...func(jrpc.Handler) jrpc.Handler) Router
+	With(middlewares ...func(codec.Handler) codec.Handler) Router
 
 	// Group adds a new inline-Router along the current routing
 	// path, with a fresh middleware stack for the inline-Router.
@@ -37,16 +39,16 @@ type Router interface {
 	Route(pattern string, fn func(r Router)) Router
 
 	// Mount attaches another Handler along ./pattern/*
-	Mount(pattern string, h jrpc.Handler)
+	Mount(pattern string, h codec.Handler)
 
 	// Handle and HandleFunc adds routes for `pattern` that matches
 	// all HTTP methods.
-	Handle(pattern string, h jrpc.Handler)
-	HandleFunc(pattern string, h jrpc.HandlerFunc)
+	Handle(pattern string, h codec.Handler)
+	HandleFunc(pattern string, h codec.HandlerFunc)
 
 	// NotFound defines a handler to respond whenever a route could
 	// not be found.
-	NotFound(h jrpc.HandlerFunc)
+	NotFound(h codec.HandlerFunc)
 }
 
 // Routes interface adds two methods for router traversal, which is also
@@ -66,4 +68,4 @@ type Routes interface {
 
 // Middlewares type is a slice of standard middleware handlers with methods
 // to compose middleware chains and Handler's.
-type Middlewares []func(jrpc.Handler) jrpc.Handler
+type Middlewares []func(codec.Handler) codec.Handler
diff --git a/pkg/jmux/router_chain.go b/contrib/jmux/router_chain.go
similarity index 68%
rename from pkg/jmux/router_chain.go
rename to contrib/jmux/router_chain.go
index 0eb1d3b..f44f20c 100644
--- a/pkg/jmux/router_chain.go
+++ b/contrib/jmux/router_chain.go
@@ -1,39 +1,41 @@
 package jmux
 
-import "gfx.cafe/open/jrpc"
+import (
+	"gfx.cafe/open/jrpc/pkg/codec"
+)
 
 // Chain returns a Middlewares type from a slice of middleware handlers.
-func Chain(middlewares ...func(jrpc.Handler) jrpc.Handler) Middlewares {
+func Chain(middlewares ...func(codec.Handler) codec.Handler) Middlewares {
 	return Middlewares(middlewares)
 }
 
 // Handler builds and returns a Handler from the chain of middlewares,
 // with `h Handler` as the final handler.
-func (mws Middlewares) Handler(h jrpc.Handler) jrpc.Handler {
+func (mws Middlewares) Handler(h codec.Handler) codec.Handler {
 	return &ChainHandler{h, chain(mws, h), mws}
 }
 
 // HandlerFunc builds and returns a Handler from the chain of middlewares,
 // with `h Handler` as the final handler.
-func (mws Middlewares) HandlerFunc(h jrpc.HandlerFunc) jrpc.Handler {
+func (mws Middlewares) HandlerFunc(h codec.HandlerFunc) codec.Handler {
 	return &ChainHandler{h, chain(mws, h), mws}
 }
 
 // ChainHandler is a Handler with support for handler composition and
 // execution.
 type ChainHandler struct {
-	Endpoint    jrpc.Handler
-	chain       jrpc.Handler
+	Endpoint    codec.Handler
+	chain       codec.Handler
 	Middlewares Middlewares
 }
 
-func (c *ChainHandler) ServeRPC(w jrpc.ResponseWriter, r *jrpc.Request) {
+func (c *ChainHandler) ServeRPC(w codec.ResponseWriter, r *codec.Request) {
 	c.chain.ServeRPC(w, r)
 }
 
 // chain builds a Handler composed of an inline middleware stack and endpoint
 // handler in the order they are passed.
-func chain(middlewares []func(jrpc.Handler) jrpc.Handler, endpoint jrpc.Handler) jrpc.Handler {
+func chain(middlewares []func(codec.Handler) codec.Handler, endpoint codec.Handler) codec.Handler {
 	// Return ahead of time if there aren't any middlewares for the chain
 	if len(middlewares) == 0 {
 		return endpoint
diff --git a/pkg/jmux/router_context.go b/contrib/jmux/router_context.go
similarity index 98%
rename from pkg/jmux/router_context.go
rename to contrib/jmux/router_context.go
index 828332b..7b851fd 100644
--- a/pkg/jmux/router_context.go
+++ b/contrib/jmux/router_context.go
@@ -2,13 +2,12 @@ package jmux
 
 import (
 	"context"
+	"gfx.cafe/open/jrpc/pkg/codec"
 	"strings"
-
-	"gfx.cafe/open/jrpc"
 )
 
 // MethodParam returns the url parameter from a Request object.
-func MethodParam(r *jrpc.Request, key string) string {
+func MethodParam(r *codec.Request, key string) string {
 	if rctx := RouteContext(r.Context()); rctx != nil {
 		return rctx.MethodParam(key)
 	}
diff --git a/pkg/jmux/router_tree.go b/contrib/jmux/router_tree.go
similarity index 97%
rename from pkg/jmux/router_tree.go
rename to contrib/jmux/router_tree.go
index f5031e4..2b7a2f1 100644
--- a/pkg/jmux/router_tree.go
+++ b/contrib/jmux/router_tree.go
@@ -5,11 +5,10 @@ package jmux
 
 import (
 	"fmt"
+	"gfx.cafe/open/jrpc/pkg/codec"
 	"regexp"
 	"sort"
 	"strings"
-
-	"gfx.cafe/open/jrpc"
 )
 
 type nodeTyp uint8
@@ -50,7 +49,7 @@ type node struct {
 
 type endpoint struct {
 	// endpoint handler
-	handler jrpc.Handler
+	handler codec.Handler
 
 	// pattern is the routing pattern for handler nodes
 	pattern string
@@ -59,7 +58,7 @@ type endpoint struct {
 	paramKeys []string
 }
 
-func (n *node) InsertRoute(pattern string, handler jrpc.Handler) *node {
+func (n *node) InsertRoute(pattern string, handler codec.Handler) *node {
 	var parent *node
 	search := pattern
 	for {
@@ -263,7 +262,7 @@ func (n *node) getEdge(ntyp nodeTyp, label, tail byte, prefix string) *node {
 	return nil
 }
 
-func (n *node) setEndpoint(handler jrpc.Handler, pattern string) {
+func (n *node) setEndpoint(handler codec.Handler, pattern string) {
 	paramKeys := patParamKeys(pattern)
 	n.endpoint = &endpoint{
 		handler:   handler,
@@ -272,7 +271,7 @@ func (n *node) setEndpoint(handler jrpc.Handler, pattern string) {
 	}
 }
 
-func (n *node) FindRoute(rctx *Context, path string) (*node, *endpoint, jrpc.Handler) {
+func (n *node) FindRoute(rctx *Context, path string) (*node, *endpoint, codec.Handler) {
 	// Reset the context routing pattern and params
 	rctx.routePattern = ""
 	rctx.routeParams.Keys = rctx.routeParams.Keys[:0]
@@ -683,21 +682,21 @@ func (ns nodes) findEdge(label byte) *node {
 // Route describes the details of a routing handler.
 type Route struct {
 	SubRoutes Routes
-	Handler   jrpc.Handler
+	Handler   codec.Handler
 	Pattern   string
 }
 
 // WalkFunc is the type of the function called for each method and route visited by Walk.
-type WalkFunc func(route string, handler jrpc.Handler, middlewares ...func(jrpc.Handler) jrpc.Handler) error
+type WalkFunc func(route string, handler codec.Handler, middlewares ...func(codec.Handler) codec.Handler) error
 
 // Walk walks any router tree that implements Routes interface.
 func Walk(r Routes, walkFn WalkFunc) error {
 	return walk(r, walkFn, "")
 }
 
-func walk(r Routes, walkFn WalkFunc, parentRoute string, parentMw ...func(jrpc.Handler) jrpc.Handler) error {
+func walk(r Routes, walkFn WalkFunc, parentRoute string, parentMw ...func(codec.Handler) codec.Handler) error {
 	for _, route := range r.Routes() {
-		mws := make([]func(jrpc.Handler) jrpc.Handler, len(parentMw))
+		mws := make([]func(codec.Handler) codec.Handler, len(parentMw))
 		copy(mws, parentMw)
 		mws = append(mws, r.Middlewares()...)
 
diff --git a/contrib/middleware/log.go b/contrib/middleware/log.go
index efd1e41..bd3fb8b 100644
--- a/contrib/middleware/log.go
+++ b/contrib/middleware/log.go
@@ -2,9 +2,9 @@ package middleware
 
 import (
 	"context"
+	"gfx.cafe/open/jrpc/pkg/codec"
 	"time"
 
-	"gfx.cafe/open/jrpc"
 	"tuxpa.in/a/zlog"
 	"tuxpa.in/a/zlog/log"
 )
@@ -15,8 +15,8 @@ type ctxKeyLogger int
 // RequestIDKey is the key that holds the unique request ID in a request context.
 const LoggerKey ctxKeyLogger = 76
 
-func Logger(next jrpc.Handler) jrpc.Handler {
-	fn := func(w jrpc.ResponseWriter, r *jrpc.Request) {
+func Logger(next codec.Handler) codec.Handler {
+	fn := func(w codec.ResponseWriter, r *codec.Request) {
 		start := time.Now()
 		l := log.Trace().
 			Str("remote", r.Remote()).
@@ -29,7 +29,7 @@ func Logger(next jrpc.Handler) jrpc.Handler {
 		l = l.Stringer("dur", time.Since(start))
 		l.Msg("RPC Request")
 	}
-	return jrpc.HandlerFunc(fn)
+	return codec.HandlerFunc(fn)
 }
 
 func GetLogger(ctx context.Context) *zlog.Event {
diff --git a/contrib/middleware/middleware.go b/contrib/middleware/middleware.go
index 77cf2de..491a05a 100644
--- a/contrib/middleware/middleware.go
+++ b/contrib/middleware/middleware.go
@@ -1,11 +1,13 @@
 package middleware
 
-import "gfx.cafe/open/jrpc"
+import (
+	"gfx.cafe/open/jrpc/pkg/codec"
+)
 
 // New will create a new middleware handler from a jrpc.Handler.
-func New(h jrpc.Handler) func(next jrpc.Handler) jrpc.Handler {
-	return func(next jrpc.Handler) jrpc.Handler {
-		return jrpc.HandlerFunc(func(w jrpc.ResponseWriter, r *jrpc.Request) {
+func New(h codec.Handler) func(next codec.Handler) codec.Handler {
+	return func(next codec.Handler) codec.Handler {
+		return codec.HandlerFunc(func(w codec.ResponseWriter, r *codec.Request) {
 			h.ServeRPC(w, r)
 		})
 	}
diff --git a/contrib/middleware/recoverer.go b/contrib/middleware/recoverer.go
index 866daf2..4102247 100644
--- a/contrib/middleware/recoverer.go
+++ b/contrib/middleware/recoverer.go
@@ -7,12 +7,11 @@ import (
 	"bytes"
 	"errors"
 	"fmt"
+	"gfx.cafe/open/jrpc/pkg/codec"
 	"io"
 	"os"
 	"runtime/debug"
 	"strings"
-
-	"gfx.cafe/open/jrpc"
 )
 
 // Recoverer is a middleware that recovers from panics, logs the panic (and a
@@ -20,8 +19,8 @@ import (
 // possible. Recoverer prints a request ID if one is provided.
 //
 // Alternatively, look at jrpcs://github.com/go-chi/jrpclog middleware pkgs.
-func Recoverer(next jrpc.Handler) jrpc.Handler {
-	fn := func(w jrpc.ResponseWriter, r *jrpc.Request) {
+func Recoverer(next codec.Handler) codec.Handler {
+	fn := func(w codec.ResponseWriter, r *codec.Request) {
 		defer func() {
 			if rvr := recover(); rvr != nil {
 				PrintPrettyStack(rvr)
@@ -32,7 +31,7 @@ func Recoverer(next jrpc.Handler) jrpc.Handler {
 		next.ServeRPC(w, r)
 	}
 
-	return jrpc.HandlerFunc(fn)
+	return codec.HandlerFunc(fn)
 }
 
 // for ability to test the PrintPrettyStack function
diff --git a/contrib/middleware/request_id.go b/contrib/middleware/request_id.go
index 2ed3018..cfed73f 100644
--- a/contrib/middleware/request_id.go
+++ b/contrib/middleware/request_id.go
@@ -8,11 +8,10 @@ import (
 	"crypto/rand"
 	"encoding/base64"
 	"fmt"
+	"gfx.cafe/open/jrpc/pkg/codec"
 	"os"
 	"strings"
 	"sync/atomic"
-
-	"gfx.cafe/open/jrpc"
 )
 
 // Key to use when setting the request ID.
@@ -67,15 +66,15 @@ func init() {
 // where "random" is a base62 random string that uniquely identifies this go
 // process, and where the last number is an atomically incremented request
 // counter.
-func RequestID(next jrpc.Handler) jrpc.Handler {
-	fn := func(w jrpc.ResponseWriter, r *jrpc.Request) {
+func RequestID(next codec.Handler) codec.Handler {
+	fn := func(w codec.ResponseWriter, r *codec.Request) {
 		ctx := r.Context()
 		myid := atomic.AddUint64(&reqid, 1)
 		requestID := fmt.Sprintf("%s-%06d", prefix, myid)
 		ctx = context.WithValue(ctx, RequestIDKey, requestID)
 		next.ServeRPC(w, r.WithContext(ctx))
 	}
-	return jrpc.HandlerFunc(fn)
+	return codec.HandlerFunc(fn)
 }
 
 // GetReqID returns a request ID from the given context if one is present.
diff --git a/contrib/openrpc/out/example/main.go b/contrib/openrpc/out/example/main.go
index eaf4f72..0647e9d 100644
--- a/contrib/openrpc/out/example/main.go
+++ b/contrib/openrpc/out/example/main.go
@@ -3,6 +3,7 @@ package main
 import (
 	"context"
 	"gfx.cafe/open/jrpc/contrib/openrpc/out"
+	jrpc2 "gfx.cafe/open/jrpc/pkg/server"
 	"log"
 	"net/http"
 
@@ -16,7 +17,7 @@ func main() {
 	rpc_handler := &out.GoOpenRPCHandler{Srv: handler}
 	rmux := jrpc.NewMux()
 	rpc_handler.RouteRPC(rmux)
-	srv := jrpc.NewServer(rmux)
+	srv := jrpc2.NewServer(rmux)
 	log.Println(srv.Router().Routes())
 	log.Println("listening on port 9545")
 	log.Println(http.ListenAndServe(":9545", srv))
diff --git a/pkg/subscription/conn.go b/contrib/subscription/conn.go
similarity index 77%
rename from pkg/subscription/conn.go
rename to contrib/subscription/conn.go
index 3346cdc..3655f25 100644
--- a/pkg/subscription/conn.go
+++ b/contrib/subscription/conn.go
@@ -2,12 +2,11 @@ package subscription
 
 import (
 	"context"
-
-	"gfx.cafe/open/jrpc"
+	"gfx.cafe/open/jrpc/pkg/codec"
 )
 
 type SubscriptionConn interface {
-	jrpc.StreamingConn
+	codec.StreamingConn
 
 	Subscribe(ctx context.Context, namespace string, channel any, args ...any) (*ClientSubscription, error)
 }
diff --git a/pkg/subscription/engine.go b/contrib/subscription/engine.go
similarity index 100%
rename from pkg/subscription/engine.go
rename to contrib/subscription/engine.go
diff --git a/pkg/subscription/subscription.go b/contrib/subscription/subscription.go
similarity index 100%
rename from pkg/subscription/subscription.go
rename to contrib/subscription/subscription.go
diff --git a/doc.go b/doc.go
index 1d2e59d..2e81269 100644
--- a/doc.go
+++ b/doc.go
@@ -1,4 +1,8 @@
+// Package jrpc implements a jsonrpc2 server
 /*
 
+
+
+
  */
 package jrpc
diff --git a/example/echo/main.go b/example/echo/main.go
index c55c76c..cf6d9aa 100644
--- a/example/echo/main.go
+++ b/example/echo/main.go
@@ -1,6 +1,8 @@
 package main
 
 import (
+	"gfx.cafe/open/jrpc/pkg/codec"
+	"gfx.cafe/open/jrpc/pkg/server"
 	"log"
 	"net/http"
 
@@ -10,9 +12,9 @@ import (
 func main() {
 
 	r := jrpc.NewRouter()
-	srv := jrpc.NewServer(r)
+	srv := server.NewServer(r)
 
-	r.HandleFunc("echo", func(w jrpc.ResponseWriter, r *jrpc.Request) {
+	r.HandleFunc("echo", func(w codec.ResponseWriter, r *codec.Request) {
 		w.Send(r.Params, nil)
 	})
 
diff --git a/example/proxy/main.go b/example/proxy/main.go
index e241b77..6a2186d 100644
--- a/example/proxy/main.go
+++ b/example/proxy/main.go
@@ -3,6 +3,8 @@ package main
 import (
 	"encoding/json"
 	"gfx.cafe/open/jrpc/contrib/middleware"
+	"gfx.cafe/open/jrpc/pkg/codec"
+	"gfx.cafe/open/jrpc/pkg/server"
 	"log"
 	"net/http"
 
@@ -18,7 +20,7 @@ func main() {
 		panic(err)
 	}
 
-	r.HandleFunc("eth_*", func(w jrpc.ResponseWriter, r *jrpc.Request) {
+	r.HandleFunc("eth_*", func(w codec.ResponseWriter, r *codec.Request) {
 		var res json.RawMessage
 		err := c.Call(&res, r.Method(), r.ParamSlice()...)
 		w.Send(res, err)
@@ -26,7 +28,7 @@ func main() {
 
 	log.Println("running on 8855")
 
-	srv := jrpc.NewServer(r)
+	srv := server.NewServer(r)
 	log.Println(http.ListenAndServe(":8855", srv))
 }
 
diff --git a/example/subscription/main.go b/example/subscription/main.go
index c003a09..4e1db2b 100644
--- a/example/subscription/main.go
+++ b/example/subscription/main.go
@@ -3,6 +3,8 @@ package main
 import (
 	"context"
 	"gfx.cafe/open/jrpc/contrib/middleware"
+	"gfx.cafe/open/jrpc/pkg/codec"
+	"gfx.cafe/open/jrpc/pkg/server"
 	"log"
 	"net/http"
 	"time"
@@ -14,13 +16,13 @@ func main() {
 
 	r := jrpc.NewRouter()
 	r.Use(middleware.Logger)
-	srv := jrpc.NewServer(r)
+	srv := server.NewServer(r)
 
-	r.HandleFunc("echo", func(w jrpc.ResponseWriter, r *jrpc.Request) {
+	r.HandleFunc("echo", func(w codec.ResponseWriter, r *codec.Request) {
 		w.Send(r.Params(), nil)
 	})
 
-	r.HandleFunc("testservice_subscribe", func(w jrpc.ResponseWriter, r *jrpc.Request) {
+	r.HandleFunc("testservice_subscribe", func(w codec.ResponseWriter, r *codec.Request) {
 		sub, err := jrpc.UpgradeToSubscription(w, r)
 		w.Send(sub, err)
 		if err != nil {
diff --git a/exports.go b/exports.go
new file mode 100644
index 0000000..8694880
--- /dev/null
+++ b/exports.go
@@ -0,0 +1,48 @@
+package jrpc
+
+import (
+	"context"
+
+	"gfx.cafe/open/jrpc/pkg/codec"
+	"gfx.cafe/open/jrpc/pkg/server"
+)
+
+// to make the repo cleaner, we export everything here. this way the packages dont need to ever import this
+
+type (
+	// Conn
+	Conn = codec.Conn
+	// Handler
+	Handler = codec.Handler
+	// HandlerFunc
+	HandlerFunc = codec.HandlerFunc
+	// ResponseWriter
+	ResponseWriter = codec.ResponseWriter
+	// StreamingConn
+	StreamingConn = codec.Conn
+)
+type (
+	// BatchElem
+	BatchElem = codec.BatchElem
+)
+
+var (
+	ContextWithConn     = codec.ContextWithConn
+	ContextWithPeerInfo = server.ContextWithPeerInfo
+
+	ConnFromContext     = codec.ConnFromContext
+	PeerInfoFromContext = server.PeerInfoFromContext
+)
+
+// Do
+func Do[T any](ctx context.Context, c Conn, method string, args any) (*T, error) {
+	return codec.Do[T](ctx, c, method, args)
+}
+
+// Call
+func Call[T any](ctx context.Context, c Conn, method string, args ...any) (*T, error) {
+	return codec.Call[T](ctx, c, method, args...)
+}
+
+// CallInto
+var CallInto = codec.CallInto
diff --git a/pkg/clientutil/helper.go b/pkg/clientutil/helper.go
index 0e3177e..b0f70cd 100644
--- a/pkg/clientutil/helper.go
+++ b/pkg/clientutil/helper.go
@@ -4,8 +4,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"gfx.cafe/open/jrpc/pkg/codec"
-
-	"gfx.cafe/open/jrpc"
 	"gfx.cafe/util/go/generic"
 )
 
@@ -23,7 +21,7 @@ func PutMessage(x *codec.Message) {
 	msgPool.Put(x)
 }
 
-func FillBatch(ids []int, msgs []*codec.Message, b []*jrpc.BatchElem) {
+func FillBatch(ids []int, msgs []*codec.Message, b []*codec.BatchElem) {
 	answers := map[int]*codec.Message{}
 	for _, v := range msgs {
 		answers[v.ID.Number()] = v
diff --git a/pkg/codec/codecs/codecs.go b/pkg/codec/codecs/codecs.go
deleted file mode 100644
index 983a7bb..0000000
--- a/pkg/codec/codecs/codecs.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package codecs
-
-import "gfx.cafe/open/jrpc/pkg/codec/codecs/inproc"
-
-var NewInProc = inproc.NewCodec
diff --git a/helper.go b/pkg/codec/helper.go
similarity index 90%
rename from helper.go
rename to pkg/codec/helper.go
index be79e44..491ef95 100644
--- a/helper.go
+++ b/pkg/codec/helper.go
@@ -1,9 +1,8 @@
-package jrpc
+package codec
 
-import (
-	"context"
-)
+import "context"
 
+// Do
 func Do[T any](ctx context.Context, c Conn, method string, args any) (*T, error) {
 	var t T
 	err := c.Do(ctx, &t, method, args)
@@ -13,6 +12,7 @@ func Do[T any](ctx context.Context, c Conn, method string, args any) (*T, error)
 	return &t, nil
 }
 
+// Call
 func Call[T any](ctx context.Context, c Conn, method string, args ...any) (*T, error) {
 	var t T
 	err := c.Do(ctx, &t, method, args)
@@ -22,6 +22,7 @@ func Call[T any](ctx context.Context, c Conn, method string, args ...any) (*T, e
 	return &t, nil
 }
 
+// CallInto
 func CallInto(ctx context.Context, c Conn, result any, method string, args ...any) error {
 	err := c.Do(ctx, result, method, args)
 	if err != nil {
diff --git a/jrpc.go b/pkg/codec/jrpc.go
similarity index 99%
rename from jrpc.go
rename to pkg/codec/jrpc.go
index 766ab26..c398640 100644
--- a/jrpc.go
+++ b/pkg/codec/jrpc.go
@@ -1,4 +1,4 @@
-package jrpc
+package codec
 
 import (
 	"context"
diff --git a/pkg/codec/json.go b/pkg/codec/json.go
index 1441247..673da9f 100644
--- a/pkg/codec/json.go
+++ b/pkg/codec/json.go
@@ -3,7 +3,7 @@ package codec
 import (
 	"bytes"
 	"encoding/json"
-	"gfx.cafe/open/jrpc/pkg/codec/codecs/websocket/wsjson"
+	"gfx.cafe/open/jrpc/contrib/codecs/websocket/wsjson"
 	"strconv"
 )
 
diff --git a/request.go b/pkg/codec/reqresp.go
similarity index 72%
rename from request.go
rename to pkg/codec/reqresp.go
index 315f6ff..5692866 100644
--- a/request.go
+++ b/pkg/codec/reqresp.go
@@ -1,19 +1,40 @@
-package jrpc
+package codec
 
 import (
 	"context"
 
-	codec2 "gfx.cafe/open/jrpc/pkg/codec"
-
 	json "github.com/goccy/go-json"
 )
 
+type Response struct {
+	Version Version         `json:"jsonrpc,omitempty"`
+	ID      *ID             `json:"id,omitempty"`
+	Result  json.RawMessage `json:"result,omitempty"`
+	Error   *JsonError      `json:"error,omitempty"`
+}
+
+func (r *Response) Msg() *Message {
+	out := &Message{}
+	if r.ID != nil {
+		out.ID = r.ID
+	}
+	if r.Error != nil {
+		out.Error = r.Error
+	} else {
+		out.Result = r.Result
+	}
+	return out
+}
+
 type Request struct {
 	RequestMarshaling
-
 	ctx context.Context
 }
 
+func NewRequestFromRaw(ctx context.Context, req *RequestMarshaling) *Request {
+	return &Request{ctx: ctx, RequestMarshaling: *req}
+}
+
 func (r *Request) UnmarshalJSON(xs []byte) error {
 	return json.Unmarshal(xs, &r.RequestMarshaling)
 }
@@ -23,11 +44,11 @@ func (r *Request) MarshalJSON() ([]byte, error) {
 }
 
 type RequestMarshaling struct {
-	Version codec2.Version  `json:"jsonrpc"`
-	ID      *codec2.ID      `json:"id,omitempty"`
+	Version Version         `json:"jsonrpc"`
+	ID      *ID             `json:"id,omitempty"`
 	Method  string          `json:"method"`
 	Params  json.RawMessage `json:"params"`
-	Peer    codec2.PeerInfo `json:"-"`
+	Peer    PeerInfo        `json:"-"`
 }
 
 func NewRequestInt(ctx context.Context, id int, method string, params any) *Request {
@@ -36,7 +57,7 @@ func NewRequestInt(ctx context.Context, id int, method string, params any) *Requ
 	}
 	r := &Request{ctx: ctx}
 	pms, _ := json.Marshal(params)
-	r.ID = codec2.NewNumberIDPtr(int64(id))
+	r.ID = NewNumberIDPtr(int64(id))
 	r.Method = method
 	r.Params = pms
 	return r
@@ -48,7 +69,7 @@ func NewRequest(ctx context.Context, id string, method string, params any) *Requ
 	}
 	r := &Request{ctx: ctx}
 	pms, _ := json.Marshal(params)
-	r.ID = codec2.NewStringIDPtr(id)
+	r.ID = NewStringIDPtr(id)
 	r.Method = method
 	r.Params = pms
 	return r
@@ -66,7 +87,7 @@ func NewNotification(ctx context.Context, method string, params any) *Request {
 	return r
 }
 
-func (r *Request) makeError(err error) *codec2.Message {
+func (r *Request) makeError(err error) *Message {
 	m := r.Msg()
 	return m.ErrorResponse(err)
 }
@@ -103,8 +124,8 @@ func (r *Request) Context() context.Context {
 	return r.ctx
 }
 
-func (r *Request) Msg() codec2.Message {
-	return codec2.Message{
+func (r *Request) Msg() Message {
+	return Message{
 		ID:     r.ID,
 		Method: r.Method,
 		Params: r.Params,
diff --git a/pkg/jrpctest/server.go b/pkg/jrpctest/server.go
index 6eb377c..613daff 100644
--- a/pkg/jrpctest/server.go
+++ b/pkg/jrpctest/server.go
@@ -1,18 +1,17 @@
 package jrpctest
 
 import (
+	jmux2 "gfx.cafe/open/jrpc/contrib/jmux"
+	"gfx.cafe/open/jrpc/pkg/server"
 	"strings"
-
-	"gfx.cafe/open/jrpc"
-	"gfx.cafe/open/jrpc/pkg/jmux"
 )
 
-func NewServer() *jrpc.Server {
-	server := jrpc.NewServer(NewRouter())
+func NewServer() *server.Server {
+	server := server.NewServer(NewRouter())
 	return server
 }
-func NewRouter() *jmux.Mux {
-	mux := jmux.NewRouter()
+func NewRouter() *jmux2.Mux {
+	mux := jmux2.NewRouter()
 	//mux.HandleFunc("testservice_subscribe", func(w jrpc.ResponseWriter, r *jrpc.Request) {
 	//	sub, err := jrpc.UpgradeToSubscription(w, r)
 	//	w.Send(sub, err)
@@ -40,8 +39,8 @@ func NewRouter() *jmux.Mux {
 	}
 	return mux
 }
-func NewRouterWithMaxSize(size int) *jmux.Mux {
-	mux := jmux.NewRouter()
+func NewRouterWithMaxSize(size int) *jmux2.Mux {
+	mux := jmux2.NewRouter()
 	//mux.HandleFunc("testservice_subscribe", func(w jrpc.ResponseWriter, r *jrpc.Request) {
 	//	sub, err := jrpc.UpgradeToSubscription(w, r)
 	//	w.Send(sub, err)
diff --git a/pkg/jrpctest/services.go b/pkg/jrpctest/services.go
index 653a532..eb510c1 100644
--- a/pkg/jrpctest/services.go
+++ b/pkg/jrpctest/services.go
@@ -3,12 +3,10 @@ package jrpctest
 import (
 	"context"
 	"errors"
+	"gfx.cafe/open/jrpc/pkg/codec"
+	"gfx.cafe/open/jrpc/pkg/server"
 	"strings"
 	"time"
-
-	"gfx.cafe/open/jrpc/pkg/codec"
-
-	"gfx.cafe/open/jrpc"
 )
 
 type testService struct{}
@@ -44,7 +42,7 @@ func (s *testService) EchoWithCtx(ctx context.Context, str string, i int, args *
 }
 
 func (s *testService) PeerInfo(ctx context.Context) codec.PeerInfo {
-	return jrpc.PeerInfoFromContext(ctx)
+	return server.PeerInfoFromContext(ctx)
 }
 
 func (s *testService) Sleep(ctx context.Context, duration time.Duration) {
@@ -78,7 +76,7 @@ func (s *testService) ReturnError() error {
 }
 
 func (s *testService) CallMeBack(ctx context.Context, method string, args []any) (any, error) {
-	c, ok := jrpc.ConnFromContext(ctx)
+	c, ok := codec.ConnFromContext(ctx)
 	if !ok {
 		return nil, errors.New("no client")
 	}
@@ -88,7 +86,7 @@ func (s *testService) CallMeBack(ctx context.Context, method string, args []any)
 }
 
 func (s *testService) CallMeBackLater(ctx context.Context, method string, args []any) error {
-	c, ok := jrpc.ConnFromContext(ctx)
+	c, ok := codec.ConnFromContext(ctx)
 	if !ok {
 		return errors.New("no client")
 	}
diff --git a/pkg/jrpctest/suites.go b/pkg/jrpctest/suites.go
index e040cda..34564a4 100644
--- a/pkg/jrpctest/suites.go
+++ b/pkg/jrpctest/suites.go
@@ -8,20 +8,21 @@ import (
 	"testing"
 	"time"
 
-	"gfx.cafe/open/jrpc"
 	"gfx.cafe/open/jrpc/pkg/codec"
+	"gfx.cafe/open/jrpc/pkg/server"
+
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
 
-type ClientMaker func() jrpc.Conn
-type ServerMaker func() (*jrpc.Server, ClientMaker, func())
+type ClientMaker func() codec.Conn
+type ServerMaker func() (*server.Server, ClientMaker, func())
 
 type BasicTestSuiteArgs struct {
 	ServerMaker ServerMaker
 }
 
-type TestContext func(t *testing.T, server *jrpc.Server, client jrpc.Conn)
+type TestContext func(t *testing.T, server *server.Server, client codec.Conn)
 
 func RunBasicTestSuite(t *testing.T, args BasicTestSuiteArgs) {
 	var executeTest = func(t *testing.T, c TestContext) {
@@ -40,7 +41,7 @@ func RunBasicTestSuite(t *testing.T, args BasicTestSuiteArgs) {
 	}
 
 	t.Parallel()
-	makeTest("Request", func(t *testing.T, server *jrpc.Server, client jrpc.Conn) {
+	makeTest("Request", func(t *testing.T, server *server.Server, client codec.Conn) {
 		var resp EchoResult
 		err := client.Do(nil, &resp, "test_echo", []any{"hello", 10, &EchoArgs{"world"}})
 		require.NoError(t, err)
@@ -49,20 +50,20 @@ func RunBasicTestSuite(t *testing.T, args BasicTestSuiteArgs) {
 		}
 	})
 
-	makeTest("ResponseType", func(t *testing.T, server *jrpc.Server, client jrpc.Conn) {
-		if err := jrpc.CallInto(nil, client, nil, "test_echo", "hello", 10, &EchoArgs{"world"}); err != nil {
+	makeTest("ResponseType", func(t *testing.T, server *server.Server, client codec.Conn) {
+		if err := codec.CallInto(nil, client, nil, "test_echo", "hello", 10, &EchoArgs{"world"}); err != nil {
 			t.Errorf("Passing nil as result should be fine, but got an error: %v", err)
 		}
 		var resultVar EchoResult
 		// Note: passing the var, not a ref
-		err := jrpc.CallInto(nil, client, resultVar, "test_echo", "hello", 10, &EchoArgs{"world"})
+		err := codec.CallInto(nil, client, resultVar, "test_echo", "hello", 10, &EchoArgs{"world"})
 		if err == nil {
 			t.Error("Passing a var as result should be an error")
 		}
 	})
 
-	makeTest("BatchRequest", func(t *testing.T, server *jrpc.Server, client jrpc.Conn) {
-		batch := []*jrpc.BatchElem{
+	makeTest("BatchRequest", func(t *testing.T, server *server.Server, client codec.Conn) {
+		batch := []*codec.BatchElem{
 			{
 				Method: "test_echo",
 				Params: []any{"hello", 10, &EchoArgs{"world"}},
@@ -82,7 +83,7 @@ func RunBasicTestSuite(t *testing.T, args BasicTestSuiteArgs) {
 		if err := client.BatchCall(nil, batch...); err != nil {
 			t.Fatal(err)
 		}
-		wantResult := []*jrpc.BatchElem{
+		wantResult := []*codec.BatchElem{
 			{
 				Method: "test_echo",
 				Params: []any{"hello", 10, &EchoArgs{"world"}},
@@ -113,21 +114,21 @@ func RunBasicTestSuite(t *testing.T, args BasicTestSuiteArgs) {
 		}
 	})
 
-	makeTest("ResposeType", func(t *testing.T, server *jrpc.Server, client jrpc.Conn) {
-		if err := jrpc.CallInto(nil, client, nil, "test_echo", "hello", 10, &EchoArgs{"world"}); err != nil {
+	makeTest("ResposeType", func(t *testing.T, server *server.Server, client codec.Conn) {
+		if err := codec.CallInto(nil, client, nil, "test_echo", "hello", 10, &EchoArgs{"world"}); err != nil {
 			t.Errorf("Passing nil as result should be fine, but got an error: %v", err)
 		}
 		var resultVar EchoResult
 		// Note: passing the var, not a ref
-		err := jrpc.CallInto(nil, client, resultVar, "test_echo", "hello", 10, &EchoArgs{"world"})
+		err := codec.CallInto(nil, client, resultVar, "test_echo", "hello", 10, &EchoArgs{"world"})
 		if err == nil {
 			t.Error("Passing a var as result should be an error")
 		}
 	})
 
-	makeTest("ErrorReturnType", func(t *testing.T, server *jrpc.Server, client jrpc.Conn) {
+	makeTest("ErrorReturnType", func(t *testing.T, server *server.Server, client codec.Conn) {
 		var resp any
-		err := jrpc.CallInto(nil, client, &resp, "test_returnError")
+		err := codec.CallInto(nil, client, &resp, "test_returnError")
 		require.Error(t, err)
 
 		// Check code.
@@ -143,13 +144,13 @@ func RunBasicTestSuite(t *testing.T, args BasicTestSuiteArgs) {
 			t.Fatalf("wrong error data %#v, want %#v", e.ErrorData(), testError{}.ErrorData())
 		}
 	})
-	makeTest("Notify", func(t *testing.T, server *jrpc.Server, client jrpc.Conn) {
+	makeTest("Notify", func(t *testing.T, server *server.Server, client codec.Conn) {
 		if err := client.Notify(context.Background(), "test_echo", []any{"hello", 10, &EchoArgs{"world"}}); err != nil {
 			t.Fatal(err)
 		}
 	})
 
-	makeTest("context cancel", func(t *testing.T, server *jrpc.Server, client jrpc.Conn) {
+	makeTest("context cancel", func(t *testing.T, server *server.Server, client codec.Conn) {
 		maxContextCancelTimeout := 300 * time.Millisecond
 		// The actual test starts here.
 		var (
@@ -179,7 +180,7 @@ func RunBasicTestSuite(t *testing.T, args BasicTestSuiteArgs) {
 
 				// Now perform a call with the context.
 				// The key thing here is that no call will ever complete successfully.
-				err := jrpc.CallInto(ctx, client, nil, "test_block")
+				err := codec.CallInto(ctx, client, nil, "test_block")
 				switch {
 				case err == nil:
 					_, hasDeadline := ctx.Deadline()
@@ -198,7 +199,7 @@ func RunBasicTestSuite(t *testing.T, args BasicTestSuiteArgs) {
 	})
 }
 
-type ServerRemaker func(address string) (*jrpc.Server, ClientMaker, func())
+type ServerRemaker func(address string) (*server.Server, ClientMaker, func())
 
 type ReconnectTestSuiteArgs struct {
 	ServerMaker ServerMaker
@@ -215,7 +216,7 @@ func RunReconnectSuite(t *testing.T, args BasicTestSuiteArgs) {
 
 // This test checks that requests made through Call can be canceled by canceling
 // the context.
-func cancelTester(t *testing.T, server *jrpc.Server, client jrpc.Conn) {
+func cancelTester(t *testing.T, server *server.Server, client codec.Conn) {
 	maxContextCancelTimeout := 300 * time.Millisecond
 
 	// The actual test starts here.
@@ -246,7 +247,7 @@ func cancelTester(t *testing.T, server *jrpc.Server, client jrpc.Conn) {
 
 			// Now perform a call with the context.
 			// The key thing here is that no call will ever complete successfully.
-			err := jrpc.CallInto(ctx, client, nil, "test_block")
+			err := codec.CallInto(ctx, client, nil, "test_block")
 			switch {
 			case err == nil:
 				_, hasDeadline := ctx.Deadline()
diff --git a/server.go b/pkg/server/server.go
similarity index 97%
rename from server.go
rename to pkg/server/server.go
index 9a4097e..2f591cc 100644
--- a/server.go
+++ b/pkg/server/server.go
@@ -1,4 +1,4 @@
-package jrpc
+package server
 
 import (
 	"context"
@@ -18,7 +18,7 @@ import (
 
 // Server is an RPC server.
 type Server struct {
-	services Handler
+	services codec.Handler
 	run      int32
 	codecs   mapset.Set
 	Tracing  Tracing
@@ -29,7 +29,7 @@ type Tracing struct {
 }
 
 // NewServer creates a new server instance with no registered handlers.
-func NewServer(r Handler) *Server {
+func NewServer(r codec.Handler) *Server {
 	server := &Server{
 		codecs: mapset.NewSet(),
 		run:    1,
@@ -121,16 +121,15 @@ func (s *Server) ServeCodec(pctx context.Context, remote codec.ReaderWriter) {
 				} else {
 					defer wg.Done()
 				}
-				s.services.ServeRPC(v, &Request{
-					ctx: ctx,
-					RequestMarshaling: RequestMarshaling{
+				s.services.ServeRPC(v, codec.NewRequestFromRaw(
+					ctx,
+					&codec.RequestMarshaling{
 						ID:      v.msg.ID,
 						Version: v.msg.Version,
 						Method:  v.msg.Method,
 						Params:  v.msg.Params,
 						Peer:    remote.PeerInfo(),
-					},
-				})
+					}))
 			}()
 		}
 		wg.Wait()
diff --git a/readme.md b/readme.md
index 9d106ce..c5bd253 100644
--- a/readme.md
+++ b/readme.md
@@ -5,19 +5,29 @@ this is a bottom up implementation of jsonrpc2, primarily made for hosting eth-l
 many packages are unused / incomplete. the one which are done i have put below in the readme
 
 ```
-conn.go        -  defines the interface a json rpc client
-jrpc.go        - define the Handler, HandlerFunc, and ResponseWriter
-request.go     - define Request, along with json marshaling for the request
-response.go    - define Response, along with json marshaling for the response
-server.go      - implemntation of a server, which uses a backing codec.ReaderWriter
-codec/         - codec related things
-  stdio/       - implementation of jrpc.Conn and codec.ReaderWriter
-  errors.go    - jsonrpc2 error codes and marshaling
-  json.go      - jsonrpc2 json rules, encoding, decoding
-  peer.go      - peerinfo
-  transport.go - define ReaderWriter interface
-  wire.go      - jsonrpc2 wire protocol marshaling, like ID and Version
-jmux/          - a chi based router which satisfies the jrpc.Handler interface
-clientutil/    - common utilities for client implementations to use
-  idreply.go   - generalizes making a request with an incrementing id, then waiting on it
+exports.go       - export things in subpackages to jrpc namespace, cleaning up the public use package
+pkg/             - packages for implementing jrpc
+  clientutil/      - common utilities for client implementations to use
+    idreply.go       - generalizes making a request with an incrementing id, then waiting on it
+    helper.go        - helpers for decoding messages, etc
+  codec/           - codec related things. used by client and server implementations
+    errors.go        - jsonrpc2 error codes and marshaling
+    json.go          - jsonrpc2 json rules, encoding, decoding
+    peer.go          - peerinfo
+    transport.go     - define ReaderWriter interface
+    wire.go          - jsonrpc2 wire protocol marshaling, like ID and Version
+    server.go        - a server server implementation that uses the codec
+    jrpc.go          - define the Handler, HandlerFunc, and ResponseWriter
+    reqresp.go       - define Request, Response, along with json marshaling for the request
+  jrpctest/        - utilities for testing client and server.
+    suite.go         - implementors of client and server should pass this
+contrib/         - packages that add to jrpc
+  jmux/            - a chi based router which satisfies the jrpc.Handler interface
+  handlers/        - special jrpc handlers
+    argreflect/      - go-ethereum style struct reflection
+  middleware/      - pre implemented middleware
+  openrpc/         - openapi specification implementation
+  subscription     - WIP: subscription engine for go-ethereum style subs
+
+
 ```
diff --git a/response.go b/response.go
deleted file mode 100644
index 07ae27f..0000000
--- a/response.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package jrpc
-
-import (
-	"encoding/json"
-	codec2 "gfx.cafe/open/jrpc/pkg/codec"
-)
-
-type Response struct {
-	Version codec2.Version    `json:"jsonrpc,omitempty"`
-	ID      *codec2.ID        `json:"id,omitempty"`
-	Result  json.RawMessage   `json:"result,omitempty"`
-	Error   *codec2.JsonError `json:"error,omitempty"`
-}
-
-func (r *Response) Msg() *codec2.Message {
-	out := &codec2.Message{}
-	if r.ID != nil {
-		out.ID = r.ID
-	}
-	if r.Error != nil {
-		out.Error = r.Error
-	} else {
-		out.Result = r.Result
-	}
-	return out
-}
-- 
GitLab