diff --git a/client/client.go b/client/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..4f0bbe030329e4d614da9f6df0e70ff2eb77288d
--- /dev/null
+++ b/client/client.go
@@ -0,0 +1,51 @@
+// Copyright 2015, 2016 Eris Industries (UK) Ltd.
+// This file is part of Eris-RT
+
+// Eris-RT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Eris-RT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Eris-RT.  If not, see <http://www.gnu.org/licenses/>.
+
+package client
+
+import (
+	"github.com/tendermint/go-rpc/client"
+
+	tendermint_client "github.com/eris-ltd/eris-db/rpc/tendermint/client"
+	"github.com/eris-ltd/eris-db/txs"
+)
+
+type Client interface{
+	Broadcast(transaction txs.Tx) (*txs.Receipt, error)
+}
+
+// NOTE [ben] Compiler check to ensure ErisClient successfully implements
+// eris-db/client.Client
+var _ Client = (*ErisClient)(nil)
+
+// Eris-Client is 
+type ErisClient struct {
+	broadcastRPC string
+}
+
+//------------------------------------------------------------------------------------
+// broadcast to blockchain node
+// NOTE: [ben] Eris Client first continues from tendermint rpc, but will have handshake to negotiate
+// protocol version for moving towards rpc/v1 
+
+func (erisClient *ErisClient) Broadcast(tx txs.Tx) (*txs.Receipt, error) {
+	client := rpcclient.NewClientURI(erisClient.broadcastRPC)
+	receipt, err := tendermint_client.BroadcastTx(client, tx)
+	if err != nil {
+		return nil, err
+	}
+	return &receipt, nil
+}
\ No newline at end of file
diff --git a/client/client_test.go b/client/client_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..54f6621a127ba7f51b512c28082759a25d5e28b6
--- /dev/null
+++ b/client/client_test.go
@@ -0,0 +1,18 @@
+// Copyright 2015, 2016 Eris Industries (UK) Ltd.
+// This file is part of Eris-RT
+
+// Eris-RT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Eris-RT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Eris-RT.  If not, see <http://www.gnu.org/licenses/>.
+
+package client
+
diff --git a/client/cmd/transaction.go b/client/cmd/transaction.go
index f6c8e0b1c17b3f10594a3e73c444d36706e05480..02c21edad1df1f3049a1719d583a46a4b8063b39 100644
--- a/client/cmd/transaction.go
+++ b/client/cmd/transaction.go
@@ -60,7 +60,7 @@ func buildTransactionCommand() {
 		Short: "eris-client tx name --amt <amt> --name <name> --data <data>",
 		Long:  "eris-client tx name --amt <amt> --name <name> --data <data>",
 		Run:   func(cmd *cobra.Command, args []string) {
-			// transaction.Name(clientDo)
+			transaction.Name(clientDo)
 		},
 		PreRun: assertParameters,
 	}
diff --git a/client/core/core.go b/client/core/transaction_factory.go
similarity index 87%
rename from client/core/core.go
rename to client/core/transaction_factory.go
index 4aa2ea7f5142c26756cdba5ddd2ce63d737021e2..ace2eebba538be20169a42d55779d79f2c1b40dc 100644
--- a/client/core/core.go
+++ b/client/core/transaction_factory.go
@@ -114,18 +114,18 @@ func Name(nodeAddr, signAddr, pubkey, addr, amtS, nonceS, feeS, name, data strin
 	return tx, nil
 }
 
-// type PermFunc struct {
-// 	Name string
-// 	Args string
-// }
+type PermFunc struct {
+	Name string
+	Args string
+}
 
-// var PermsFuncs = []PermFunc{
-// 	{"set_base", "address, permission flag, value"},
-// 	{"unset_base", "address, permission flag"},
-// 	{"set_global", "permission flag, value"},
-// 	{"add_role", "address, role"},
-// 	{"rm_role", "address, role"},
-// }
+var PermsFuncs = []PermFunc{
+	{"set_base", "address, permission flag, value"},
+	{"unset_base", "address, permission flag"},
+	{"set_global", "permission flag, value"},
+	{"add_role", "address, role"},
+	{"rm_role", "address, role"},
+}
 
 // func Permissions(nodeAddr, signAddr, pubkey, addrS, nonceS, permFunc string, argsS []string) (*txs.PermissionsTx, error) {
 // 	pub, _, nonce, err := checkCommon(nodeAddr, signAddr, pubkey, addrS, "0", nonceS)
@@ -300,35 +300,6 @@ func coreNewAccount(nodeAddr, pubkey, chainID string) (*types.NewAccountTx, erro
 //------------------------------------------------------------------------------------
 // sign and broadcast
 
-func Pub(addr, rpcAddr string) (pubBytes []byte, err error) {
-	args := map[string]string{
-		"addr": addr,
-	}
-	pubS, err := RequestResponse(rpcAddr, "pub", args)
-	if err != nil {
-		return
-	}
-	return hex.DecodeString(pubS)
-}
-
-func Sign(signBytes, signAddr, signRPC string) (sig [64]byte, err error) {
-	args := map[string]string{
-		"msg":  signBytes,
-		"hash": signBytes, // TODO:[ben] backwards compatibility
-		"addr": signAddr,
-	}
-	sigS, err := RequestResponse(signRPC, "sign", args)
-	if err != nil {
-		return
-	}
-	sigBytes, err := hex.DecodeString(sigS)
-	if err != nil {
-		return
-	}
-	copy(sig[:], sigBytes)
-	return
-}
-
 func Broadcast(tx txs.Tx, broadcastRPC string) (*txs.Receipt, error) {
 	client := rpcclient.NewClientURI(broadcastRPC)
 	receipt, err := tendermint_client.BroadcastTx(client, tx)
@@ -341,64 +312,64 @@ func Broadcast(tx txs.Tx, broadcastRPC string) (*txs.Receipt, error) {
 //------------------------------------------------------------------------------------
 // utils for talking to the key server
 
-type HTTPResponse struct {
-	Response string
-	Error    string
-}
+// type HTTPResponse struct {
+// 	Response string
+// 	Error    string
+// }
 
-func RequestResponse(addr, method string, args map[string]string) (string, error) {
-	b, err := json.Marshal(args)
-	if err != nil {
-		return "", err
-	}
-	endpoint := fmt.Sprintf("%s/%s", addr, method)
-	log.WithFields(log.Fields{
-		"key server endpoint": endpoint,
-		"request body": string(b),
-		}).Debugf("Sending request body to key server")
-	req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(b))
-	if err != nil {
-		return "", err
-	}
-	req.Header.Add("Content-Type", "application/json")
-	res, errS, err := requestResponse(req)
-	if err != nil {
-		return "", fmt.Errorf("Error calling eris-keys at %s: %s", endpoint, err.Error())
-	}
-	if errS != "" {
-		return "", fmt.Errorf("Error (string) calling eris-keys at %s: %s", endpoint, errS)
-	}
-	log.WithFields(log.Fields{
-		"endpoint": endpoint,
-		"request body": string(b),
-		"response": res,
-		}).Debugf("Received response from key server")
-	return res, nil
-}
+// func RequestResponse(addr, method string, args map[string]string) (string, error) {
+// 	b, err := json.Marshal(args)
+// 	if err != nil {
+// 		return "", err
+// 	}
+// 	endpoint := fmt.Sprintf("%s/%s", addr, method)
+// 	log.WithFields(log.Fields{
+// 		"key server endpoint": endpoint,
+// 		"request body": string(b),
+// 		}).Debugf("Sending request body to key server")
+// 	req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(b))
+// 	if err != nil {
+// 		return "", err
+// 	}
+// 	req.Header.Add("Content-Type", "application/json")
+// 	res, errS, err := requestResponse(req)
+// 	if err != nil {
+// 		return "", fmt.Errorf("Error calling eris-keys at %s: %s", endpoint, err.Error())
+// 	}
+// 	if errS != "" {
+// 		return "", fmt.Errorf("Error (string) calling eris-keys at %s: %s", endpoint, errS)
+// 	}
+// 	log.WithFields(log.Fields{
+// 		"endpoint": endpoint,
+// 		"request body": string(b),
+// 		"response": res,
+// 		}).Debugf("Received response from key server")
+// 	return res, nil
+// }
 
-func requestResponse(req *http.Request) (string, string, error) {
-	client := new(http.Client)
-	resp, err := client.Do(req)
-	if err != nil {
-		return "", "", err
-	}
-	if resp.StatusCode >= 400 {
-		return "", "", fmt.Errorf(resp.Status)
-	}
-	return unpackResponse(resp)
-}
+// func requestResponse(req *http.Request) (string, string, error) {
+// 	client := new(http.Client)
+// 	resp, err := client.Do(req)
+// 	if err != nil {
+// 		return "", "", err
+// 	}
+// 	if resp.StatusCode >= 400 {
+// 		return "", "", fmt.Errorf(resp.Status)
+// 	}
+// 	return unpackResponse(resp)
+// }
 
-func unpackResponse(resp *http.Response) (string, string, error) {
-	b, err := ioutil.ReadAll(resp.Body)
-	if err != nil {
-		return "", "", err
-	}
-	r := new(HTTPResponse)
-	if err := json.Unmarshal(b, r); err != nil {
-		return "", "", err
-	}
-	return r.Response, r.Error, nil
-}
+// func unpackResponse(resp *http.Response) (string, string, error) {
+// 	b, err := ioutil.ReadAll(resp.Body)
+// 	if err != nil {
+// 		return "", "", err
+// 	}
+// 	r := new(HTTPResponse)
+// 	if err := json.Unmarshal(b, r); err != nil {
+// 		return "", "", err
+// 	}
+// 	return r.Response, r.Error, nil
+// }
 
 //------------------------------------------------------------------------------------
 // sign and broadcast convenience
diff --git a/client/core/transaction_factory_test.go b/client/core/transaction_factory_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..5ff0eb22f1ca048857ad8e865dbca257343c30e6
--- /dev/null
+++ b/client/core/transaction_factory_test.go
@@ -0,0 +1,67 @@
+// Copyright 2015, 2016 Eris Industries (UK) Ltd.
+// This file is part of Eris-RT
+
+// Eris-RT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Eris-RT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Eris-RT.  If not, see <http://www.gnu.org/licenses/>.
+
+package core
+
+import (
+	"testing"
+
+	"github.com/eris-ltd/eris-db/keys"
+)
+
+// Unit tests for client/core an idael showcase for the need to
+// modularise and restructure the components of the code.
+
+func TestCheckCommon(t *testing.T) {
+
+}
+
+func TestSendTransaction(t *testing.T) {
+
+}
+
+
+func TestUtilInputAddress(t *testing.T) {
+	// test in parallel
+	t.Run("ExtractInputAddress from transaction", func (t *testing.T) {
+		t.Run("SendTransaction", )
+		// t.Run("NameTransaction", )
+		t.Run("CallTransaction", )
+		// t.Run("PermissionTransaction", )
+		// t.Run("BondTransaction", )
+		// t.Run("UnbondTransaction", )
+		// t.Run("RebondTransaction", )
+	})
+}
+
+func testUtilInputAddressSendTx(t *testing.T) {
+
+}
+
+//---------------------------------------------------------------------
+// Mock client for replacing signing done by eris-keys
+
+// NOTE [ben] Compiler check to ensure MockKeysClient successfully implements
+// eris-db/client.KeyClient
+var _ keys.KeyClient = (*MockKeyClient)(nil)
+
+type MockKeyClient struct{}
+
+func (mock *MockKeyClient) Sign(signBytes, signAddress []byte) (signature [64]byte, err error) {
+	return
+}
+
+func (mock *MockKeyClient) PublicKey(address []byte) (publicKey []byte, err error)
diff --git a/client/core/transaction_factory_util.go b/client/core/transaction_factory_util.go
new file mode 100644
index 0000000000000000000000000000000000000000..6951dcea59c1af98e2548c4ef5bfdce38ab1246b
--- /dev/null
+++ b/client/core/transaction_factory_util.go
@@ -0,0 +1,193 @@
+// Copyright 2015, 2016 Eris Industries (UK) Ltd.
+// This file is part of Eris-RT
+
+// Eris-RT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Eris-RT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Eris-RT.  If not, see <http://www.gnu.org/licenses/>.
+
+package core
+
+import (
+	
+	keys "github.com/eris-ltd/eris-db/keys"
+	txs "github.com/eris-ltd/eris-db/txs"
+)
+
+//------------------------------------------------------------------------------------
+// sign and broadcast convenience
+
+// tx has either one input or we default to the first one (ie for send/bond)
+// TODO: better support for multisig and bonding
+func signTx(signAddr, chainID string, tx_ txs.Tx) ([]byte, txs.Tx, error) {
+	signBytes := fmt.Sprintf("%X", account.SignBytes(chainID, tx_))
+	var inputAddr []byte
+	var sigED crypto.SignatureEd25519
+	switch tx := tx_.(type) {
+	case *txs.SendTx:
+		inputAddr = tx.Inputs[0].Address
+		defer func(s *crypto.SignatureEd25519) { tx.Inputs[0].Signature = *s }(&sigED)
+	case *txs.NameTx:
+		inputAddr = tx.Input.Address
+		defer func(s *crypto.SignatureEd25519) { tx.Input.Signature = *s }(&sigED)
+	case *txs.CallTx:
+		inputAddr = tx.Input.Address
+		defer func(s *crypto.SignatureEd25519) { tx.Input.Signature = *s }(&sigED)
+	case *txs.PermissionsTx:
+		inputAddr = tx.Input.Address
+		defer func(s *crypto.SignatureEd25519) { tx.Input.Signature = *s }(&sigED)
+	case *txs.BondTx:
+		inputAddr = tx.Inputs[0].Address
+		defer func(s *crypto.SignatureEd25519) {
+			tx.Signature = *s
+			tx.Inputs[0].Signature = *s
+		}(&sigED)
+	case *txs.UnbondTx:
+		inputAddr = tx.Address
+		defer func(s *crypto.SignatureEd25519) { tx.Signature = *s }(&sigED)
+	case *txs.RebondTx:
+		inputAddr = tx.Address
+		defer func(s *crypto.SignatureEd25519) { tx.Signature = *s }(&sigED)
+	}
+	addrHex := fmt.Sprintf("%X", inputAddr)
+	sig, err := Sign(signBytes, addrHex, signAddr)
+	if err != nil {
+		return nil, nil, err
+	}
+	sigED = crypto.SignatureEd25519(sig)
+	log.WithFields(log.Fields{
+		"transaction sign bytes": signBytes,
+		"account address": addrHex,
+		"signature": fmt.Sprintf("%X", sig), 
+		}).Debug("Signed transaction")
+	return inputAddr, tx_, nil
+}
+
+// readInputAddressFromTransaction returns the hexadecimal string form of the 
+func readInputAddressFromTransaction(tx_ txs.Tx) (addressHex string) {
+	// signBytes := fmt.Sprintf("%X", account.SignBytes(chainID, tx_))
+	var inputAddr []byte
+	// var sigED crypto.SignatureEd25519
+	switch tx := tx_.(type) {
+	case *txs.SendTx:
+		inputAddr = tx.Inputs[0].Address
+		// defer func(s *crypto.SignatureEd25519) { tx.Inputs[0].Signature = *s }(&sigED)
+	case *txs.NameTx:
+		inputAddr = tx.Input.Address
+		// defer func(s *crypto.SignatureEd25519) { tx.Input.Signature = *s }(&sigED)
+	case *txs.CallTx:
+		inputAddr = tx.Input.Address
+		// defer func(s *crypto.SignatureEd25519) { tx.Input.Signature = *s }(&sigED)
+	case *txs.PermissionsTx:
+		inputAddr = tx.Input.Address
+		// defer func(s *crypto.SignatureEd25519) { tx.Input.Signature = *s }(&sigED)
+	case *txs.BondTx:
+		inputAddr = tx.Inputs[0].Address
+		// defer func(s *crypto.SignatureEd25519) {
+		// 	tx.Signature = *s
+		// 	tx.Inputs[0].Signature = *s
+		// }(&sigED)
+	case *txs.UnbondTx:
+		inputAddr = tx.Address
+		// defer func(s *crypto.SignatureEd25519) { tx.Signature = *s }(&sigED)
+	case *txs.RebondTx:
+		inputAddr = tx.Address
+		// defer func(s *crypto.SignatureEd25519) { tx.Signature = *s }(&sigED)
+	}
+	addressHex := fmt.Sprintf("%X", inputAddr)
+	// sig, err := Sign(signBytes, addrHex, signAddr)
+	// if err != nil {
+	// 	return nil, nil, err
+	// }
+	// sigED = crypto.SignatureEd25519(sig)
+	return addressHex 
+}
+
+func checkCommon(, keyClient keys.KeyClient, pubkey, addr, amtS, nonceS string) (pub crypto.PubKey, amt int64, nonce int64, err error) {
+	if amtS == "" {
+		err = fmt.Errorf("input must specify an amount with the --amt flag")
+		return
+	}
+
+	var pubKeyBytes []byte
+	if pubkey == "" && addr == "" {
+		err = fmt.Errorf("at least one of --pubkey or --addr must be given")
+		return
+	} else if pubkey != "" {
+		if addr != "" {
+			log.WithFields(log.Fields{
+				"public key": pubkey,
+				"address": addr,
+				}).Info("you have specified both a pubkey and an address. the pubkey takes precedent")
+		}
+		pubKeyBytes, err = hex.DecodeString(pubkey)
+		if err != nil {
+			err = fmt.Errorf("pubkey is bad hex: %v", err)
+			return
+		}
+	} else {
+		// grab the pubkey from eris-keys
+		pubKeyBytes, err = Pub(addr, signAddr)
+		if err != nil {
+			err = fmt.Errorf("failed to fetch pubkey for address (%s): %v", addr, err)
+			return
+		}
+
+	}
+
+	if len(pubKeyBytes) == 0 {
+		err = fmt.Errorf("Error resolving public key")
+		return
+	}
+
+	amt, err = strconv.ParseInt(amtS, 10, 64)
+	if err != nil {
+		err = fmt.Errorf("amt is misformatted: %v", err)
+	}
+
+	var pubArray [32]byte
+	copy(pubArray[:], pubKeyBytes)
+	pub = crypto.PubKeyEd25519(pubArray)
+	addrBytes := pub.Address()
+
+	if nonceS == "" {
+		if nodeAddr == "" {
+			err = fmt.Errorf("input must specify a nonce with the --nonce flag or use --node-addr (or ERIS_CLIENT_NODE_ADDR) to fetch the nonce from a node")
+			return
+		}
+
+		// fetch nonce from node
+		client := rpcclient.NewClientURI(nodeAddr)
+		account, err2 := tendermint_client.GetAccount(client, addrBytes)
+		if err2 != nil {
+			err = fmt.Errorf("Error connecting to node (%s) to fetch nonce: %s", nodeAddr, err2.Error())
+			return
+		}
+		if account == nil {
+			err = fmt.Errorf("unknown account %X", addrBytes)
+			return
+		}
+		nonce = int64(account.Sequence) + 1
+		log.WithFields(log.Fields{
+			"nonce": nonce,
+			"node address": nodeAddr,
+			"account address": fmt.Sprintf("%X", addrBytes),
+			}).Debug("Fetch nonce from node")
+	} else {
+		nonce, err = strconv.ParseInt(nonceS, 10, 64)
+		if err != nil {
+			err = fmt.Errorf("nonce is misformatted: %v", err)
+			return
+		}
+	}
+
+	return
+}
\ No newline at end of file
diff --git a/client/transaction/transaction.go b/client/transaction/transaction.go
index faef29683f2a89da2375f74568fc58ee7126ad45..83f617614155bea7c6d4f556d0cf8a880600ea69 100644
--- a/client/transaction/transaction.go
+++ b/client/transaction/transaction.go
@@ -18,13 +18,13 @@ package transaction
 
 import (
 	"fmt"
-	// "io/ioutil"
 	"os"
 
 	log "github.com/eris-ltd/eris-logger"
 
 	"github.com/eris-ltd/eris-db/client/core"
 	"github.com/eris-ltd/eris-db/definitions"
+	"github.com/eris-ltd/eris-db/keys"
 )
 
 func Send(do *definitions.ClientDo) {
diff --git a/keys/key_client.go b/keys/key_client.go
new file mode 100644
index 0000000000000000000000000000000000000000..90452e0a83b57c8f08a73128cd94a7d3a3f13d03
--- /dev/null
+++ b/keys/key_client.go
@@ -0,0 +1,81 @@
+// Copyright 2015, 2016 Eris Industries (UK) Ltd.
+// This file is part of Eris-RT
+
+// Eris-RT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Eris-RT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Eris-RT.  If not, see <http://www.gnu.org/licenses/>.
+
+package keys
+
+import (
+	"encoding/hex"
+	"fmt"
+)
+
+type KeyClient interface{
+	// Sign needs to return the signature bytes for given message to sign
+	// and the address to sign it with.
+	Sign(signBytes, signAddress []byte) (signature [64]byte, err error)
+	// PublicKey needs to return the public key associated with a given address
+	PublicKey(address []byte) (publicKey []byte, err error)
+}
+
+// NOTE [ben] Compiler check to ensure ErisKeyClient successfully implements
+// eris-db/keys.KeyClient
+var _ KeyClient = (*ErisKeyClient)(nil)
+
+struct ErisKeyClient{
+	rpcString string
+}
+
+// ErisKeyClient.New returns a new eris-keys client for provided rpc location
+// Eris-keys connects over http request-responses
+func (erisKeys *ErisKeyClient) New(rpcString string) ErisKeyClient{
+	return &ErisKeysServer{
+		rpcString: rpcString,
+	}
+}
+
+// Eris-keys client Sign requests the signature from ErisKeysClient over rpc for the given
+// bytes to be signed and the address to sign them with.
+func (erisKeys *ErisKeyClient) Sign(signBytes, signAddress []byte) (signature [64]byte, err error) {
+	args := map[string]string{
+		"msg":  string(signBytes),
+		"hash": string(signBytes), // TODO:[ben] backwards compatibility
+		"addr": string(signAddress),
+	}
+	sigS, err := RequestResponse(erisKeys.rpcString, "sign", args)
+	if err != nil {
+		return
+	}
+	sigBytes, err := hex.DecodeString(sigS)
+	if err != nil {
+		return
+	}
+	copy(signature[:], sigBytes)
+	return
+}
+
+// Eris-keys client PublicKey requests the public key associated with an address from
+// the eris-keys server.
+func (erisKeys *ErisKeyClient) PublicKey(address []byte) (publicKey []byte, err error) {
+	args := map[string]string{
+		"addr": address,
+	}
+	pubS, err := RequestResponse(erisKeys.rpcString, "pub", args)
+	if err != nil {
+		return
+	}
+	// TODO: [ben] assert that received public key results in 
+	// address
+	return hex.DecodeString(pubS)
+}
\ No newline at end of file
diff --git a/keys/key_client_util.go b/keys/key_client_util.go
new file mode 100644
index 0000000000000000000000000000000000000000..d043779f7e72e2d16a2b7bc7b6560ba0a28a2f4d
--- /dev/null
+++ b/keys/key_client_util.go
@@ -0,0 +1,91 @@
+// Copyright 2015, 2016 Eris Industries (UK) Ltd.
+// This file is part of Eris-RT
+
+// Eris-RT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Eris-RT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Eris-RT.  If not, see <http://www.gnu.org/licenses/>.
+
+// version provides the current Eris-DB version and a VersionIdentifier
+// for the modules to identify their version with.
+
+package keys
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+
+	log "github.com/eris-ltd/eris-logger"
+)
+
+// Eris-Keys server connects over http request-response structures
+
+type HTTPResponse struct {
+	Response string
+	Error    string
+}
+
+func RequestResponse(addr, method string, args map[string]string) (string, error) {
+	b, err := json.Marshal(args)
+	if err != nil {
+		return "", err
+	}
+	endpoint := fmt.Sprintf("%s/%s", addr, method)
+	log.WithFields(log.Fields{
+		"key server endpoint": endpoint,
+		"request body": string(b),
+		}).Debugf("Sending request body to key server")
+	req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(b))
+	if err != nil {
+		return "", err
+	}
+	req.Header.Add("Content-Type", "application/json")
+	res, errS, err := requestResponse(req)
+	if err != nil {
+		return "", fmt.Errorf("Error calling eris-keys at %s: %s", endpoint, err.Error())
+	}
+	if errS != "" {
+		return "", fmt.Errorf("Error (string) calling eris-keys at %s: %s", endpoint, errS)
+	}
+	log.WithFields(log.Fields{
+		"endpoint": endpoint,
+		"request body": string(b),
+		"response": res,
+		}).Debugf("Received response from key server")
+	return res, nil
+}
+
+func requestResponse(req *http.Request) (string, string, error) {
+	client := new(http.Client)
+	resp, err := client.Do(req)
+	if err != nil {
+		return "", "", err
+	}
+	if resp.StatusCode >= 400 {
+		return "", "", fmt.Errorf(resp.Status)
+	}
+	return unpackResponse(resp)
+}
+
+func unpackResponse(resp *http.Response) (string, string, error) {
+	b, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return "", "", err
+	}
+	r := new(HTTPResponse)
+	if err := json.Unmarshal(b, r); err != nil {
+		return "", "", err
+	}
+	return r.Response, r.Error, nil
+}
\ No newline at end of file