diff --git a/.gitignore b/.gitignore
index 221e318e1134480da924f070bef662ab4e5e9ff0..12be4236c891d5077c99a4a556c9813fc1b86284 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@ target
 *.swp
 debug
 .idea
+.vscode
diff --git a/client/cmd/eris-client.go b/client/cmd/eris-client.go
index 57e8d3ba4b8f7da97c2dc4d8adae4711c02c1a15..f073141998de883a7134df05199485f30b5e53e0 100644
--- a/client/cmd/eris-client.go
+++ b/client/cmd/eris-client.go
@@ -23,8 +23,6 @@ import (
 
 	"github.com/spf13/cobra"
 
-	log "github.com/eris-ltd/eris-logger"
-
 	"github.com/eris-ltd/eris-db/definitions"
 	"github.com/eris-ltd/eris-db/version"
 )
@@ -41,14 +39,6 @@ Made with <3 by Eris Industries.
 
 Complete documentation is available at https://monax.io/docs/documentation
 ` + "\nVERSION:\n " + version.VERSION,
-	PersistentPreRun: func(cmd *cobra.Command, args []string) {
-		log.SetLevel(log.WarnLevel)
-		if clientDo.Verbose {
-			log.SetLevel(log.InfoLevel)
-		} else if clientDo.Debug {
-			log.SetLevel(log.DebugLevel)
-		}
-	},
 	Run: func(cmd *cobra.Command, args []string) { cmd.Help() },
 }
 
@@ -70,11 +60,12 @@ func AddGlobalFlags() {
 }
 
 func AddClientCommands() {
-	buildTransactionCommand()
-	ErisClientCmd.AddCommand(TransactionCmd)
+	ErisClientCmd.AddCommand(buildTransactionCommand())
+	ErisClientCmd.AddCommand(buildStatusCommand())
+
+	buildGenesisGenCommand()
+	ErisClientCmd.AddCommand(GenesisGenCmd)
 
-	buildStatusCommand()
-	ErisClientCmd.AddCommand(StatusCmd)
 }
 
 //------------------------------------------------------------------------------
diff --git a/client/cmd/genesis.go b/client/cmd/genesis.go
new file mode 100644
index 0000000000000000000000000000000000000000..ad8d01216b3fdcbc1304c7e8787581e533a49403
--- /dev/null
+++ b/client/cmd/genesis.go
@@ -0,0 +1,39 @@
+package commands
+
+import (
+	"fmt"
+
+	"github.com/eris-ltd/eris-db/genesis"
+
+	"github.com/spf13/cobra"
+)
+
+// TODO refactor these vars into a struct?
+var (
+	AccountsPathFlag   string
+	ValidatorsPathFlag string
+)
+
+var GenesisGenCmd = &cobra.Command{
+	Use:   "make-genesis",
+	Short: "eris-client make-genesis creates a genesis.json with known inputs",
+	Long:  "eris-client make-genesis creates a genesis.json with known inputs",
+
+	Run: func(cmd *cobra.Command, args []string) {
+		// TODO refactor to not panic
+		genesisFile, err := genesis.GenerateKnown(args[0], AccountsPathFlag, ValidatorsPathFlag)
+		if err != nil {
+			panic(err)
+		}
+		fmt.Println(genesisFile) // may want to save somewhere instead
+	},
+}
+
+func buildGenesisGenCommand() {
+	addGenesisPersistentFlags()
+}
+
+func addGenesisPersistentFlags() {
+	GenesisGenCmd.Flags().StringVarP(&AccountsPathFlag, "accounts", "", "", "path to accounts.csv with the following params: (pubkey, starting balance, name, permissions, setbit")
+	GenesisGenCmd.Flags().StringVarP(&ValidatorsPathFlag, "validators", "", "", "path to validators.csv with the following params: (pubkey, starting balance, name, permissions, setbit")
+}
diff --git a/client/cmd/status.go b/client/cmd/status.go
index 5b29803d848fba681b028bcc0cae3cf969853968..08807956dc1012a49aea2b49e1a50c8ac975e974 100644
--- a/client/cmd/status.go
+++ b/client/cmd/status.go
@@ -20,24 +20,23 @@ import (
 	"github.com/spf13/cobra"
 
 	"github.com/eris-ltd/eris-db/client/methods"
+	"github.com/eris-ltd/eris-db/util"
 )
 
-var StatusCmd = &cobra.Command{
-	Use:   "status",
-	Short: "eris-client status returns the current status from a chain.",
-	Long: `eris-client status returns the current status from a chain.
+func buildStatusCommand() *cobra.Command {
+	statusCmd := &cobra.Command{
+		Use:   "status",
+		Short: "eris-client status returns the current status from a chain.",
+		Long: `eris-client status returns the current status from a chain.
 `,
-	Run: func(cmd *cobra.Command, args []string) {
-		methods.Status(clientDo)
-	},
-}
-
-func buildStatusCommand() {
-	addStatusPersistentFlags()
-}
-
-func addStatusPersistentFlags() {
-	StatusCmd.PersistentFlags().StringVarP(&clientDo.NodeAddrFlag, "node-addr", "", defaultNodeRpcAddress(), "set the eris-db node rpc server address (default respects $ERIS_CLIENT_NODE_ADDRESS)")
+		Run: func(cmd *cobra.Command, args []string) {
+			err := methods.Status(clientDo)
+			if err != nil {
+				util.Fatalf("Could not get status: %s", err)
+			}
+		},
+	}
+	statusCmd.PersistentFlags().StringVarP(&clientDo.NodeAddrFlag, "node-addr", "", defaultNodeRpcAddress(), "set the eris-db node rpc server address (default respects $ERIS_CLIENT_NODE_ADDRESS)")
 	// TransactionCmd.PersistentFlags().StringVarP(&clientDo.PubkeyFlag, "pubkey", "", defaultPublicKey(), "specify the public key to sign with (defaults to $ERIS_CLIENT_PUBLIC_KEY)")
 	// TransactionCmd.PersistentFlags().StringVarP(&clientDo.AddrFlag, "addr", "", defaultAddress(), "specify the account address (for which the public key can be found at eris-keys) (default respects $ERIS_CLIENT_ADDRESS)")
 	// TransactionCmd.PersistentFlags().StringVarP(&clientDo.ChainidFlag, "chain-id", "", defaultChainId(), "specify the chainID (default respects $CHAIN_ID)")
@@ -46,4 +45,6 @@ func addStatusPersistentFlags() {
 	// // TransactionCmd.PersistentFlags().BoolVarP(&clientDo.SignFlag, "sign", "s", false, "sign the transaction using the eris-keys daemon")
 	// TransactionCmd.PersistentFlags().BoolVarP(&clientDo.BroadcastFlag, "broadcast", "b", true, "broadcast the transaction to the blockchain")
 	// TransactionCmd.PersistentFlags().BoolVarP(&clientDo.WaitFlag, "wait", "w", false, "wait for the transaction to be committed in a block")
+
+	return statusCmd
 }
diff --git a/client/cmd/transaction.go b/client/cmd/transaction.go
index 87a3e10c5ee99aeb84a97062280d98c4047d0016..a527577481c7fec56dc87ac25383cbfef6b7f3f7 100644
--- a/client/cmd/transaction.go
+++ b/client/cmd/transaction.go
@@ -17,37 +17,36 @@
 package commands
 
 import (
-	"os"
 	"strings"
 
 	"github.com/spf13/cobra"
 
-	log "github.com/eris-ltd/eris-logger"
-
-	"github.com/eris-ltd/eris-db/client/transaction"
+	"github.com/eris-ltd/eris-db/client/methods"
+	"github.com/eris-ltd/eris-db/util"
 )
 
-var TransactionCmd = &cobra.Command{
-	Use:   "tx",
-	Short: "eris-client tx formulates and signs a transaction to a chain",
-	Long: `eris-client tx formulates and signs a transaction to a chain.
-`,
-	Run: func(cmd *cobra.Command, args []string) { cmd.Help() },
-}
-
-func buildTransactionCommand() {
+func buildTransactionCommand() *cobra.Command {
 	// Transaction command has subcommands send, name, call, bond,
 	// unbond, rebond, permissions. Dupeout transaction is not accessible through the command line.
+	transactionCmd := &cobra.Command{
+		Use:   "tx",
+		Short: "eris-client tx formulates and signs a transaction to a chain",
+		Long:  "eris-client tx formulates and signs a transaction to a chain.",
+		Run:   func(cmd *cobra.Command, args []string) { cmd.Help() },
+	}
 
-	addTransactionPersistentFlags()
+	addTransactionPersistentFlags(transactionCmd)
 
 	// SendTx
-	var sendCmd = &cobra.Command{
+	sendCmd := &cobra.Command{
 		Use:   "send",
 		Short: "eris-client tx send --amt <amt> --to <addr>",
 		Long:  "eris-client tx send --amt <amt> --to <addr>",
 		Run: func(cmd *cobra.Command, args []string) {
-			transaction.Send(clientDo)
+			err := methods.Send(clientDo)
+			if err != nil {
+				util.Fatalf("Could not complete send: %s", err)
+			}
 		},
 		PreRun: assertParameters,
 	}
@@ -55,7 +54,7 @@ func buildTransactionCommand() {
 	sendCmd.Flags().StringVarP(&clientDo.ToFlag, "to", "t", "", "specify an address to send to")
 
 	// NameTx
-	var nameCmd = &cobra.Command{
+	nameCmd := &cobra.Command{
 		Use:   "name",
 		Short: "eris-client tx name --amt <amt> --name <name> --data <data>",
 		Long:  "eris-client tx name --amt <amt> --name <name> --data <data>",
@@ -71,12 +70,15 @@ func buildTransactionCommand() {
 	nameCmd.Flags().StringVarP(&clientDo.FeeFlag, "fee", "f", "", "specify the fee to send")
 
 	// CallTx
-	var callCmd = &cobra.Command{
+	callCmd := &cobra.Command{
 		Use:   "call",
 		Short: "eris-client tx call --amt <amt> --fee <fee> --gas <gas> --to <contract addr> --data <data>",
 		Long:  "eris-client tx call --amt <amt> --fee <fee> --gas <gas> --to <contract addr> --data <data>",
 		Run: func(cmd *cobra.Command, args []string) {
-			transaction.Call(clientDo)
+			err := methods.Call(clientDo)
+			if err != nil {
+				util.Fatalf("Could not complete call: %s", err)
+			}
 		},
 		PreRun: assertParameters,
 	}
@@ -87,7 +89,7 @@ func buildTransactionCommand() {
 	callCmd.Flags().StringVarP(&clientDo.GasFlag, "gas", "g", "", "specify the gas limit for a CallTx")
 
 	// BondTx
-	var bondCmd = &cobra.Command{
+	bondCmd := &cobra.Command{
 		Use:   "bond",
 		Short: "eris-client tx bond --pubkey <pubkey> --amt <amt> --unbond-to <address>",
 		Long:  "eris-client tx bond --pubkey <pubkey> --amt <amt> --unbond-to <address>",
@@ -100,7 +102,7 @@ func buildTransactionCommand() {
 	bondCmd.Flags().StringVarP(&clientDo.UnbondtoFlag, "to", "t", "", "specify an address to unbond to")
 
 	// UnbondTx
-	var unbondCmd = &cobra.Command{
+	unbondCmd := &cobra.Command{
 		Use:   "unbond",
 		Short: "eris-client tx unbond --addr <address> --height <block_height>",
 		Long:  "eris-client tx unbond --addr <address> --height <block_height>",
@@ -126,7 +128,7 @@ func buildTransactionCommand() {
 	rebondCmd.Flags().StringVarP(&clientDo.HeightFlag, "height", "n", "", "specify a height to unbond at")
 
 	// PermissionsTx
-	var permissionsCmd = &cobra.Command{
+	permissionsCmd := &cobra.Command{
 		Use:   "permission",
 		Short: "eris-client tx perm <function name> <args ...>",
 		Long:  "eris-client tx perm <function name> <args ...>",
@@ -136,20 +138,21 @@ func buildTransactionCommand() {
 		PreRun: assertParameters,
 	}
 
-	TransactionCmd.AddCommand(sendCmd, nameCmd, callCmd, bondCmd, unbondCmd, rebondCmd, permissionsCmd)
+	transactionCmd.AddCommand(sendCmd, nameCmd, callCmd, bondCmd, unbondCmd, rebondCmd, permissionsCmd)
+	return transactionCmd
 }
 
-func addTransactionPersistentFlags() {
-	TransactionCmd.PersistentFlags().StringVarP(&clientDo.SignAddrFlag, "sign-addr", "", defaultKeyDaemonAddress(), "set eris-keys daemon address (default respects $ERIS_CLIENT_SIGN_ADDRESS)")
-	TransactionCmd.PersistentFlags().StringVarP(&clientDo.NodeAddrFlag, "node-addr", "", defaultNodeRpcAddress(), "set the eris-db node rpc server address (default respects $ERIS_CLIENT_NODE_ADDRESS)")
-	TransactionCmd.PersistentFlags().StringVarP(&clientDo.PubkeyFlag, "pubkey", "", defaultPublicKey(), "specify the public key to sign with (defaults to $ERIS_CLIENT_PUBLIC_KEY)")
-	TransactionCmd.PersistentFlags().StringVarP(&clientDo.AddrFlag, "addr", "", defaultAddress(), "specify the account address (for which the public key can be found at eris-keys) (default respects $ERIS_CLIENT_ADDRESS)")
-	TransactionCmd.PersistentFlags().StringVarP(&clientDo.ChainidFlag, "chain-id", "", defaultChainId(), "specify the chainID (default respects $CHAIN_ID)")
-	TransactionCmd.PersistentFlags().StringVarP(&clientDo.NonceFlag, "nonce", "", "", "specify the nonce to use for the transaction (should equal the sender account's nonce + 1)")
-
-	// TransactionCmd.PersistentFlags().BoolVarP(&clientDo.SignFlag, "sign", "s", false, "sign the transaction using the eris-keys daemon")
-	TransactionCmd.PersistentFlags().BoolVarP(&clientDo.BroadcastFlag, "broadcast", "b", true, "broadcast the transaction to the blockchain")
-	TransactionCmd.PersistentFlags().BoolVarP(&clientDo.WaitFlag, "wait", "w", true, "wait for the transaction to be committed in a block")
+func addTransactionPersistentFlags(transactionCmd *cobra.Command) {
+	transactionCmd.PersistentFlags().StringVarP(&clientDo.SignAddrFlag, "sign-addr", "", defaultKeyDaemonAddress(), "set eris-keys daemon address (default respects $ERIS_CLIENT_SIGN_ADDRESS)")
+	transactionCmd.PersistentFlags().StringVarP(&clientDo.NodeAddrFlag, "node-addr", "", defaultNodeRpcAddress(), "set the eris-db node rpc server address (default respects $ERIS_CLIENT_NODE_ADDRESS)")
+	transactionCmd.PersistentFlags().StringVarP(&clientDo.PubkeyFlag, "pubkey", "", defaultPublicKey(), "specify the public key to sign with (defaults to $ERIS_CLIENT_PUBLIC_KEY)")
+	transactionCmd.PersistentFlags().StringVarP(&clientDo.AddrFlag, "addr", "", defaultAddress(), "specify the account address (for which the public key can be found at eris-keys) (default respects $ERIS_CLIENT_ADDRESS)")
+	transactionCmd.PersistentFlags().StringVarP(&clientDo.ChainidFlag, "chain-id", "", defaultChainId(), "specify the chainID (default respects $CHAIN_ID)")
+	transactionCmd.PersistentFlags().StringVarP(&clientDo.NonceFlag, "nonce", "", "", "specify the nonce to use for the transaction (should equal the sender account's nonce + 1)")
+
+	// transactionCmd.PersistentFlags().BoolVarP(&clientDo.SignFlag, "sign", "s", false, "sign the transaction using the eris-keys daemon")
+	transactionCmd.PersistentFlags().BoolVarP(&clientDo.BroadcastFlag, "broadcast", "b", true, "broadcast the transaction to the blockchain")
+	transactionCmd.PersistentFlags().BoolVarP(&clientDo.WaitFlag, "wait", "w", true, "wait for the transaction to be committed in a block")
 }
 
 //------------------------------------------------------------------------------
@@ -180,26 +183,21 @@ func defaultAddress() string {
 
 func assertParameters(cmd *cobra.Command, args []string) {
 	if clientDo.ChainidFlag == "" {
-		log.Fatal(`Please provide a chain id either through the flag --chain-id or environment variable $CHAIN_ID.`)
-		os.Exit(1)
+		util.Fatalf(`Please provide a chain id either through the flag --chain-id or environment variable $CHAIN_ID.`)
 	}
 
 	if !strings.HasPrefix(clientDo.NodeAddrFlag, "tcp://") &&
 		!strings.HasPrefix(clientDo.NodeAddrFlag, "unix://") {
 		// TODO: [ben] go-rpc will deprecate reformatting; also it is bad practice to auto-correct for this;
-		log.Warn(`Please use fully formed listening address for the node, including the tcp:// or unix:// prefix.`)
+		// TODO: [Silas] I've made this fatal, but I'm inclined to define the default as tcp:// and normalise as with http
+		// below
+		util.Fatalf(`Please use fully formed listening address for the node, including the tcp:// or unix:// prefix.`)
 	}
 
 	if !strings.HasPrefix(clientDo.SignAddrFlag, "http://") {
 		// NOTE: [ben] we preserve the auto-correction here as it is a simple http request-response to the key server.
+		// TODO: [Silas] we don't have logging here to log that we've done this. I'm inclined to either urls without a scheme
+		// and be quiet about it, or to make non-compliance fatal
 		clientDo.SignAddrFlag = "http://" + clientDo.SignAddrFlag
-		log.WithFields(log.Fields{
-			"signing address": clientDo.SignAddrFlag,
-		}).Warn(`Please use fully formed listening address for the key server; adding http:// prefix`)
 	}
-	log.WithFields(log.Fields{
-		"signing address": clientDo.SignAddrFlag,
-		"node address":    clientDo.NodeAddrFlag,
-		"chain id":        clientDo.ChainidFlag,
-	}).Debug("Asserted parameters")
 }
diff --git a/client/methods/call.go b/client/methods/call.go
new file mode 100644
index 0000000000000000000000000000000000000000..1bdcbdfeceea4f9c41663c244d76cddf392a6591
--- /dev/null
+++ b/client/methods/call.go
@@ -0,0 +1,55 @@
+// 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 methods
+
+import (
+	"fmt"
+
+	"github.com/eris-ltd/eris-db/client"
+	"github.com/eris-ltd/eris-db/client/rpc"
+	"github.com/eris-ltd/eris-db/definitions"
+	"github.com/eris-ltd/eris-db/keys"
+)
+
+func Call(do *definitions.ClientDo) error {
+	// construct two clients to call out to keys server and
+	// blockchain node.
+	logger, err := loggerFromClientDo(do, "Call")
+	if err != nil {
+		return fmt.Errorf("Could not generate logging config from ClientDo: %s", err)
+	}
+	erisKeyClient := keys.NewErisKeyClient(do.SignAddrFlag, logger)
+	erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag, logger)
+	// form the call transaction
+	callTransaction, err := rpc.Call(erisNodeClient, erisKeyClient,
+		do.PubkeyFlag, do.AddrFlag, do.ToFlag, do.AmtFlag, do.NonceFlag,
+		do.GasFlag, do.FeeFlag, do.DataFlag)
+	if err != nil {
+		return fmt.Errorf("Failed on forming Call Transaction: %s", err)
+	}
+	// TODO: [ben] we carry over the sign bool, but always set it to true,
+	// as we move away from and deprecate the api that allows sending unsigned
+	// transactions and relying on (our) receiving node to sign it.
+	txResult, err := rpc.SignAndBroadcast(do.ChainidFlag, erisNodeClient, erisKeyClient,
+		callTransaction, true, do.BroadcastFlag, do.WaitFlag)
+
+	if err != nil {
+		return fmt.Errorf("Failed on signing (and broadcasting) transaction: %s", err)
+	}
+	unpackSignAndBroadcast(txResult, logger)
+	return nil
+}
diff --git a/client/methods/helpers.go b/client/methods/helpers.go
new file mode 100644
index 0000000000000000000000000000000000000000..13c7138d26f1a9ddcc15a7e38fe505b6df43acef
--- /dev/null
+++ b/client/methods/helpers.go
@@ -0,0 +1,56 @@
+// 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 methods
+
+import (
+	"github.com/eris-ltd/eris-db/client/rpc"
+	"github.com/eris-ltd/eris-db/core"
+	"github.com/eris-ltd/eris-db/definitions"
+	"github.com/eris-ltd/eris-db/logging"
+	"github.com/eris-ltd/eris-db/logging/lifecycle"
+	"github.com/eris-ltd/eris-db/logging/loggers"
+)
+
+func unpackSignAndBroadcast(result *rpc.TxResult, logger loggers.InfoTraceLogger) {
+	if result == nil {
+		// if we don't provide --sign or --broadcast
+		return
+	}
+
+	logger = logger.With("transaction hash", result.Hash)
+
+	if result.Address != nil {
+		logger = logger.With("Contract Address", result.Address)
+	}
+
+	if result.Return != nil {
+		logger = logger.With("Block Hash", result.BlockHash,
+			"Return Value", result.Return,
+			"Exception", result.Exception,
+		)
+	}
+
+	logging.InfoMsg(logger, "SignAndBroadcast result")
+}
+
+func loggerFromClientDo(do *definitions.ClientDo, scope string) (loggers.InfoTraceLogger, error) {
+	lc, err := core.LoadLoggingConfigFromClientDo(do)
+	if err != nil {
+		return nil, err
+	}
+	return logging.WithScope(lifecycle.NewLoggerFromLoggingConfig(lc), scope), nil
+}
diff --git a/client/methods/send.go b/client/methods/send.go
new file mode 100644
index 0000000000000000000000000000000000000000..65c2db37e5584c6e11205097aa0d3c01c45343bb
--- /dev/null
+++ b/client/methods/send.go
@@ -0,0 +1,53 @@
+// 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 methods
+
+import (
+	"fmt"
+
+	"github.com/eris-ltd/eris-db/client"
+	"github.com/eris-ltd/eris-db/client/rpc"
+	"github.com/eris-ltd/eris-db/definitions"
+	"github.com/eris-ltd/eris-db/keys"
+)
+
+func Send(do *definitions.ClientDo) error {
+	// construct two clients to call out to keys server and
+	// blockchain node.
+	logger, err := loggerFromClientDo(do, "Send")
+	if err != nil {
+		return fmt.Errorf("Could not generate logging config from ClientDo: %s", err)
+	}
+	erisKeyClient := keys.NewErisKeyClient(do.SignAddrFlag, logger)
+	erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag, logger)
+	// form the send transaction
+	sendTransaction, err := rpc.Send(erisNodeClient, erisKeyClient,
+		do.PubkeyFlag, do.AddrFlag, do.ToFlag, do.AmtFlag, do.NonceFlag)
+	if err != nil {
+		fmt.Errorf("Failed on forming Send Transaction: %s", err)
+	}
+	// TODO: [ben] we carry over the sign bool, but always set it to true,
+	// as we move away from and deprecate the api that allows sending unsigned
+	// transactions and relying on (our) receiving node to sign it.
+	txResult, err := rpc.SignAndBroadcast(do.ChainidFlag, erisNodeClient, erisKeyClient,
+		sendTransaction, true, do.BroadcastFlag, do.WaitFlag)
+	if err != nil {
+		return fmt.Errorf("Failed on signing (and broadcasting) transaction: %s", err)
+	}
+	unpackSignAndBroadcast(txResult, logger)
+	return nil
+}
diff --git a/client/methods/status.go b/client/methods/status.go
index ba2470e7645ba0759445daed1042211c8f184401..dd5bf72523bc9ba5277fadf10fb6e9bf2c1d7209 100644
--- a/client/methods/status.go
+++ b/client/methods/status.go
@@ -19,35 +19,35 @@ package methods
 import (
 	"fmt"
 
-	log "github.com/eris-ltd/eris-logger"
-
 	"github.com/eris-ltd/eris-db/client"
 	"github.com/eris-ltd/eris-db/definitions"
 )
 
-func Status(do *definitions.ClientDo) {
-	erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag)
+func Status(do *definitions.ClientDo) error {
+	logger, err := loggerFromClientDo(do, "Status")
+	if err != nil {
+		return fmt.Errorf("Could not generate logging config from ClientDo: %s", err)
+	}
+	erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag, logger)
 	genesisHash, validatorPublicKey, latestBlockHash, latestBlockHeight, latestBlockTime, err := erisNodeClient.Status()
 	if err != nil {
-		log.Errorf("Error requesting status from chain at (%s): %s", do.NodeAddrFlag, err)
-		return
+		return fmt.Errorf("Error requesting status from chain at (%s): %s", do.NodeAddrFlag, err)
 	}
 
 	chainName, chainId, genesisHashfromChainId, err := erisNodeClient.ChainId()
 	if err != nil {
-		log.Errorf("Error requesting chainId from chain at (%s): %s", do.NodeAddrFlag, err)
-		return
+		return fmt.Errorf("Error requesting chainId from chain at (%s): %s", do.NodeAddrFlag, err)
 	}
 
-	log.WithFields(log.Fields{
-		"chain":                    do.NodeAddrFlag,
-		"genesisHash":              fmt.Sprintf("%X", genesisHash),
-		"chainName":                chainName,
-		"chainId":                  chainId,
-		"genesisHash from chainId": fmt.Sprintf("%X", genesisHashfromChainId),
-		"validator public key":     fmt.Sprintf("%X", validatorPublicKey),
-		"latest block hash":        fmt.Sprintf("%X", latestBlockHash),
-		"latest block height":      latestBlockHeight,
-		"latest block time":        latestBlockTime,
-	}).Info("status")
+	logger.Info("chain", do.NodeAddrFlag,
+		"genesisHash", fmt.Sprintf("%X", genesisHash),
+		"chainName", chainName,
+		"chainId", chainId,
+		"genesisHash from chainId", fmt.Sprintf("%X", genesisHashfromChainId),
+		"validator public key", fmt.Sprintf("%X", validatorPublicKey),
+		"latest block hash", fmt.Sprintf("%X", latestBlockHash),
+		"latest block height", latestBlockHeight,
+		"latest block time", latestBlockTime,
+	)
+	return nil
 }
diff --git a/client/mock/client_mock.go b/client/mock/client_mock.go
index 12cdd76cf9b15f9575898224889a1528eb8a58b7..adcde24bd7ba41b53b2f21feedb62189da477a92 100644
--- a/client/mock/client_mock.go
+++ b/client/mock/client_mock.go
@@ -23,6 +23,7 @@ import (
 	. "github.com/eris-ltd/eris-db/client"
 	consensus_types "github.com/eris-ltd/eris-db/consensus/types"
 	core_types "github.com/eris-ltd/eris-db/core/types"
+	"github.com/eris-ltd/eris-db/logging/loggers"
 	"github.com/eris-ltd/eris-db/txs"
 )
 
@@ -117,3 +118,7 @@ func (mock *MockNodeClient) GetName(name string) (owner []byte, data string, exp
 func (mock *MockNodeClient) ListValidators() (blockHeight int, bondedValidators, unbondingValidators []consensus_types.Validator, err error) {
 	return 0, nil, nil, nil
 }
+
+func (mock *MockNodeClient) Logger() loggers.InfoTraceLogger {
+	return loggers.NewNoopInfoTraceLogger()
+}
diff --git a/client/client.go b/client/node_client.go
similarity index 84%
rename from client/client.go
rename to client/node_client.go
index 34275c8b2deacc6a10d18d18a496297dd3b44430..4502d77cca860fc0708f8ec14248502a515843ac 100644
--- a/client/client.go
+++ b/client/node_client.go
@@ -21,18 +21,16 @@ import (
 	// "strings"
 
 	"github.com/tendermint/go-rpc/client"
-	// Note [ben]: this is included to silence the logger from tendermint/go-rpc/client
-	// see func init()
-	tendermint_log "github.com/tendermint/log15"
-
-	log "github.com/eris-ltd/eris-logger"
 
 	acc "github.com/eris-ltd/eris-db/account"
 	consensus_types "github.com/eris-ltd/eris-db/consensus/types"
 	core_types "github.com/eris-ltd/eris-db/core/types"
+	"github.com/eris-ltd/eris-db/logging"
+	"github.com/eris-ltd/eris-db/logging/loggers"
 	tendermint_client "github.com/eris-ltd/eris-db/rpc/tendermint/client"
 	tendermint_types "github.com/eris-ltd/eris-db/rpc/tendermint/core/types"
 	"github.com/eris-ltd/eris-db/txs"
+	tmLog15 "github.com/tendermint/log15"
 )
 
 type NodeClient interface {
@@ -48,6 +46,9 @@ type NodeClient interface {
 	DumpStorage(address []byte) (storage *core_types.Storage, err error)
 	GetName(name string) (owner []byte, data string, expirationBlock int, err error)
 	ListValidators() (blockHeight int, bondedValidators, unbondingValidators []consensus_types.Validator, err error)
+
+	// Logging context for this NodeClient
+	Logger() loggers.InfoTraceLogger
 }
 
 type NodeWebsocketClient interface {
@@ -58,29 +59,30 @@ type NodeWebsocketClient interface {
 	Close()
 }
 
-// NOTE [ben] Compiler check to ensure ErisNodeClient successfully implements
+// NOTE [ben] Compiler check to ensure erisNodeClient successfully implements
 // eris-db/client.NodeClient
-var _ NodeClient = (*ErisNodeClient)(nil)
+var _ NodeClient = (*erisNodeClient)(nil)
 
 // Eris-Client is a simple struct exposing the client rpc methods
-
-type ErisNodeClient struct {
+type erisNodeClient struct {
 	broadcastRPC string
+	logger       loggers.InfoTraceLogger
 }
 
 // ErisKeyClient.New returns a new eris-keys client for provided rpc location
 // Eris-keys connects over http request-responses
-func NewErisNodeClient(rpcString string) *ErisNodeClient {
-	return &ErisNodeClient{
+func NewErisNodeClient(rpcString string, logger loggers.InfoTraceLogger) *erisNodeClient {
+	return &erisNodeClient{
 		broadcastRPC: rpcString,
+		logger:       logging.WithScope(logger, "ErisNodeClient"),
 	}
 }
 
 // Note [Ben]: This is a hack to silence Tendermint logger from tendermint/go-rpc
 // it needs to be initialised before go-rpc, hence it's placement here.
 func init() {
-	h := tendermint_log.LvlFilterHandler(tendermint_log.LvlWarn, tendermint_log.StdoutHandler)
-	tendermint_log.Root().SetHandler(h)
+	h := tmLog15.LvlFilterHandler(tmLog15.LvlWarn, tmLog15.StdoutHandler)
+	tmLog15.Root().SetHandler(h)
 }
 
 //------------------------------------------------------------------------------------
@@ -88,7 +90,7 @@ func init() {
 // NOTE: [ben] Eris Client first continues from tendermint rpc, but will have handshake to negotiate
 // protocol version for moving towards rpc/v1
 
-func (erisNodeClient *ErisNodeClient) Broadcast(tx txs.Tx) (*txs.Receipt, error) {
+func (erisNodeClient *erisNodeClient) Broadcast(tx txs.Tx) (*txs.Receipt, error) {
 	client := rpcclient.NewClientURI(erisNodeClient.broadcastRPC)
 	receipt, err := tendermint_client.BroadcastTx(client, tx)
 	if err != nil {
@@ -97,7 +99,7 @@ func (erisNodeClient *ErisNodeClient) Broadcast(tx txs.Tx) (*txs.Receipt, error)
 	return &receipt, nil
 }
 
-func (erisNodeClient *ErisNodeClient) DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error) {
+func (erisNodeClient *erisNodeClient) DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error) {
 	var wsAddr string
 	// TODO: clean up this inherited mess on dealing with the address prefixes.
 	nodeAddr := erisNodeClient.broadcastRPC
@@ -115,16 +117,17 @@ func (erisNodeClient *ErisNodeClient) DeriveWebsocketClient() (nodeWsClient Node
 	// }
 	// wsAddr = "ws://" + wsAddr
 	wsAddr = nodeAddr
-	log.WithFields(log.Fields{
-		"websocket address": wsAddr,
-		"endpoint":          "/websocket",
-	}).Debug("Subscribing to websocket address")
+	logging.TraceMsg(erisNodeClient.logger, "Subscribing to websocket address",
+		"websocket address", wsAddr,
+		"endpoint", "/websocket",
+	)
 	wsClient := rpcclient.NewWSClient(wsAddr, "/websocket")
 	if _, err = wsClient.Start(); err != nil {
 		return nil, err
 	}
-	derivedErisNodeWebsocketClient := &ErisNodeWebsocketClient{
+	derivedErisNodeWebsocketClient := &erisNodeWebsocketClient{
 		tendermintWebsocket: wsClient,
+		logger:              logging.WithScope(erisNodeClient.logger, "ErisNodeWebsocketClient"),
 	}
 	return derivedErisNodeWebsocketClient, nil
 }
@@ -134,7 +137,7 @@ func (erisNodeClient *ErisNodeClient) DeriveWebsocketClient() (nodeWsClient Node
 
 // Status returns the ChainId (GenesisHash), validator's PublicKey, latest block hash
 // the block height and the latest block time.
-func (erisNodeClient *ErisNodeClient) Status() (GenesisHash []byte, ValidatorPublicKey []byte, LatestBlockHash []byte, LatestBlockHeight int, LatestBlockTime int64, err error) {
+func (erisNodeClient *erisNodeClient) Status() (GenesisHash []byte, ValidatorPublicKey []byte, LatestBlockHash []byte, LatestBlockHeight int, LatestBlockTime int64, err error) {
 	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
 	res, err := tendermint_client.Status(client)
 	if err != nil {
@@ -152,7 +155,7 @@ func (erisNodeClient *ErisNodeClient) Status() (GenesisHash []byte, ValidatorPub
 	return
 }
 
-func (erisNodeClient *ErisNodeClient) ChainId() (ChainName, ChainId string, GenesisHash []byte, err error) {
+func (erisNodeClient *erisNodeClient) ChainId() (ChainName, ChainId string, GenesisHash []byte, err error) {
 	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
 	chainIdResult, err := tendermint_client.ChainId(client)
 	if err != nil {
@@ -170,7 +173,7 @@ func (erisNodeClient *ErisNodeClient) ChainId() (ChainName, ChainId string, Gene
 
 // QueryContract executes the contract code at address with the given data
 // NOTE: there is no check on the caller;
-func (erisNodeClient *ErisNodeClient) QueryContract(callerAddress, calleeAddress, data []byte) (ret []byte, gasUsed int64, err error) {
+func (erisNodeClient *erisNodeClient) QueryContract(callerAddress, calleeAddress, data []byte) (ret []byte, gasUsed int64, err error) {
 	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
 	callResult, err := tendermint_client.Call(client, callerAddress, calleeAddress, data)
 	if err != nil {
@@ -182,7 +185,7 @@ func (erisNodeClient *ErisNodeClient) QueryContract(callerAddress, calleeAddress
 }
 
 // QueryContractCode executes the contract code at address with the given data but with provided code
-func (erisNodeClient *ErisNodeClient) QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error) {
+func (erisNodeClient *erisNodeClient) QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error) {
 	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
 	// TODO: [ben] Call and CallCode have an inconsistent signature; it makes sense for both to only
 	// have a single address that is the contract to query.
@@ -196,7 +199,7 @@ func (erisNodeClient *ErisNodeClient) QueryContractCode(address, code, data []by
 }
 
 // GetAccount returns a copy of the account
-func (erisNodeClient *ErisNodeClient) GetAccount(address []byte) (*acc.Account, error) {
+func (erisNodeClient *erisNodeClient) GetAccount(address []byte) (*acc.Account, error) {
 	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
 	account, err := tendermint_client.GetAccount(client, address)
 	if err != nil {
@@ -213,7 +216,7 @@ func (erisNodeClient *ErisNodeClient) GetAccount(address []byte) (*acc.Account,
 }
 
 // DumpStorage returns the full storage for an account.
-func (erisNodeClient *ErisNodeClient) DumpStorage(address []byte) (storage *core_types.Storage, err error) {
+func (erisNodeClient *erisNodeClient) DumpStorage(address []byte) (storage *core_types.Storage, err error) {
 	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
 	resultStorage, err := tendermint_client.DumpStorage(client, address)
 	if err != nil {
@@ -231,7 +234,7 @@ func (erisNodeClient *ErisNodeClient) DumpStorage(address []byte) (storage *core
 //--------------------------------------------------------------------------------------------
 // Name registry
 
-func (erisNodeClient *ErisNodeClient) GetName(name string) (owner []byte, data string, expirationBlock int, err error) {
+func (erisNodeClient *erisNodeClient) GetName(name string) (owner []byte, data string, expirationBlock int, err error) {
 	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
 	entryResult, err := tendermint_client.GetName(client, name)
 	if err != nil {
@@ -248,7 +251,7 @@ func (erisNodeClient *ErisNodeClient) GetName(name string) (owner []byte, data s
 
 //--------------------------------------------------------------------------------------------
 
-func (erisNodeClient *ErisNodeClient) ListValidators() (blockHeight int,
+func (erisNodeClient *erisNodeClient) ListValidators() (blockHeight int,
 	bondedValidators []consensus_types.Validator, unbondingValidators []consensus_types.Validator, err error) {
 	client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC)
 	validatorsResult, err := tendermint_client.ListValidators(client)
@@ -263,3 +266,7 @@ func (erisNodeClient *ErisNodeClient) ListValidators() (blockHeight int,
 	unbondingValidators = validatorsResult.UnbondingValidators
 	return
 }
+
+func (erisNodeClient *erisNodeClient) Logger() loggers.InfoTraceLogger {
+	return erisNodeClient.logger
+}
diff --git a/client/core/transaction_factory.go b/client/rpc/client.go
similarity index 93%
rename from client/core/transaction_factory.go
rename to client/rpc/client.go
index 569e1d8a2cd3fb5131ca42f9c96d4e6a5633a613..77ed13b528cf10853ab790088a456e777ecf910c 100644
--- a/client/core/transaction_factory.go
+++ b/client/rpc/client.go
@@ -14,27 +14,20 @@
 // 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
+package rpc
 
 import (
 	"encoding/hex"
 	"fmt"
 	"strconv"
 
-	log "github.com/eris-ltd/eris-logger"
-
 	ptypes "github.com/eris-ltd/eris-db/permission/types"
 
-	"github.com/eris-ltd/eris-db/account"
 	"github.com/eris-ltd/eris-db/client"
 	"github.com/eris-ltd/eris-db/keys"
 	"github.com/eris-ltd/eris-db/txs"
 )
 
-var (
-	MaxCommitWaitTimeSeconds = 20
-)
-
 //------------------------------------------------------------------------------------
 // core functions with string args.
 // validates strings and forms transaction
@@ -272,16 +265,14 @@ type TxResult struct {
 }
 
 // Preserve
-func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient keys.KeyClient, tx txs.Tx, sign, broadcast, wait bool) (txResult *TxResult, err error) {
+func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient keys.KeyClient, tx txs.Tx, sign,
+	broadcast, wait bool) (txResult *TxResult, err error) {
 	var inputAddr []byte
 	if sign {
 		inputAddr, tx, err = signTx(keyClient, chainID, tx)
 		if err != nil {
 			return nil, err
 		}
-		log.WithFields(log.Fields{
-			"transaction": string(account.SignBytes(chainID, tx)),
-		}).Debug("Signed transaction")
 	}
 
 	if broadcast {
@@ -300,23 +291,19 @@ func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient ke
 						// if broadcast threw an error, just return
 						return
 					}
-					log.Debug("Waiting for transaction to be confirmed.")
 					confirmation := <-confirmationChannel
 					if confirmation.Error != nil {
-						log.Errorf("Encountered error waiting for event: %s\n", confirmation.Error)
-						err = confirmation.Error
+						err = fmt.Errorf("Encountered error waiting for event: %s", confirmation.Error)
 						return
 					}
 					if confirmation.Exception != nil {
-						log.Errorf("Encountered Exception from chain: %s\n", confirmation.Exception)
-						err = confirmation.Exception
+						err = fmt.Errorf("Encountered Exception from chain: %s", confirmation.Exception)
 						return
 					}
 					txResult.BlockHash = confirmation.BlockHash
 					txResult.Exception = ""
 					eventDataTx, ok := confirmation.Event.(*txs.EventDataTx)
 					if !ok {
-						log.Errorf("Received wrong event type.")
 						err = fmt.Errorf("Received wrong event type.")
 						return
 					}
diff --git a/client/core/transaction_factory_test.go b/client/rpc/client_test.go
similarity index 91%
rename from client/core/transaction_factory_test.go
rename to client/rpc/client_test.go
index 4544655e1d045332e07678a772b09a1c267b155b..6541bd3f9f3b92b29a3c1c231669afde4350a0a2 100644
--- a/client/core/transaction_factory_test.go
+++ b/client/rpc/client_test.go
@@ -14,7 +14,7 @@
 // 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
+package rpc
 
 import (
 	"fmt"
@@ -26,19 +26,19 @@ import (
 	mockkeys "github.com/eris-ltd/eris-db/keys/mock"
 )
 
-func TestTransactionFactory(t *testing.T) {
+func Test(t *testing.T) {
 	mockKeyClient := mockkeys.NewMockKeyClient()
 	mockNodeClient := mockclient.NewMockNodeClient()
-	testTransactionFactorySend(t, mockNodeClient, mockKeyClient)
-	testTransactionFactoryCall(t, mockNodeClient, mockKeyClient)
-	testTransactionFactoryName(t, mockNodeClient, mockKeyClient)
-	testTransactionFactoryPermissions(t, mockNodeClient, mockKeyClient)
+	testSend(t, mockNodeClient, mockKeyClient)
+	testCall(t, mockNodeClient, mockKeyClient)
+	testName(t, mockNodeClient, mockKeyClient)
+	testPermissions(t, mockNodeClient, mockKeyClient)
 	// t.Run("BondTransaction", )
 	// t.Run("UnbondTransaction", )
 	// t.Run("RebondTransaction", )
 }
 
-func testTransactionFactorySend(t *testing.T,
+func testSend(t *testing.T,
 	nodeClient *mockclient.MockNodeClient, keyClient *mockkeys.MockKeyClient) {
 
 	// generate an ED25519 key and ripemd160 address
@@ -66,7 +66,7 @@ func testTransactionFactorySend(t *testing.T,
 	// TODO: test content of Transaction
 }
 
-func testTransactionFactoryCall(t *testing.T,
+func testCall(t *testing.T,
 	nodeClient *mockclient.MockNodeClient, keyClient *mockkeys.MockKeyClient) {
 
 	// generate an ED25519 key and ripemd160 address
@@ -99,7 +99,7 @@ func testTransactionFactoryCall(t *testing.T,
 	// TODO: test content of Transaction
 }
 
-func testTransactionFactoryName(t *testing.T,
+func testName(t *testing.T,
 	nodeClient *mockclient.MockNodeClient, keyClient *mockkeys.MockKeyClient) {
 
 	// generate an ED25519 key and ripemd160 address
@@ -130,7 +130,7 @@ func testTransactionFactoryName(t *testing.T,
 	// TODO: test content of Transaction
 }
 
-func testTransactionFactoryPermissions(t *testing.T,
+func testPermissions(t *testing.T,
 	nodeClient *mockclient.MockNodeClient, keyClient *mockkeys.MockKeyClient) {
 
 	// generate an ED25519 key and ripemd160 address
diff --git a/client/core/transaction_factory_util.go b/client/rpc/client_util.go
similarity index 92%
rename from client/core/transaction_factory_util.go
rename to client/rpc/client_util.go
index 3ad64a6ba36ad88ad5c11b6a0d70584285239095..df8ffe3f0fe77cd927014794878b6ada6806f421 100644
--- a/client/core/transaction_factory_util.go
+++ b/client/rpc/client_util.go
@@ -14,20 +14,19 @@
 // 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
+package rpc
 
 import (
 	"encoding/hex"
 	"fmt"
 	"strconv"
 
-	log "github.com/eris-ltd/eris-logger"
-
 	"github.com/tendermint/go-crypto"
 
 	acc "github.com/eris-ltd/eris-db/account"
 	"github.com/eris-ltd/eris-db/client"
 	"github.com/eris-ltd/eris-db/keys"
+	"github.com/eris-ltd/eris-db/logging"
 	ptypes "github.com/eris-ltd/eris-db/permission/types"
 	"github.com/eris-ltd/eris-db/txs"
 )
@@ -101,10 +100,10 @@ func checkCommon(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey,
 		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")
+			logging.InfoMsg(nodeClient.Logger(), "Both a public key and an address have been specified. The public key takes precedent.",
+				"public_key", pubkey,
+				"address", addr,
+			)
 		}
 		pubKeyBytes, err = hex.DecodeString(pubkey)
 		if err != nil {
@@ -151,10 +150,10 @@ func checkCommon(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey,
 			return pub, amt, nonce, err2
 		}
 		nonce = int64(account.Sequence) + 1
-		log.WithFields(log.Fields{
-			"nonce":           nonce,
-			"account address": fmt.Sprintf("%X", addrBytes),
-		}).Debug("Fetch nonce from node")
+		logging.TraceMsg(nodeClient.Logger(), "Fetch nonce from node",
+			"nonce", nonce,
+			"account address", addrBytes,
+		)
 	} else {
 		nonce, err = strconv.ParseInt(nonceS, 10, 64)
 		if err != nil {
diff --git a/client/transaction/transaction.go b/client/transaction/transaction.go
deleted file mode 100644
index e9840d244cacff3bb8b88d90d6dd23bf4eb00974..0000000000000000000000000000000000000000
--- a/client/transaction/transaction.go
+++ /dev/null
@@ -1,96 +0,0 @@
-// 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 transaction
-
-import (
-	"fmt"
-	"os"
-
-	log "github.com/eris-ltd/eris-logger"
-
-	"github.com/eris-ltd/eris-db/client"
-	"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) {
-	// construct two clients to call out to keys server and
-	// blockchain node.
-	erisKeyClient := keys.NewErisKeyClient(do.SignAddrFlag)
-	erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag)
-	// form the send transaction
-	sendTransaction, err := core.Send(erisNodeClient, erisKeyClient,
-		do.PubkeyFlag, do.AddrFlag, do.ToFlag, do.AmtFlag, do.NonceFlag)
-	if err != nil {
-		log.Fatalf("Failed on forming Send Transaction: %s", err)
-		return
-	}
-	// TODO: [ben] we carry over the sign bool, but always set it to true,
-	// as we move away from and deprecate the api that allows sending unsigned
-	// transactions and relying on (our) receiving node to sign it.
-	unpackSignAndBroadcast(
-		core.SignAndBroadcast(do.ChainidFlag, erisNodeClient,
-			erisKeyClient, sendTransaction, true, do.BroadcastFlag, do.WaitFlag))
-}
-
-func Call(do *definitions.ClientDo) {
-	// construct two clients to call out to keys server and
-	// blockchain node.
-	erisKeyClient := keys.NewErisKeyClient(do.SignAddrFlag)
-	erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag)
-	// form the call transaction
-	callTransaction, err := core.Call(erisNodeClient, erisKeyClient,
-		do.PubkeyFlag, do.AddrFlag, do.ToFlag, do.AmtFlag, do.NonceFlag,
-		do.GasFlag, do.FeeFlag, do.DataFlag)
-	if err != nil {
-		log.Fatalf("Failed on forming Call Transaction: %s", err)
-		return
-	}
-	// TODO: [ben] we carry over the sign bool, but always set it to true,
-	// as we move away from and deprecate the api that allows sending unsigned
-	// transactions and relying on (our) receiving node to sign it.
-	unpackSignAndBroadcast(
-		core.SignAndBroadcast(do.ChainidFlag, erisNodeClient,
-			erisKeyClient, callTransaction, true, do.BroadcastFlag, do.WaitFlag))
-}
-
-//----------------------------------------------------------------------
-// Helper functions
-
-func unpackSignAndBroadcast(result *core.TxResult, err error) {
-	if err != nil {
-		log.Fatalf("Failed on signing (and broadcasting) transaction: %s", err)
-		os.Exit(1)
-	}
-	if result == nil {
-		// if we don't provide --sign or --broadcast
-		return
-	}
-	printResult := log.Fields{
-		"transaction hash": fmt.Sprintf("%X", result.Hash),
-	}
-	if result.Address != nil {
-		printResult["Contract Address"] = fmt.Sprintf("%X", result.Address)
-	}
-	if result.Return != nil {
-		printResult["Block Hash"] = fmt.Sprintf("%X", result.BlockHash)
-		printResult["Return Value"] = fmt.Sprintf("%X", result.Return)
-		printResult["Exception"] = fmt.Sprintf("%s", result.Exception)
-	}
-	log.WithFields(printResult).Warn("Result")
-}
diff --git a/client/websocket_client.go b/client/websocket_client.go
index 42f1bf37d384f999cb5d913cbf741124e71f1736..429958aad169eab380d832f83a7b70e868ebde08 100644
--- a/client/websocket_client.go
+++ b/client/websocket_client.go
@@ -24,8 +24,8 @@ import (
 	"github.com/tendermint/go-rpc/client"
 	"github.com/tendermint/go-wire"
 
-	log "github.com/eris-ltd/eris-logger"
-
+	"github.com/eris-ltd/eris-db/logging"
+	"github.com/eris-ltd/eris-db/logging/loggers"
 	ctypes "github.com/eris-ltd/eris-db/rpc/tendermint/core/types"
 	"github.com/eris-ltd/eris-db/txs"
 )
@@ -41,29 +41,30 @@ type Confirmation struct {
 	Error     error
 }
 
-// NOTE [ben] Compiler check to ensure ErisNodeClient successfully implements
+// NOTE [ben] Compiler check to ensure erisNodeClient successfully implements
 // eris-db/client.NodeClient
-var _ NodeWebsocketClient = (*ErisNodeWebsocketClient)(nil)
+var _ NodeWebsocketClient = (*erisNodeWebsocketClient)(nil)
 
-type ErisNodeWebsocketClient struct {
+type erisNodeWebsocketClient struct {
 	// TODO: assert no memory leak on closing with open websocket
 	tendermintWebsocket *rpcclient.WSClient
+	logger              loggers.InfoTraceLogger
 }
 
 // Subscribe to an eventid
-func (erisNodeWebsocketClient *ErisNodeWebsocketClient) Subscribe(eventid string) error {
+func (erisNodeWebsocketClient *erisNodeWebsocketClient) Subscribe(eventid string) error {
 	// TODO we can in the background listen to the subscription id and remember it to ease unsubscribing later.
 	return erisNodeWebsocketClient.tendermintWebsocket.Subscribe(eventid)
 }
 
 // Unsubscribe from an eventid
-func (erisNodeWebsocketClient *ErisNodeWebsocketClient) Unsubscribe(subscriptionId string) error {
+func (erisNodeWebsocketClient *erisNodeWebsocketClient) Unsubscribe(subscriptionId string) error {
 	return erisNodeWebsocketClient.tendermintWebsocket.Unsubscribe(subscriptionId)
 }
 
 // Returns a channel that will receive a confirmation with a result or the exception that
 // has been confirmed; or an error is returned and the confirmation channel is nil.
-func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx txs.Tx, chainId string, inputAddr []byte) (chan Confirmation, error) {
+func (erisNodeWebsocketClient *erisNodeWebsocketClient) WaitForConfirmation(tx txs.Tx, chainId string, inputAddr []byte) (chan Confirmation, error) {
 	// check no errors are reported on the websocket
 	if err := erisNodeWebsocketClient.assertNoErrors(); err != nil {
 		return nil, err
@@ -88,7 +89,8 @@ func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx t
 			result := new(ctypes.ErisDBResult)
 			if wire.ReadJSONPtr(result, resultBytes, &err); err != nil {
 				// keep calm and carry on
-				log.Errorf("[eris-client] Failed to unmarshal json bytes for websocket event: %s", err)
+				logging.InfoMsg(erisNodeWebsocketClient.logger, "Failed to unmarshal json bytes for websocket event",
+					"error", err)
 				continue
 			}
 
@@ -97,36 +99,41 @@ func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx t
 				// Received confirmation of subscription to event streams
 				// TODO: collect subscription IDs, push into channel and on completion
 				// unsubscribe
-				log.Infof("[eris-client] recceived confirmation for event (%s) with subscription id (%s).",
-					subscription.Event, subscription.SubscriptionId)
+				logging.InfoMsg(erisNodeWebsocketClient.logger, "Received confirmation for event",
+					"event", subscription.Event,
+					"subscription_id", subscription.SubscriptionId)
 				continue
 			}
 
 			event, ok := (*result).(*ctypes.ResultEvent)
 			if !ok {
 				// keep calm and carry on
-				log.Warnf("[eris-client] Failed to cast to ResultEvent for websocket event: %s", *result)
+				logging.InfoMsg(erisNodeWebsocketClient.logger, "Failed to cast to ResultEvent for websocket event",
+					"event", event.Event)
 				continue
 			}
 
 			blockData, ok := event.Data.(txs.EventDataNewBlock)
 			if ok {
 				latestBlockHash = blockData.Block.Hash()
-				log.WithFields(log.Fields{
-					"new block":   blockData.Block,
-					"latest hash": latestBlockHash,
-				}).Debug("Registered new block")
+				logging.TraceMsg(erisNodeWebsocketClient.logger, "Registered new block",
+					"block", blockData.Block,
+					"latest_block_hash", latestBlockHash,
+				)
 				continue
 			}
 
 			// we don't accept events unless they came after a new block (ie. in)
 			if latestBlockHash == nil {
-				log.Infof("[eris-client] no first block has been registered, so ignoring event: %s", event.Event)
+				logging.InfoMsg(erisNodeWebsocketClient.logger, "First block has not been registered so ignoring event",
+					"event", event.Event)
 				continue
 			}
 
 			if event.Event != eid {
-				log.Warnf("[eris-client] received unsolicited event! Got %s, expected %s\n", event.Event, eid)
+				logging.InfoMsg(erisNodeWebsocketClient.logger, "Received unsolicited event",
+					"event_received", event.Event,
+					"event_expected", eid)
 				continue
 			}
 
@@ -143,10 +150,9 @@ func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx t
 			}
 
 			if !bytes.Equal(txs.TxHash(chainId, data.Tx), txs.TxHash(chainId, tx)) {
-				log.WithFields(log.Fields{
+				logging.TraceMsg(erisNodeWebsocketClient.logger, "Received different event",
 					// TODO: consider re-implementing TxID again, or other more clear debug
-					"received transaction event": txs.TxHash(chainId, data.Tx),
-				}).Debug("Received different event")
+					"received transaction event", txs.TxHash(chainId, data.Tx))
 				continue
 			}
 
@@ -188,13 +194,13 @@ func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx t
 	return confirmationChannel, nil
 }
 
-func (erisNodeWebsocketClient *ErisNodeWebsocketClient) Close() {
+func (erisNodeWebsocketClient *erisNodeWebsocketClient) Close() {
 	if erisNodeWebsocketClient.tendermintWebsocket != nil {
 		erisNodeWebsocketClient.tendermintWebsocket.Stop()
 	}
 }
 
-func (erisNodeWebsocketClient *ErisNodeWebsocketClient) assertNoErrors() error {
+func (erisNodeWebsocketClient *erisNodeWebsocketClient) assertNoErrors() error {
 	if erisNodeWebsocketClient.tendermintWebsocket != nil {
 		select {
 		case err := <-erisNodeWebsocketClient.tendermintWebsocket.ErrorsCh:
diff --git a/cmd/eris-db.go b/cmd/eris-db.go
index 605b12060b4f669c249dd4a04b05937f6b568262..5f66acdca4d5d50304bb864ddedd2e054a1a30fe 100644
--- a/cmd/eris-db.go
+++ b/cmd/eris-db.go
@@ -21,17 +21,12 @@ import (
 	"strconv"
 	"strings"
 
-	cobra "github.com/spf13/cobra"
+	"github.com/spf13/cobra"
 
-	log "github.com/eris-ltd/eris-logger"
-
-	definitions "github.com/eris-ltd/eris-db/definitions"
-	version "github.com/eris-ltd/eris-db/version"
+	"github.com/eris-ltd/eris-db/definitions"
+	"github.com/eris-ltd/eris-db/version"
 )
 
-// Global Do struct
-var do *definitions.Do
-
 var ErisDbCmd = &cobra.Command{
 	Use:   "eris-db",
 	Short: "Eris-DB is the server side of the eris chain.",
@@ -39,42 +34,30 @@ var ErisDbCmd = &cobra.Command{
 a modular consensus engine and application manager to run a chain to suit
 your needs.
 
-Made with <3 by Eris Industries.
+Made with <3 by Monax Industries.
 
 Complete documentation is available at https://monax.io/docs/documentation
 ` + "\nVERSION:\n " + version.VERSION,
-	PersistentPreRun: func(cmd *cobra.Command, args []string) {
-
-		log.SetLevel(log.WarnLevel)
-		if do.Verbose {
-			log.SetLevel(log.InfoLevel)
-		} else if do.Debug {
-			log.SetLevel(log.DebugLevel)
-		}
-	},
 	Run: func(cmd *cobra.Command, args []string) { cmd.Help() },
 }
 
 func Execute() {
-	InitErisDbCli()
-	AddGlobalFlags()
-	AddCommands()
+	do := definitions.NewDo()
+	AddGlobalFlags(do)
+	AddCommands(do)
 	ErisDbCmd.Execute()
 }
 
-func InitErisDbCli() {
-	// initialise an empty Do struct for command execution
-	do = definitions.NewDo()
-}
-
-func AddGlobalFlags() {
-	ErisDbCmd.PersistentFlags().BoolVarP(&do.Verbose, "verbose", "v", defaultVerbose(), "verbose output; more output than no output flags; less output than debug level; default respects $ERIS_DB_VERBOSE")
-	ErisDbCmd.PersistentFlags().BoolVarP(&do.Debug, "debug", "d", defaultDebug(), "debug level output; the most output available for eris-db; if it is too chatty use verbose flag; default respects $ERIS_DB_DEBUG")
+func AddGlobalFlags(do *definitions.Do) {
+	ErisDbCmd.PersistentFlags().BoolVarP(&do.Verbose, "verbose", "v",
+		defaultVerbose(),
+		"verbose output; more output than no output flags; less output than debug level; default respects $ERIS_DB_VERBOSE")
+	ErisDbCmd.PersistentFlags().BoolVarP(&do.Debug, "debug", "d", defaultDebug(),
+		"debug level output; the most output available for eris-db; if it is too chatty use verbose flag; default respects $ERIS_DB_DEBUG")
 }
 
-func AddCommands() {
-	buildServeCommand()
-	ErisDbCmd.AddCommand(ServeCmd)
+func AddCommands(do *definitions.Do) {
+	ErisDbCmd.AddCommand(buildServeCommand(do))
 }
 
 //------------------------------------------------------------------------------
diff --git a/cmd/serve.go b/cmd/serve.go
index eccf3388ab0c14bc167c8211ce3a9e82208a39f4..29319b1fd815498dae5ac82659211afacc8280c8 100644
--- a/cmd/serve.go
+++ b/cmd/serve.go
@@ -17,19 +17,19 @@
 package commands
 
 import (
+	"fmt"
 	"os"
 	"os/signal"
 	"path"
 	"syscall"
 
-	cobra "github.com/spf13/cobra"
-
-	log "github.com/eris-ltd/eris-logger"
-
-	"fmt"
+	"github.com/spf13/cobra"
 
-	core "github.com/eris-ltd/eris-db/core"
-	util "github.com/eris-ltd/eris-db/util"
+	"github.com/eris-ltd/eris-db/core"
+	"github.com/eris-ltd/eris-db/definitions"
+	"github.com/eris-ltd/eris-db/logging"
+	"github.com/eris-ltd/eris-db/logging/lifecycle"
+	"github.com/eris-ltd/eris-db/util"
 )
 
 const (
@@ -41,148 +41,157 @@ var DefaultConfigFilename = fmt.Sprintf("%s.%s",
 	DefaultConfigBasename,
 	DefaultConfigType)
 
-var ServeCmd = &cobra.Command{
-	Use:   "serve",
-	Short: "Eris-DB serve starts an eris-db node with client API enabled by default.",
-	Long: `Eris-DB serve starts an eris-db node with client API enabled by default.
+// build the serve subcommand
+func buildServeCommand(do *definitions.Do) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "serve",
+		Short: "Eris-DB serve starts an eris-db node with client API enabled by default.",
+		Long: `Eris-DB serve starts an eris-db node with client API enabled by default.
 The Eris-DB node is modularly configured for the consensus engine and application
 manager.  The client API can be disabled.`,
-	Example: fmt.Sprintf(`$ eris-db serve -- will start the Eris-DB node based on the configuration file "%s" in the current working directory
+		Example: fmt.Sprintf(`$ eris-db serve -- will start the Eris-DB node based on the configuration file "%s" in the current working directory
 $ eris-db serve --work-dir <path-to-working-directory> -- will start the Eris-DB node based on the configuration file "%s" in the provided working directory
 $ eris-db serve --chain-id <CHAIN_ID> -- will overrule the configuration entry assert_chain_id`,
-		DefaultConfigFilename, DefaultConfigFilename),
-	PreRun: func(cmd *cobra.Command, args []string) {
-		// if WorkDir was not set by a flag or by $ERIS_DB_WORKDIR
-		// NOTE [ben]: we can consider an `Explicit` flag that eliminates
-		// the use of any assumptions while starting Eris-DB
-		if do.WorkDir == "" {
-			if currentDirectory, err := os.Getwd(); err != nil {
-				log.Fatalf("No directory provided and failed to get current working directory: %v", err)
+			DefaultConfigFilename, DefaultConfigFilename),
+		PreRun: func(cmd *cobra.Command, args []string) {
+			// if WorkDir was not set by a flag or by $ERIS_DB_WORKDIR
+			// NOTE [ben]: we can consider an `Explicit` flag that eliminates
+			// the use of any assumptions while starting Eris-DB
+			if do.WorkDir == "" {
+				if currentDirectory, err := os.Getwd(); err != nil {
+					panic(fmt.Sprintf("No directory provided and failed to get current "+
+						"working directory: %v", err))
+					os.Exit(1)
+				} else {
+					do.WorkDir = currentDirectory
+				}
+			}
+			if !util.IsDir(do.WorkDir) {
+				panic(fmt.Sprintf("Provided working directory %s is not a directory",
+					do.WorkDir))
 				os.Exit(1)
-			} else {
-				do.WorkDir = currentDirectory
 			}
-		}
-		if !util.IsDir(do.WorkDir) {
-			log.Fatalf("Provided working directory %s is not a directory", do.WorkDir)
-		}
-	},
-	Run: Serve,
-}
-
-// build the serve subcommand
-func buildServeCommand() {
-	addServeFlags()
+		},
+		Run: ServeRunner(do),
+	}
+	addServeFlags(do, cmd)
+	return cmd
 }
 
-func addServeFlags() {
-	ServeCmd.PersistentFlags().StringVarP(&do.ChainId, "chain-id", "c",
+func addServeFlags(do *definitions.Do, serveCmd *cobra.Command) {
+	serveCmd.PersistentFlags().StringVarP(&do.ChainId, "chain-id", "c",
 		defaultChainId(), "specify the chain id to use for assertion against the genesis file or the existing state. If omitted, and no id is set in $CHAIN_ID, then assert_chain_id is used from the configuration file.")
-	ServeCmd.PersistentFlags().StringVarP(&do.WorkDir, "work-dir", "w",
+	serveCmd.PersistentFlags().StringVarP(&do.WorkDir, "work-dir", "w",
 		defaultWorkDir(), "specify the working directory for the chain to run.  If omitted, and no path set in $ERIS_DB_WORKDIR, the current working directory is taken.")
-	ServeCmd.PersistentFlags().StringVarP(&do.DataDir, "data-dir", "",
+	serveCmd.PersistentFlags().StringVarP(&do.DataDir, "data-dir", "",
 		defaultDataDir(), "specify the data directory.  If omitted and not set in $ERIS_DB_DATADIR, <working_directory>/data is taken.")
-	ServeCmd.PersistentFlags().BoolVarP(&do.DisableRpc, "disable-rpc", "",
+	serveCmd.PersistentFlags().BoolVarP(&do.DisableRpc, "disable-rpc", "",
 		defaultDisableRpc(), "indicate for the RPC to be disabled. If omitted the RPC is enabled by default, unless (deprecated) $ERISDB_API is set to false.")
 }
 
 //------------------------------------------------------------------------------
 // functions
-
-// serve() prepares the environment and sets up the core for Eris_DB to run.
-// After the setup succeeds, serve() starts the core and halts for core to
-// terminate.
-func Serve(cmd *cobra.Command, args []string) {
-	// load configuration from a single location to avoid a wrong configuration
-	// file is loaded.
-	err := do.ReadConfig(do.WorkDir, DefaultConfigBasename, DefaultConfigType)
-	if err != nil {
-		log.WithFields(log.Fields{
-			"directory": do.WorkDir,
-			"file":      DefaultConfigFilename,
-		}).Fatalf("Fatal error reading configuration")
-		os.Exit(1)
-	}
-	// if do.ChainId is not yet set, load chain_id for assertion from configuration file
-	if do.ChainId == "" {
-		if do.ChainId = do.Config.GetString("chain.assert_chain_id"); do.ChainId == "" {
-			log.Fatalf("Failed to read non-empty string for ChainId from config.")
-			os.Exit(1)
-		}
-	}
+func NewCoreFromDo(do *definitions.Do) (*core.Core, error) {
 	// load the genesis file path
 	do.GenesisFile = path.Join(do.WorkDir,
 		do.Config.GetString("chain.genesis_file"))
+
 	if do.Config.GetString("chain.genesis_file") == "" {
-		log.Fatalf("Failed to read non-empty string for genesis file from config.")
-		os.Exit(1)
+		return nil, fmt.Errorf("The config value chain.genesis_file is empty, " +
+			"but should be set to the location of the genesis.json file.")
 	}
 	// Ensure data directory is set and accessible
 	if err := do.InitialiseDataDirectory(); err != nil {
-		log.Fatalf("Failed to initialise data directory (%s): %v", do.DataDir, err)
-		os.Exit(1)
+		return nil, fmt.Errorf("Failed to initialise data directory (%s): %v", do.DataDir, err)
 	}
-	log.WithFields(log.Fields{
-		"chainId":          do.ChainId,
-		"workingDirectory": do.WorkDir,
-		"dataDirectory":    do.DataDir,
-		"genesisFile":      do.GenesisFile,
-	}).Info("Eris-DB serve configuring")
 
-	consensusConfig, err := core.LoadConsensusModuleConfig(do)
+	loggerConfig, err := core.LoadLoggingConfigFromDo(do)
 	if err != nil {
-		log.Fatalf("Failed to load consensus module configuration: %s.", err)
-		os.Exit(1)
+		return nil, fmt.Errorf("Failed to load logging config: %s", err)
 	}
 
-	managerConfig, err := core.LoadApplicationManagerModuleConfig(do)
+	// Create a root logger to pass through to dependencies
+	logger := logging.WithScope(lifecycle.NewLoggerFromLoggingConfig(loggerConfig), "Serve")
+	// Capture all logging from tendermint/tendermint and tendermint/go-*
+	// dependencies
+	lifecycle.CaptureTendermintLog15Output(logger)
+	// And from stdlib go log
+	lifecycle.CaptureStdlibLogOutput(logger)
+
+	// if do.ChainId is not yet set, load chain_id for assertion from configuration file
+
+	if do.ChainId == "" {
+		if do.ChainId = do.Config.GetString("chain.assert_chain_id"); do.ChainId == "" {
+			return nil, fmt.Errorf("The config chain.assert_chain_id is empty, " +
+				"but should be set to the chain_id of the chain we are trying to run.")
+		}
+	}
+
+	logging.Msg(logger, "Loading configuration for serve command",
+		"chainId", do.ChainId,
+		"workingDirectory", do.WorkDir,
+		"dataDirectory", do.DataDir,
+		"genesisFile", do.GenesisFile)
+
+	consensusConfig, err := core.LoadConsensusModuleConfig(do)
 	if err != nil {
-		log.Fatalf("Failed to load application manager module configuration: %s.", err)
-		os.Exit(1)
+		return nil, fmt.Errorf("Failed to load consensus module configuration: %s.", err)
 	}
-	log.WithFields(log.Fields{
-		"consensusModule":    consensusConfig.Version,
-		"applicationManager": managerConfig.Version,
-	}).Debug("Modules configured")
 
-	newCore, err := core.NewCore(do.ChainId, consensusConfig, managerConfig)
+	managerConfig, err := core.LoadApplicationManagerModuleConfig(do)
 	if err != nil {
-		log.Fatalf("Failed to load core: %s", err)
+		return nil, fmt.Errorf("Failed to load application manager module configuration: %s.", err)
 	}
 
-	if !do.DisableRpc {
-		serverConfig, err := core.LoadServerConfig(do)
-		if err != nil {
-			log.Fatalf("Failed to load server configuration: %s.", err)
-			os.Exit(1)
-		}
+	logging.Msg(logger, "Modules configured",
+		"consensusModule", consensusConfig.Version,
+		"applicationManager", managerConfig.Version)
+
+	return core.NewCore(do.ChainId, consensusConfig, managerConfig, logger)
+}
 
-		serverProcess, err := newCore.NewGatewayV0(serverConfig)
+// ServeRunner() returns a command runner that prepares the environment and sets
+// up the core for Eris-DB to run. After the setup succeeds, it starts the core
+// and waits for the core to terminate.
+func ServeRunner(do *definitions.Do) func(*cobra.Command, []string) {
+	return func(cmd *cobra.Command, args []string) {
+		// load configuration from a single location to avoid a wrong configuration
+		// file is loaded.
+		err := do.ReadConfig(do.WorkDir, DefaultConfigBasename, DefaultConfigType)
 		if err != nil {
-			log.Fatalf("Failed to load servers: %s.", err)
-			os.Exit(1)
+			util.Fatalf("Fatal error reading configuration from %s/%s", do.WorkDir,
+				DefaultConfigFilename)
 		}
-		err = serverProcess.Start()
+
+		newCore, err := NewCoreFromDo(do)
+
 		if err != nil {
-			log.Fatalf("Failed to start servers: %s.", err)
-			os.Exit(1)
+			util.Fatalf("Failed to load core: %s", err)
 		}
-		_, err = newCore.NewGatewayTendermint(serverConfig)
-		if err != nil {
-			log.Fatalf("Failed to start Tendermint gateway")
+
+		if !do.DisableRpc {
+			serverConfig, err := core.LoadServerConfig(do)
+			if err != nil {
+				util.Fatalf("Failed to load server configuration: %s.", err)
+			}
+			serverProcess, err := newCore.NewGatewayV0(serverConfig)
+			if err != nil {
+				util.Fatalf("Failed to load servers: %s.", err)
+			}
+			err = serverProcess.Start()
+			if err != nil {
+				util.Fatalf("Failed to start servers: %s.", err)
+			}
+			_, err = newCore.NewGatewayTendermint(serverConfig)
+			if err != nil {
+				util.Fatalf("Failed to start Tendermint gateway")
+			}
+			<-serverProcess.StopEventChannel()
+		} else {
+			signals := make(chan os.Signal, 1)
+			signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
+			fmt.Fprintf(os.Stderr, "Received %s signal. Marmots out.", <-signals)
 		}
-		<-serverProcess.StopEventChannel()
-	} else {
-		signals := make(chan os.Signal, 1)
-		done := make(chan bool, 1)
-		signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
-		go func() {
-			signal := <-signals
-			// TODO: [ben] clean up core; in a manner consistent with enabled rpc
-			log.Fatalf("Received %s signal. Marmots out.", signal)
-			done <- true
-		}()
-		<-done
 	}
 }
 
diff --git a/config/config.go b/config/config.go
new file mode 100644
index 0000000000000000000000000000000000000000..32dc2c320950ee81bd0780cae6499baabd9c8824
--- /dev/null
+++ b/config/config.go
@@ -0,0 +1,187 @@
+// Copyright 2015, 2016 Monax 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 config
+
+import (
+	"bytes"
+	"fmt"
+	"text/template"
+)
+
+type ConfigServiceGeneral struct {
+	ChainImageName      string
+	UseDataContainer    bool
+	ExportedPorts       string
+	ContainerEntrypoint string
+}
+
+// TODO: [ben] increase the configurability upon need
+type ConfigChainGeneral struct {
+	AssertChainId       string
+	ErisdbMajorVersion  uint8
+	ErisdbMinorVersion  uint8
+	GenesisRelativePath string
+}
+
+type ConfigChainModule struct {
+	Name               string
+	MajorVersion       uint8
+	MinorVersion       uint8
+	ModuleRelativeRoot string
+}
+
+type ConfigTendermint struct {
+	Moniker  string
+	Seeds    string
+	FastSync bool
+}
+
+var serviceGeneralTemplate *template.Template
+var chainGeneralTemplate *template.Template
+var chainConsensusTemplate *template.Template
+var chainApplicationManagerTemplate *template.Template
+var tendermintTemplate *template.Template
+
+func init() {
+	var err error
+	if serviceGeneralTemplate, err = template.New("serviceGeneral").Parse(sectionServiceGeneral); err != nil {
+		panic(err)
+	}
+	if chainGeneralTemplate, err = template.New("chainGeneral").Parse(sectionChainGeneral); err != nil {
+		panic(err)
+	}
+	if chainConsensusTemplate, err = template.New("chainConsensus").Parse(sectionChainConsensus); err != nil {
+		panic(err)
+	}
+	if chainApplicationManagerTemplate, err = template.New("chainApplicationManager").Parse(sectionChainApplicationManager); err != nil {
+		panic(err)
+	}
+	if tendermintTemplate, err = template.New("tendermint").Parse(sectionTendermint); err != nil {
+		panic(err)
+	}
+}
+
+// NOTE: [ben] for 0.12.0-rc3 we only have a single configuration path
+// with Tendermint in-process as the consensus engine and ErisMint
+// in-process as the application manager, so we hard-code the few
+// parameters that are already templated.
+// Let's learn to walk before we can run.
+func GetConfigurationFileBytes(chainId, moniker, seeds string, chainImageName string,
+	useDataContainer bool, exportedPortsString, containerEntrypoint string) ([]byte, error) {
+
+	erisdbService := &ConfigServiceGeneral{
+		ChainImageName:      chainImageName,
+		UseDataContainer:    useDataContainer,
+		ExportedPorts:       exportedPortsString,
+		ContainerEntrypoint: containerEntrypoint,
+	}
+	erisdbChain := &ConfigChainGeneral{
+		AssertChainId:       chainId,
+		ErisdbMajorVersion:  uint8(0),
+		ErisdbMinorVersion:  uint8(12),
+		GenesisRelativePath: "genesis.json",
+	}
+	chainConsensusModule := &ConfigChainModule{
+		Name:               "tendermint",
+		MajorVersion:       uint8(0),
+		MinorVersion:       uint8(6),
+		ModuleRelativeRoot: "tendermint",
+	}
+	chainApplicationManagerModule := &ConfigChainModule{
+		Name:               "erismint",
+		MajorVersion:       uint8(0),
+		MinorVersion:       uint8(12),
+		ModuleRelativeRoot: "erismint",
+	}
+	tendermintModule := &ConfigTendermint{
+		Moniker:  moniker,
+		Seeds:    seeds,
+		FastSync: false,
+	}
+
+	// NOTE: [ben] according to StackOverflow appending strings with copy is
+	// more efficient than bytes.WriteString, but for readability and because
+	// this is not performance critical code we opt for bytes, which is
+	// still more efficient than + concatentation operator.
+	var buffer bytes.Buffer
+
+	// write copyright header
+	buffer.WriteString(headerCopyright)
+
+	// write section [service]
+	if err := serviceGeneralTemplate.Execute(&buffer, erisdbService); err != nil {
+		return nil, fmt.Errorf("Failed to write template service general for %s: %s",
+			chainId, err)
+	}
+	// write section for service dependencies; this is currently a static section
+	// with a fixed dependency on eris-keys
+	buffer.WriteString(sectionServiceDependencies)
+
+	// write section [chain]
+	if err := chainGeneralTemplate.Execute(&buffer, erisdbChain); err != nil {
+		return nil, fmt.Errorf("Failed to write template chain general for %s: %s",
+			chainId, err)
+	}
+
+	// write separator chain consensus
+	buffer.WriteString(separatorChainConsensus)
+	// write section [chain.consensus]
+	if err := chainConsensusTemplate.Execute(&buffer, chainConsensusModule); err != nil {
+		return nil, fmt.Errorf("Failed to write template chain consensus for %s: %s",
+			chainId, err)
+	}
+
+	// write separator chain application manager
+	buffer.WriteString(separatorChainApplicationManager)
+	// write section [chain.consensus]
+	if err := chainApplicationManagerTemplate.Execute(&buffer,
+		chainApplicationManagerModule); err != nil {
+		return nil, fmt.Errorf("Failed to write template chain application manager for %s: %s",
+			chainId, err)
+	}
+
+	// write separator servers
+	buffer.WriteString(separatorServerConfiguration)
+	// TODO: [ben] upon necessity replace this with template too
+	// write static section servers
+	buffer.WriteString(sectionServers)
+
+	// write separator modules
+	buffer.WriteString(separatorModules)
+
+	// write section module Tendermint
+	if err := tendermintTemplate.Execute(&buffer, tendermintModule); err != nil {
+		return nil, fmt.Errorf("Failed to write template tendermint for %s, moniker %s: %s",
+			chainId, moniker, err)
+	}
+
+	// write static section erismint
+	buffer.WriteString(sectionErisMint)
+
+	return buffer.Bytes(), nil
+}
+
+func GetExampleConfigFileBytes() ([]byte, error) {
+	return GetConfigurationFileBytes(
+		"simplechain",
+		"delectable_marmot",
+		"192.168.168.255",
+		"db:latest",
+		true,
+		"46657",
+		"eris-db")
+}
diff --git a/config/config_test.go b/config/config_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..e01346e846133f931b825009eb627aa6616f4b3b
--- /dev/null
+++ b/config/config_test.go
@@ -0,0 +1,37 @@
+// Copyright 2015, 2016 Monax 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 config
+
+import (
+	"bytes"
+	"testing"
+
+	"github.com/spf13/viper"
+	"github.com/stretchr/testify/assert"
+)
+
+// Since the logic for generating configuration files (in eris-cm) is split from
+// the logic for consuming them
+func TestGeneratedConfigIsUsable(t *testing.T) {
+	bs, err := GetExampleConfigFileBytes()
+	assert.NoError(t, err, "Should be able to create example config")
+	buf := bytes.NewBuffer(bs)
+	conf := viper.New()
+	viper.SetConfigType("toml")
+	err = conf.ReadConfig(buf)
+	assert.NoError(t, err, "Should be able to read example config into Viper")
+}
diff --git a/config/dump_config_test.go b/config/dump_config_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..4ea3dd0598fa3b78cdffeae44e95c4d8d35212a5
--- /dev/null
+++ b/config/dump_config_test.go
@@ -0,0 +1,20 @@
+// +build dumpconfig
+
+// Space above matters
+package config
+
+import (
+	"io/ioutil"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+// This is a little convenience for getting a config file dump. Just run:
+// go test -tags dumpconfig ./config
+// This pseudo test won't run unless the dumpconfig tag is
+func TestDumpConfig(t *testing.T) {
+	bs, err := GetExampleConfigFileBytes()
+	assert.NoError(t, err, "Should be able to create example config")
+	ioutil.WriteFile("config_dump.toml", bs, 0644)
+}
diff --git a/config/templates.go b/config/templates.go
new file mode 100644
index 0000000000000000000000000000000000000000..cead77d92b7f5c40988c88de97a92f0ed9a4d093
--- /dev/null
+++ b/config/templates.go
@@ -0,0 +1,318 @@
+// Copyright 2015, 2016 Monax 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 config
+
+const headerCopyright = `# 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/>.
+
+# This is a TOML configuration for Eris-DB chains generated by Eris-CM
+
+`
+
+const sectionServiceGeneral = `[service]
+# NOTE: this section is read by Eris tooling, and ignored by eris-db.
+# Image specifies the image name eris-cli needs to pull 
+# for running the chain.
+image = "{{.ChainImageName}}"
+# Define whether eris-cli needs to attach the data container
+# for the chain.
+data_container = {{.UseDataContainer}}
+# Specify a list of ports that need to be exported on the container.
+ports = {{.ExportedPorts}}
+{{ if ne .ContainerEntrypoint "" }}# Entrypoint points to the default action to execute
+# in the chain container.
+entry_point = "{{.ContainerEntrypoint}}"{{ end }}
+
+`
+
+const sectionServiceDependencies = `[dependencies]
+# NOTE: this section is read by Eris tooling, and ignored by eris-db.
+# Eris-db expects these services to be available; eric-cli tooling will
+# automatically set these services up for you.
+# Services to boot with/required by the chain
+services = [ "keys" ]
+
+`
+
+const sectionChainGeneral = `[chain]
+
+# ChainId is a human-readable name to identify the chain.
+# This must correspond to the chain_id defined in the genesis file
+# and the assertion here provides a safe-guard on misconfiguring chains.
+assert_chain_id = "{{.AssertChainId}}"
+# semantic major and minor version
+major_version = {{.ErisdbMajorVersion}}
+minor_version = {{.ErisdbMinorVersion}}
+# genesis file, relative path is to eris-db working directory
+genesis_file = "{{.GenesisRelativePath}}"
+
+`
+
+const separatorChainConsensus = `
+################################################################################
+##
+##  consensus
+##
+################################################################################
+
+`
+
+const sectionChainConsensus = `  [chain.consensus]
+  # consensus defines the module to use for consensus and
+  # this will define the peer-to-peer consensus network;
+  # accepted values are ("noops", "tmsp",) "tendermint"
+  name = "{{.Name}}"
+  # version is the major and minor semantic version;
+  # the version will be asserted on
+  major_version = {{.MajorVersion}}
+  minor_version = {{.MinorVersion}}
+  # relative path to consensus' module root folder
+  relative_root = "{{.ModuleRelativeRoot}}"
+
+  `
+
+const separatorChainApplicationManager = `
+################################################################################
+##
+##  application manager
+##
+################################################################################
+
+`
+
+const sectionChainApplicationManager = `  [chain.manager]
+  # application manager name defines the module to use for handling
+  # the transactions.  Supported names are "erismint"
+  name = "{{.Name}}"
+  # version is the major and minor semantic version;
+  # the version will be asserted on
+  major_version = {{.MajorVersion}}
+  minor_version = {{.MinorVersion}}
+  # relative path to application manager root folder
+  relative_root = "{{.ModuleRelativeRoot}}"
+
+  `
+
+const separatorServerConfiguration = `
+################################################################################
+################################################################################
+##
+## Server configurations
+##
+################################################################################
+################################################################################
+
+`
+
+// TODO: [ben] map entries to structure defined in eris-db
+const sectionServers = `[servers]
+
+  [servers.bind]
+  address = ""
+  port = 1337
+
+  [servers.tls]
+  tls = false
+  cert_path = ""
+  key_path = ""
+
+  [servers.cors]
+  enable = false
+  allow_origins = []
+  allow_credentials = false
+  allow_methods = []
+  allow_headers = []
+  expose_headers = []
+  max_age = 0
+
+  [servers.http]
+  json_rpc_endpoint = "/rpc"
+
+  [servers.websocket]
+  endpoint = "/socketrpc"
+  max_sessions = 50
+  read_buffer_size = 4096
+  write_buffer_size = 4096
+
+	[servers.tendermint]
+	# Multiple listeners can be separated with a comma
+	rpc_local_address = "0.0.0.0:46657"
+	endpoint = "/websocket"
+
+  [servers.logging]
+  console_log_level = "info"
+  file_log_level = "warn"
+  log_file = ""
+
+  `
+
+const separatorModules = `
+################################################################################
+################################################################################
+##
+## Module configurations - dynamically loaded based on chain configuration
+##
+################################################################################
+################################################################################
+
+`
+
+// TODO: [ben] make configurable
+const sectionTmsp = `
+################################################################################
+##
+## Tendermint Socket Protocol (TMSP)
+## version 0.6.0
+##
+## TMSP expects a tendermint consensus process to run and connect to Eris-DB
+##
+################################################################################
+
+[tmsp]
+# listener address for accepting tendermint socket protocol connections
+listener = "tcp://0.0.0.0:46658"
+
+`
+
+// TODO: [ben] minimal fields have been made configurable; expand where needed
+const sectionTendermint = `
+################################################################################
+##
+## Tendermint
+## version 0.6.0
+##
+## in-process execution of Tendermint consensus engine
+##
+################################################################################
+
+[tendermint]
+# private validator file is used by tendermint to keep the status
+# of the private validator, but also (currently) holds the private key
+# for the private vaildator to sign with.  This private key needs to be moved
+# out and directly managed by eris-keys
+# This file needs to be in the root directory
+private_validator_file = "priv_validator.json"
+
+  # Tendermint requires additional configuration parameters.
+  # Eris-DB's tendermint consensus module will load [tendermint.configuration]
+  # as the configuration for Tendermint.
+  # Eris-DB will respect the configurations set in this file where applicable,
+  # but reserves the option to override or block conflicting settings.
+  [tendermint.configuration]
+  # moniker is the name of the node on the tendermint p2p network
+  moniker = "{{.Moniker}}"
+  # seeds lists the peers tendermint can connect to join the network
+  seeds = "{{.Seeds}}"
+  # fast_sync allows a tendermint node to catch up faster when joining
+  # the network.
+  # NOTE: Tendermint has reported potential issues with fast_sync enabled.
+  # The recommended setting is for keeping it disabled.
+  fast_sync = {{.FastSync}}
+  # database backend to use for Tendermint. Supported "leveldb" and "memdb".
+  db_backend = "leveldb"
+  # logging level. Supported "error" < "warn" < "notice" < "info" < "debug"
+  log_level = "info"
+  # node local address
+  node_laddr = "0.0.0.0:46656"
+  # rpc local address
+	# NOTE: value is ignored when run in-process as RPC is
+	# handled by [servers.tendermint]
+  rpc_laddr = "0.0.0.0:46657"
+  # proxy application address - used for tmsp connections,
+  # and this port should not be exposed for in-process Tendermint
+  proxy_app = "tcp://127.0.0.1:46658"
+
+  # Extended Tendermint configuration settings
+  # for reference to Tendermint see https://github.com/tendermint/tendermint/blob/master/config/tendermint/config.go
+
+  # genesis_file = "./data/tendermint/genesis.json"
+  # skip_upnp = false
+  # addrbook_file = "./data/tendermint/addrbook.json"
+  # priv_validator_file = "./data/tendermint/priv_validator.json"
+  # db_dir = "./data/tendermint/data"
+  # prof_laddr = ""
+  # revision_file = "./data/tendermint/revision"
+  # cswal = "./data/tendermint/data/cswal"
+  # cswal_light = false
+
+  # block_size = 10000
+  # disable_data_hash = false
+  # timeout_propose = 3000
+  # timeout_propose_delta = 500
+  # timeout_prevote = 1000
+  # timeout_prevote_delta = 500
+  # timeout_precommit = 1000
+  # timeout_precommit_delta = 500
+  # timeout_commit = 1000
+  # mempool_recheck = true
+  # mempool_recheck_empty = true
+  # mempool_broadcast = true
+
+		[tendermint.configuration.p2p]
+		# Switch config keys
+		dial_timeout_seconds = 3
+		handshake_timeout_seconds = 20
+		max_num_peers = 20
+		authenticated_encryption = true
+
+		# MConnection config keys
+		send_rate = 512000
+		recv_rate = 512000
+
+		# Fuzz params
+		fuzz_enable = false # use the fuzz wrapped conn
+		fuzz_active = false # toggle fuzzing
+		fuzz_mode = "drop"  # eg. drop, delay
+		fuzz_max_delay_milliseconds = 3000
+		fuzz_prob_drop_rw = 0.2
+		fuzz_prob_drop_conn = 0.00
+		fuzz_prob_sleep = 0.00
+
+`
+
+const sectionErisMint = `
+################################################################################
+##
+## Eris-Mint
+## version 0.12.0
+##
+## The original Ethereum virtual machine with IAVL merkle trees
+## and tendermint/go-wire encoding
+##
+################################################################################
+
+[erismint]
+# Database backend to use for ErisMint state database.
+# Supported "leveldb" and "memdb".
+db_backend = "leveldb"
+# tendermint host address needs to correspond to tendermints configuration
+# of the rpc local address
+tendermint_host = "0.0.0.0:46657"
+
+`
diff --git a/config/viper.go b/config/viper.go
new file mode 100644
index 0000000000000000000000000000000000000000..cb59f540c3548177a1bbb23a356ed28c61583aff
--- /dev/null
+++ b/config/viper.go
@@ -0,0 +1,30 @@
+package config
+
+import (
+	"fmt"
+
+	"github.com/spf13/viper"
+)
+
+// Safely get the subtree from a viper config, returning an error if it could not
+// be obtained for any reason.
+func ViperSubConfig(conf *viper.Viper, configSubtreePath string) (subConfig *viper.Viper, err error) {
+	// Viper internally panics if `moduleName` contains an unallowed
+	// character (eg, a dash).
+	defer func() {
+		if r := recover(); r != nil {
+			err = fmt.Errorf("Viper panicked trying to read config subtree: %s",
+				configSubtreePath)
+		}
+	}()
+	if !conf.IsSet(configSubtreePath) {
+		return nil, fmt.Errorf("Failed to read config subtree: %s",
+			configSubtreePath)
+	}
+	subConfig = conf.Sub(configSubtreePath)
+	if subConfig == nil {
+		return nil, fmt.Errorf("Failed to read config subtree: %s",
+			configSubtreePath)
+	}
+	return subConfig, err
+}
diff --git a/consensus/consensus.go b/consensus/consensus.go
index f0795a5dde853f3ec6ee1aa046241aba5c25d250..2f4132d6e4e5aa89975b3100d913f86d4af72e7b 100644
--- a/consensus/consensus.go
+++ b/consensus/consensus.go
@@ -28,13 +28,13 @@ func LoadConsensusEngineInPipe(moduleConfig *config.ModuleConfig,
 	pipe definitions.Pipe) error {
 	switch moduleConfig.Name {
 	case "tendermint":
-		tendermint, err := tendermint.NewTendermint(moduleConfig,
-			pipe.GetApplication())
+		tmint, err := tendermint.NewTendermint(moduleConfig, pipe.GetApplication(),
+			pipe.Logger().With())
 		if err != nil {
 			return fmt.Errorf("Failed to load Tendermint node: %v", err)
 		}
 
-		err = pipe.SetConsensusEngine(tendermint)
+		err = pipe.SetConsensusEngine(tmint)
 		if err != nil {
 			return fmt.Errorf("Failed to load Tendermint in pipe as "+
 				"ConsensusEngine: %v", err)
@@ -42,7 +42,7 @@ func LoadConsensusEngineInPipe(moduleConfig *config.ModuleConfig,
 
 		// For Tendermint we have a coupled Blockchain and ConsensusEngine
 		// implementation, so load it at the same time as ConsensusEngine
-		err = pipe.SetBlockchain(tendermint)
+		err = pipe.SetBlockchain(tmint)
 		if err != nil {
 			return fmt.Errorf("Failed to load Tendermint in pipe as "+
 				"Blockchain: %v", err)
diff --git a/consensus/tendermint/config.go b/consensus/tendermint/config.go
index 1bbb74bd6aa35be73c28c7bb75adea040cfddfbb..094d7979e429ae16ec2bd3ff39e59e926895a77d 100644
--- a/consensus/tendermint/config.go
+++ b/consensus/tendermint/config.go
@@ -23,10 +23,10 @@ import (
 	"path"
 	"time"
 
-	viper "github.com/spf13/viper"
+	"github.com/spf13/viper"
 	tendermintConfig "github.com/tendermint/go-config"
 
-	config "github.com/eris-ltd/eris-db/config"
+	"github.com/eris-ltd/eris-db/config"
 )
 
 // NOTE [ben] Compiler check to ensure TendermintConfig successfully implements
@@ -147,7 +147,8 @@ func (tmintConfig *TendermintConfig) GetMapString(key string) map[string]string
 func (tmintConfig *TendermintConfig) GetConfig(key string) tendermintConfig.Config {
 	// TODO: [ben] log out a warning as this indicates a potentially breaking code
 	// change from Tendermints side
-	if !tmintConfig.subTree.IsSet(key) {
+	subTree, _ := config.ViperSubConfig(tmintConfig.subTree, key)
+	if subTree == nil {
 		return &TendermintConfig{
 			subTree: viper.New(),
 		}
diff --git a/consensus/tendermint/tendermint.go b/consensus/tendermint/tendermint.go
index 237fb4a0bd9b3da558fa73a7014b0f14546db661..5adc08e1a2619723123088901bea595f75a300eb 100644
--- a/consensus/tendermint/tendermint.go
+++ b/consensus/tendermint/tendermint.go
@@ -34,13 +34,14 @@ import (
 	tmsp_types "github.com/tendermint/tmsp/types"
 
 	edb_event "github.com/eris-ltd/eris-db/event"
-	log "github.com/eris-ltd/eris-logger"
 
 	config "github.com/eris-ltd/eris-db/config"
 	manager_types "github.com/eris-ltd/eris-db/manager/types"
 	// files  "github.com/eris-ltd/eris-db/files"
 	blockchain_types "github.com/eris-ltd/eris-db/blockchain/types"
 	consensus_types "github.com/eris-ltd/eris-db/consensus/types"
+	"github.com/eris-ltd/eris-db/logging"
+	"github.com/eris-ltd/eris-db/logging/loggers"
 	"github.com/eris-ltd/eris-db/txs"
 	"github.com/tendermint/go-wire"
 )
@@ -49,6 +50,7 @@ type Tendermint struct {
 	tmintNode   *node.Node
 	tmintConfig *TendermintConfig
 	chainId     string
+	logger      loggers.InfoTraceLogger
 }
 
 // Compiler checks to ensure Tendermint successfully implements
@@ -57,7 +59,8 @@ var _ consensus_types.ConsensusEngine = (*Tendermint)(nil)
 var _ blockchain_types.Blockchain = (*Tendermint)(nil)
 
 func NewTendermint(moduleConfig *config.ModuleConfig,
-	application manager_types.Application) (*Tendermint, error) {
+	application manager_types.Application,
+	logger loggers.InfoTraceLogger) (*Tendermint, error) {
 	// re-assert proper configuration for module
 	if moduleConfig.Version != GetTendermintVersion().GetMinorVersionString() {
 		return nil, fmt.Errorf("Version string %s did not match %s",
@@ -71,10 +74,10 @@ func NewTendermint(moduleConfig *config.ModuleConfig,
 	if !moduleConfig.Config.IsSet("configuration") {
 		return nil, fmt.Errorf("Failed to extract Tendermint configuration subtree.")
 	}
-	tendermintConfigViper := moduleConfig.Config.Sub("configuration")
+	tendermintConfigViper, err := config.ViperSubConfig(moduleConfig.Config, "configuration")
 	if tendermintConfigViper == nil {
 		return nil,
-			fmt.Errorf("Failed to extract Tendermint configuration subtree.")
+			fmt.Errorf("Failed to extract Tendermint configuration subtree: %s", err)
 	}
 	// wrap a copy of the viper config in a tendermint/go-config interface
 	tmintConfig := GetTendermintConfig(tendermintConfigViper)
@@ -92,18 +95,19 @@ func NewTendermint(moduleConfig *config.ModuleConfig,
 	tmintConfig.AssertTendermintConsistency(moduleConfig,
 		privateValidatorFilePath)
 	chainId := tmintConfig.GetString("chain_id")
-	log.WithFields(log.Fields{
-		"chainId":              chainId,
-		"genesisFile":          tmintConfig.GetString("genesis_file"),
-		"nodeLocalAddress":     tmintConfig.GetString("node_laddr"),
-		"moniker":              tmintConfig.GetString("moniker"),
-		"seeds":                tmintConfig.GetString("seeds"),
-		"fastSync":             tmintConfig.GetBool("fast_sync"),
-		"rpcLocalAddress":      tmintConfig.GetString("rpc_laddr"),
-		"databaseDirectory":    tmintConfig.GetString("db_dir"),
-		"privateValidatorFile": tmintConfig.GetString("priv_validator_file"),
-		"privValFile":          moduleConfig.Config.GetString("private_validator_file"),
-	}).Debug("Loaded Tendermint sub-configuration")
+
+	logging.TraceMsg(logger, "Loaded Tendermint sub-configuration",
+		"chainId", chainId,
+		"genesisFile", tmintConfig.GetString("genesis_file"),
+		"nodeLocalAddress", tmintConfig.GetString("node_laddr"),
+		"moniker", tmintConfig.GetString("moniker"),
+		"seeds", tmintConfig.GetString("seeds"),
+		"fastSync", tmintConfig.GetBool("fast_sync"),
+		"rpcLocalAddress", tmintConfig.GetString("rpc_laddr"),
+		"databaseDirectory", tmintConfig.GetString("db_dir"),
+		"privateValidatorFile", tmintConfig.GetString("priv_validator_file"),
+		"privValFile", moduleConfig.Config.GetString("private_validator_file"))
+
 	// TODO: [ben] do not "or Generate Validator keys", rather fail directly
 	// TODO: [ben] implement the signer for Private validator over eris-keys
 	// TODO: [ben] copy from rootDir to tendermint workingDir;
@@ -116,8 +120,8 @@ func NewTendermint(moduleConfig *config.ModuleConfig,
 	// not running the tendermint RPC as it could lead to unexpected behaviour,
 	// not least if we accidentally try to run it on the same address as our own
 	if tmintConfig.GetString("rpc_laddr") != "" {
-		log.Warnf("Force disabling Tendermint's native RPC, which had been set to "+
-			"run on '%s' in the Tendermint config.", tmintConfig.GetString("rpc_laddr"))
+		logging.InfoMsg(logger, "Force disabling Tendermint's native RPC",
+			"provided_rpc_laddr", tmintConfig.GetString("rpc_laddr"))
 		tmintConfig.Set("rpc_laddr", "")
 	}
 
@@ -136,26 +140,25 @@ func NewTendermint(moduleConfig *config.ModuleConfig,
 		newNode.Stop()
 		return nil, fmt.Errorf("Failed to start Tendermint consensus node: %v", err)
 	}
-	log.WithFields(log.Fields{
-		"nodeAddress":       tmintConfig.GetString("node_laddr"),
-		"transportProtocol": "tcp",
-		"upnp":              !tmintConfig.GetBool("skip_upnp"),
-		"moniker":           tmintConfig.GetString("moniker"),
-	}).Info("Tendermint consensus node started")
+	logging.InfoMsg(logger, "Tendermint consensus node started",
+		"nodeAddress", tmintConfig.GetString("node_laddr"),
+		"transportProtocol", "tcp",
+		"upnp", !tmintConfig.GetBool("skip_upnp"),
+		"moniker", tmintConfig.GetString("moniker"))
 
 	// If seedNode is provided by config, dial out.
 	if tmintConfig.GetString("seeds") != "" {
 		seeds := strings.Split(tmintConfig.GetString("seeds"), ",")
 		newNode.DialSeeds(seeds)
-		log.WithFields(log.Fields{
-			"seeds": seeds,
-		}).Debug("Tendermint node called seeds")
+		logging.TraceMsg(logger, "Tendermint node called seeds",
+			"seeds", seeds)
 	}
 
 	return &Tendermint{
 		tmintNode:   newNode,
 		tmintConfig: tmintConfig,
 		chainId:     chainId,
+		logger:      logger,
 	}, nil
 }
 
@@ -228,7 +231,7 @@ func (tendermint *Tendermint) PublicValidatorKey() crypto.PubKey {
 }
 
 func (tendermint *Tendermint) Events() edb_event.EventEmitter {
-	return edb_event.NewEvents(tendermint.tmintNode.EventSwitch())
+	return edb_event.NewEvents(tendermint.tmintNode.EventSwitch(), tendermint.logger)
 }
 
 func (tendermint *Tendermint) BroadcastTransaction(transaction []byte,
diff --git a/core/config.go b/core/config.go
index a2a82dbd1e07e649962f8ee37e2f61b48ceb25aa..5a10047e276bd1db6a06d717e6f141242e340026 100644
--- a/core/config.go
+++ b/core/config.go
@@ -24,13 +24,14 @@ import (
 	"os"
 	"path"
 
-	config "github.com/eris-ltd/eris-db/config"
-	consensus "github.com/eris-ltd/eris-db/consensus"
-	definitions "github.com/eris-ltd/eris-db/definitions"
-	manager "github.com/eris-ltd/eris-db/manager"
-	server "github.com/eris-ltd/eris-db/server"
-	util "github.com/eris-ltd/eris-db/util"
-	version "github.com/eris-ltd/eris-db/version"
+	"github.com/eris-ltd/eris-db/config"
+	"github.com/eris-ltd/eris-db/consensus"
+	"github.com/eris-ltd/eris-db/definitions"
+	"github.com/eris-ltd/eris-db/logging"
+	"github.com/eris-ltd/eris-db/manager"
+	"github.com/eris-ltd/eris-db/server"
+	"github.com/eris-ltd/eris-db/util"
+	"github.com/eris-ltd/eris-db/version"
 	"github.com/spf13/viper"
 )
 
@@ -75,17 +76,14 @@ func LoadModuleConfig(conf *viper.Viper, rootWorkDir, rootDataDir,
 			fmt.Errorf("Failed to create module data directory %s.", dataDir)
 	}
 	// load configuration subtree for module
-	// TODO: [ben] Viper internally panics if `moduleName` contains an unallowed
-	// character (eg, a dash).  Either this needs to be wrapped in a go-routine
-	// and recovered from or a PR to viper is needed to address this bug.
 	if !conf.IsSet(moduleName) {
 		return nil, fmt.Errorf("Failed to read configuration section for %s",
 			moduleName)
 	}
-	subConfig := conf.Sub(moduleName)
+	subConfig, err := config.ViperSubConfig(conf, moduleName)
 	if subConfig == nil {
-		return nil,
-			fmt.Errorf("Failed to read configuration section for %s.", moduleName)
+		return nil, fmt.Errorf("Failed to read configuration section for %s: %s",
+			moduleName, err)
 	}
 
 	return &config.ModuleConfig{
@@ -104,19 +102,29 @@ func LoadModuleConfig(conf *viper.Viper, rootWorkDir, rootDataDir,
 // LoadServerModuleConfig wraps specifically for the servers run by core
 func LoadServerConfig(do *definitions.Do) (*server.ServerConfig, error) {
 	// load configuration subtree for servers
-	if !do.Config.IsSet("servers") {
-		return nil, fmt.Errorf("Failed to read configuration section for servers")
-	}
-	subConfig := do.Config.Sub("servers")
-	if subConfig == nil {
-		return nil,
-			fmt.Errorf("Failed to read configuration section for servers")
+	subConfig, err := config.ViperSubConfig(do.Config, "servers")
+	if err != nil {
+		return nil, err
 	}
 	serverConfig, err := server.ReadServerConfig(subConfig)
+	if err != nil {
+		return nil, err
+	}
 	serverConfig.ChainId = do.ChainId
 	return serverConfig, err
 }
 
+func LoadLoggingConfigFromDo(do *definitions.Do) (*logging.LoggingConfig, error) {
+	//subConfig, err := SubConfig(conf, "logging")
+	loggingConfig := &logging.LoggingConfig{}
+	return loggingConfig, nil
+}
+
+func LoadLoggingConfigFromClientDo(do *definitions.ClientDo) (*logging.LoggingConfig, error) {
+	loggingConfig := &logging.LoggingConfig{}
+	return loggingConfig, nil
+}
+
 //------------------------------------------------------------------------------
 // Helper functions
 
diff --git a/core/core.go b/core/core.go
index 03f56b56a47927b09d8b2bdc9e5a2302da1fcd84..6c58d1a3312eba6c17cf06c121be1bbb201d48eb 100644
--- a/core/core.go
+++ b/core/core.go
@@ -22,18 +22,19 @@ import (
 	// TODO: [ben] swap out go-events with eris-db/event (currently unused)
 	events "github.com/tendermint/go-events"
 
-	log "github.com/eris-ltd/eris-logger"
-
-	config "github.com/eris-ltd/eris-db/config"
-	consensus "github.com/eris-ltd/eris-db/consensus"
-	definitions "github.com/eris-ltd/eris-db/definitions"
-	event "github.com/eris-ltd/eris-db/event"
-	manager "github.com/eris-ltd/eris-db/manager"
+	"github.com/eris-ltd/eris-db/config"
+	"github.com/eris-ltd/eris-db/consensus"
+	"github.com/eris-ltd/eris-db/definitions"
+	"github.com/eris-ltd/eris-db/event"
+	"github.com/eris-ltd/eris-db/manager"
 	// rpc_v0 is carried over from Eris-DBv0.11 and before on port 1337
 	rpc_v0 "github.com/eris-ltd/eris-db/rpc/v0"
 	// rpc_tendermint is carried over from Eris-DBv0.11 and before on port 46657
+
+	"github.com/eris-ltd/eris-db/logging"
+	"github.com/eris-ltd/eris-db/logging/loggers"
 	rpc_tendermint "github.com/eris-ltd/eris-db/rpc/tendermint/core"
-	server "github.com/eris-ltd/eris-db/server"
+	"github.com/eris-ltd/eris-db/server"
 )
 
 // Core is the high-level structure
@@ -44,33 +45,30 @@ type Core struct {
 	tendermintPipe definitions.TendermintPipe
 }
 
-func NewCore(chainId string, consensusConfig *config.ModuleConfig,
-	managerConfig *config.ModuleConfig) (*Core, error) {
+func NewCore(chainId string,
+	consensusConfig *config.ModuleConfig,
+	managerConfig *config.ModuleConfig,
+	logger loggers.InfoTraceLogger) (*Core, error) {
 	// start new event switch, TODO: [ben] replace with eris-db/event
 	evsw := events.NewEventSwitch()
 	evsw.Start()
+	logger = logging.WithScope(logger, "Core")
 
 	// start a new application pipe that will load an application manager
-	pipe, err := manager.NewApplicationPipe(managerConfig, evsw,
+	pipe, err := manager.NewApplicationPipe(managerConfig, evsw, logger,
 		consensusConfig.Version)
 	if err != nil {
 		return nil, fmt.Errorf("Failed to load application pipe: %v", err)
 	}
-	log.Debug("Loaded pipe with application manager")
+	logging.TraceMsg(logger, "Loaded pipe with application manager")
 	// pass the consensus engine into the pipe
 	if e := consensus.LoadConsensusEngineInPipe(consensusConfig, pipe); e != nil {
 		return nil, fmt.Errorf("Failed to load consensus engine in pipe: %v", e)
 	}
 	tendermintPipe, err := pipe.GetTendermintPipe()
 	if err != nil {
-		log.Warn(fmt.Sprintf("Tendermint gateway not supported by %s",
-			managerConfig.Version))
-		return &Core{
-			chainId:        chainId,
-			evsw:           evsw,
-			pipe:           pipe,
-			tendermintPipe: nil,
-		}, nil
+		logging.TraceMsg(logger, "Tendermint gateway not supported by manager",
+			"manager-version", managerConfig.Version)
 	}
 	return &Core{
 		chainId:        chainId,
diff --git a/definitions/pipe.go b/definitions/pipe.go
index f15c60df5cbe4aceda0a9421ed824c35bb0b3ea3..a32a4cdf24cd43e63b95f0defcfb95ecbc311057 100644
--- a/definitions/pipe.go
+++ b/definitions/pipe.go
@@ -31,6 +31,7 @@ import (
 	core_types "github.com/eris-ltd/eris-db/core/types"
 	types "github.com/eris-ltd/eris-db/core/types"
 	event "github.com/eris-ltd/eris-db/event"
+	"github.com/eris-ltd/eris-db/logging/loggers"
 	manager_types "github.com/eris-ltd/eris-db/manager/types"
 	"github.com/eris-ltd/eris-db/txs"
 )
@@ -43,6 +44,7 @@ type Pipe interface {
 	Transactor() Transactor
 	// Hash of Genesis state
 	GenesisHash() []byte
+	Logger() loggers.InfoTraceLogger
 	// NOTE: [ben] added to Pipe interface on 0.12 refactor
 	GetApplication() manager_types.Application
 	SetConsensusEngine(consensusEngine consensus_types.ConsensusEngine) error
diff --git a/docs/generator.go b/docs/generator.go
index fb430fc067be744340d07c486593563e994a75fd..c3aa23876050cdac4db9d63feb5fa8b82e0655e8 100644
--- a/docs/generator.go
+++ b/docs/generator.go
@@ -7,10 +7,11 @@ import (
 	"strings"
 	"text/template"
 
-	"github.com/eris-ltd/common/go/docs"
 	commands "github.com/eris-ltd/eris-db/cmd"
+	docs "github.com/eris-ltd/eris-db/docs/generator"
 
 	clientCommands "github.com/eris-ltd/eris-db/client/cmd"
+	"github.com/eris-ltd/eris-db/definitions"
 	"github.com/eris-ltd/eris-db/version"
 	"github.com/spf13/cobra"
 )
@@ -115,9 +116,9 @@ func AddClientToDB(dbCmd, clientCmd *cobra.Command) error {
 func main() {
 	// Repository maintainers should populate the top level command object.
 	erisDbCommand := commands.ErisDbCmd
-	commands.InitErisDbCli()
-	commands.AddCommands()
-	commands.AddGlobalFlags()
+	do := definitions.NewDo()
+	commands.AddGlobalFlags(do)
+	commands.AddCommands(do)
 
 	erisClientCommand := clientCommands.ErisClientCmd
 	clientCommands.InitErisClientInit()
diff --git a/docs/generator/generator.go b/docs/generator/generator.go
new file mode 100644
index 0000000000000000000000000000000000000000..fee2e3d5cfbb524b89b2b61b95fba9b6cd813d43
--- /dev/null
+++ b/docs/generator/generator.go
@@ -0,0 +1,333 @@
+package generator
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strconv"
+	"strings"
+	"text/template"
+)
+
+const FrontMatter = `---
+
+layout: single
+type: docs
+title: "Documentation | {{ .Description }} | {{ $name }}"
+
+---`
+
+type Entry struct {
+	Title          string
+	Template       *template.Template
+	Specifications []*Entry
+	Examples       []*Entry
+	Description    string
+	FileName       string
+	CmdEntryPoint  string
+	URL            string
+	BaseURL        string
+}
+
+func GenerateFileName(dir, s string) string {
+	return (dir + strings.Replace(strings.ToLower(s), " ", "_", -1) + ".md")
+}
+
+func GenerateTitleFromFileName(file string) string {
+	file = strings.Replace(file, "_", " ", -1)
+	file = strings.Replace(file, "-", " ", -1)
+	return strings.Title(strings.Replace(file, ".md", "", 1))
+}
+
+func GenerateFileNameFromGlob(dir, s string) string {
+	return (dir + strings.Replace(filepath.Base(s), " ", "_", -1))
+}
+
+func GenerateURLFromFileName(s string) string {
+	s = strings.Replace(s, "./", "/", 1)
+	return strings.Replace(s, ".md", "/", -1)
+}
+
+func GenerateCommandsTemplate() (*template.Template, error) {
+	handle_link := func(s string) string {
+		return (strings.Replace(s, ".md", "/", -1))
+	}
+
+	handle_file := func(s1, s2 string) string {
+		return strings.Replace((s1 + " " + s2 + ".md"), " ", "_", -1)
+	}
+
+	funcMap := template.FuncMap{
+		"title":       strings.Title,
+		"replace":     strings.Replace,
+		"chomp":       strings.TrimSpace,
+		"handle_file": handle_file,
+		"handle_link": handle_link,
+	}
+
+	var templateText = `{{- $name := .Command.CommandPath -}}` + FrontMatter + `
+
+# {{ $name }}
+
+{{ title .Command.Short }}
+
+{{ if .Command.Runnable }}## Usage
+
+` + "```bash\n{{ .Command.UseLine }}\n```" + `{{ end }}
+
+{{ if ne .Command.Long  "" }}## Synopsis
+
+{{ .Command.Long }}
+{{ end }}
+{{ $flags := .Command.NonInheritedFlags }}
+{{ if $flags.HasFlags }}## Options
+
+` + "```bash\n  {{ $flags.FlagUsages | chomp }}\n```" + `{{ end }}
+{{ $global_flags := .Command.InheritedFlags }}
+{{ if $global_flags.HasFlags }}## Options inherited from parent commands
+
+` + "```bash\n  {{ $global_flags.FlagUsages | chomp }}\n```" + `{{ end }}
+
+{{ if .Command.HasSubCommands }}# Subcommands
+{{ range .Command.Commands }}
+{{ if ne .Deprecated "" }}
+* [{{ $name }} {{ .Name }}]({{ .BaseURL }}{{ handle_file $name .Name | handle_link }}) - {{ .Short }}
+{{ end }}
+{{ end }}
+{{ end }}
+
+{{ if .Command.HasParent }}{{ $parent := .Command.Parent }}## See Also
+* [{{ $parent.CommandPath }}]({{ .BaseURL }}{{ handle_file $parent.CommandPath "" | handle_link }}) - {{ $parent.Short }}
+{{ end }}
+
+{{ if ne .Command.Example "" }}# Quick Tips
+
+` + "```bash\n{{ .Command.Example }}\n```" + `{{ end }}
+
+{{ if ne (len .Entry.Examples) 0 }}# Examples
+{{ range .Entry.Examples }}
+* [{{ title .Title }}]({{ .URL }})
+{{- end }}
+{{ end }}
+
+{{ if ne (len .Entry.Specifications) 0 }}# Specifications
+{{ range .Entry.Specifications }}
+* [{{ title .Title }}]({{ .URL }})
+{{- end }}
+{{ end }}
+`
+
+	return template.New("docGenerator").Funcs(funcMap).Parse(templateText)
+}
+
+func GenerateEntries(dir, render_dir, description string) ([]*Entry, error) {
+	var entries []*Entry
+
+	if _, err := os.Stat(render_dir); os.IsNotExist(err) {
+		err = os.MkdirAll(render_dir, 0755)
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	files := CollectEntries(dir)
+
+	for _, file := range files {
+		this_entry, err := GenerateEntry(file, dir, render_dir, description)
+		if err != nil {
+			return nil, err
+		} else {
+			entries = append(entries, this_entry)
+		}
+	}
+
+	return entries, nil
+}
+
+func CollectEntries(dir string) []string {
+	var newFiles []string
+
+	files, err := filepath.Glob(dir + "/*")
+	if err != nil {
+		panic(err)
+	}
+
+	for _, file := range files {
+		f_info, err := os.Stat(file)
+
+		if err != nil {
+			panic(err)
+		}
+
+		if f_info.IsDir() {
+			newFiles = append(newFiles, CollectEntries(file)...)
+		} else {
+			if filepath.Ext(file) == ".md" {
+				newFiles = append(newFiles, file)
+			}
+		}
+	}
+
+	return newFiles
+}
+
+func GenerateEntry(file, dir, render_dir, description string) (*Entry, error) {
+	var err error
+
+	this_entry := &Entry{
+		FileName:    GenerateFileNameFromGlob(render_dir, file),
+		Title:       GenerateTitleFromFileName(filepath.Base(file)),
+		Description: description,
+	}
+
+	this_entry.URL = GenerateURLFromFileName(this_entry.FileName)
+
+	txt, err := ioutil.ReadFile(file)
+	if err != nil {
+		return nil, err
+	}
+
+	// Get template from docs generator
+	this_entry.Template, err = GenerateEntriesTemplate(txt)
+	if err != nil {
+		return nil, err
+	}
+
+	return this_entry, nil
+}
+
+func GenerateEntriesTemplate(txt []byte) (*template.Template, error) {
+	handle_link := func(s string) string {
+		return (strings.Replace(s, ".md", "/", -1))
+	}
+
+	handle_file := func(s1 string) string {
+		return strings.Replace((s1 + ".md"), " ", "_", -1)
+	}
+
+	insert_definition := func(file string, struc string) string {
+		txt, err := ioutil.ReadFile(filepath.Join("definitions", file))
+		if err != nil {
+			panic(err)
+		}
+		finder := regexp.MustCompile(fmt.Sprintf(`(?ms:^type %s struct {.*?^})`, struc))
+		return ("```go\n" + string(finder.Find(txt)) + "\n```")
+	}
+
+	insert_bash_lines := func(file string, linesToRead string) string {
+		var lines []byte
+		var line []byte
+		var start int
+		var stop int
+
+		fileInfo, err := os.Open(filepath.Join("docs", "tests", file))
+		if err != nil {
+			panic(err)
+		}
+		defer fileInfo.Close()
+
+		start, err = strconv.Atoi(strings.Split(linesToRead, "-")[0])
+		if strings.Contains(linesToRead, "-") {
+			stop, err = strconv.Atoi(strings.Split(linesToRead, "-")[1])
+		} else {
+			stop = start
+		}
+		if err != nil {
+			panic(err)
+		}
+
+		r := bufio.NewReader(fileInfo)
+		for i := 1; ; i++ {
+			line, err = r.ReadBytes('\n')
+			if err != nil {
+				break
+			}
+			if i >= start && i <= stop {
+				lines = append(lines, line...)
+			}
+		}
+		if err != io.EOF {
+			panic(err)
+		}
+
+		return ("```bash\n" + string(lines) + "```")
+	}
+
+	insert_file := func(file string) string {
+		file = filepath.Join("docs", "tests", file)
+		ext := filepath.Ext(file)
+		switch ext {
+		case ".sol":
+			ext = ".javascript"
+		case ".yml":
+			ext = ".yaml"
+		}
+
+		ext = strings.Replace(ext, ".", "", 1)
+
+		txtB, err := ioutil.ReadFile(file)
+		if err != nil {
+			panic(err)
+		}
+
+		txt := string(txtB)
+		if !strings.HasSuffix(txt, "\n") {
+			txt = txt + "\n"
+		}
+
+		return ("```" + ext + "\n" + txt + "```") // TODO: add auto-curl text
+	}
+
+	funcMap := template.FuncMap{
+		"title":             strings.Title,
+		"replace":           strings.Replace,
+		"chomp":             strings.TrimSpace,
+		"handle_file":       handle_file,
+		"handle_link":       handle_link,
+		"insert_definition": insert_definition,
+		"insert_bash_lines": insert_bash_lines,
+		"insert_file":       insert_file,
+	}
+
+	var templateText = `{{- $name := .Title -}}` + FrontMatter + `
+
+` + string(txt) + `
+
+## Commands
+
+* [{{ .CmdEntryPoint }}]({{ .BaseURL }}{{ handle_file .CmdEntryPoint | handle_link }})
+
+{{ if ne (len .Examples) 0 }}# Examples
+{{ range .Examples }}
+* [{{ title .Title }}]({{ .URL }})
+{{- end }}
+{{ end }}
+
+{{ if ne (len .Specifications) 0 }}# Specifications
+{{ range .Specifications }}
+* [{{ title .Title }}]({{ .URL }})
+{{- end }}
+{{ end }}
+`
+
+	return template.New("entryGenerator").Funcs(funcMap).Parse(templateText)
+}
+
+func RenderEntry(this_entry *Entry) error {
+	out_file, err := os.Create(this_entry.FileName)
+	if err != nil {
+		return err
+	}
+	defer out_file.Close()
+
+	err = this_entry.Template.Execute(out_file, this_entry)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/event/event_cache.go b/event/event_cache.go
index 08db30116ea20225cb27875fb79fd103b0165070..7b6bc356cdf528a390730daa1ac0cabcb2d5749e 100644
--- a/event/event_cache.go
+++ b/event/event_cache.go
@@ -2,9 +2,10 @@ package event
 
 import (
 	"fmt"
-	"github.com/eris-ltd/eris-db/txs"
 	"sync"
 	"time"
+
+	"github.com/eris-ltd/eris-db/txs"
 )
 
 var (
diff --git a/event/events.go b/event/events.go
index d5c37d8e724259036a661cc4758d9030c322aba1..ec790a9ed8b1d120a46ecf1642bec840756fcbdd 100644
--- a/event/events.go
+++ b/event/events.go
@@ -23,8 +23,9 @@ import (
 
 	"fmt"
 
+	"github.com/eris-ltd/eris-db/logging"
+	"github.com/eris-ltd/eris-db/logging/loggers"
 	"github.com/eris-ltd/eris-db/txs"
-	log "github.com/eris-ltd/eris-logger"
 	go_events "github.com/tendermint/go-events"
 	tm_types "github.com/tendermint/tendermint/types"
 )
@@ -43,8 +44,8 @@ type EventEmitter interface {
 	Unsubscribe(subId string) error
 }
 
-func NewEvents(eventSwitch *go_events.EventSwitch) *events {
-	return &events{eventSwitch}
+func NewEvents(eventSwitch *go_events.EventSwitch, logger loggers.InfoTraceLogger) *events {
+	return &events{eventSwitch: eventSwitch, logger: logging.WithScope(logger, "Events")}
 }
 
 // Provides an EventEmitter that wraps many underlying EventEmitters as a
@@ -57,27 +58,28 @@ func Multiplex(events ...EventEmitter) *multiplexedEvents {
 // The events struct has methods for working with events.
 type events struct {
 	eventSwitch *go_events.EventSwitch
+	logger      loggers.InfoTraceLogger
 }
 
 // Subscribe to an event.
-func (this *events) Subscribe(subId, event string,
+func (evts *events) Subscribe(subId, event string,
 	callback func(txs.EventData)) error {
 	cb := func(evt go_events.EventData) {
 		eventData, err := mapToOurEventData(evt)
 		if err != nil {
-			log.WithError(err).
-				WithFields(log.Fields{"event": event}).
-				Error("Failed to map go-events EventData to our EventData")
+			logging.InfoMsg(evts.logger, "Failed to map go-events EventData to our EventData",
+				"error", err,
+				"event", event)
 		}
 		callback(eventData)
 	}
-	this.eventSwitch.AddListenerForEvent(subId, event, cb)
+	evts.eventSwitch.AddListenerForEvent(subId, event, cb)
 	return nil
 }
 
 // Un-subscribe from an event.
-func (this *events) Unsubscribe(subId string) error {
-	this.eventSwitch.RemoveListener(subId)
+func (evts *events) Unsubscribe(subId string) error {
+	evts.eventSwitch.RemoveListener(subId)
 	return nil
 }
 
diff --git a/genesis/gen_test.go b/genesis/gen_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..30d5f9c511df7efbd372c6a44814b1c0d64070c8
--- /dev/null
+++ b/genesis/gen_test.go
@@ -0,0 +1,162 @@
+package genesis
+
+import (
+	"bytes"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
+)
+
+// set the chain ID
+var chainID string = "genesis-file-maker-test"
+
+var genesisFileExpected string = `{
+	"genesis_time": "0001-01-01T00:00:00.000Z",
+	"chain_id": "genesis-file-maker-test",
+	"params": null,
+	"accounts": [
+		{
+			"address": "74417C1BEFB3938B71B22B202050A4C6591FFCF6",
+			"amount": 9999999999,
+			"name": "genesis-file-maker-test_developer_000",
+			"permissions": {
+				"base": {
+					"perms": 14430,
+					"set": 16383
+				},
+				"roles": []
+			}
+		},
+		{
+			"address": "0C9DAEA4046491A661FCE0B41B0CAA2AD3415268",
+			"amount": 99999999999999,
+			"name": "genesis-file-maker-test_full_000",
+			"permissions": {
+				"base": {
+					"perms": 16383,
+					"set": 16383
+				},
+				"roles": []
+			}
+		},
+		{
+			"address": "E1BD50A1B90A15861F5CF0F182D291F556B21A86",
+			"amount": 9999999999,
+			"name": "genesis-file-maker-test_participant_000",
+			"permissions": {
+				"base": {
+					"perms": 2118,
+					"set": 16383
+				},
+				"roles": []
+			}
+		},
+		{
+			"address": "A6C8E2DE652DB8ADB4036293DC21F8FE389D77C2",
+			"amount": 9999999999,
+			"name": "genesis-file-maker-test_root_000",
+			"permissions": {
+				"base": {
+					"perms": 16383,
+					"set": 16383
+				},
+				"roles": []
+			}
+		},
+		{
+			"address": "E96CB7910001320B6F1E2266A8431D5E98FF0183",
+			"amount": 9999999999,
+			"name": "genesis-file-maker-test_validator_000",
+			"permissions": {
+				"base": {
+					"perms": 32,
+					"set": 16383
+				},
+				"roles": []
+			}
+		}
+	],
+	"validators": [
+		{
+			"pub_key": [
+				1,
+				"238E1A77CC7CDCD13F4D77841F1FE4A46A77DB691EC89718CD0D4CB3409F61D2"
+			],
+			"amount": 9999999999,
+			"name": "genesis-file-maker-test_full_000",
+			"unbond_to": [
+				{
+					"address": "0C9DAEA4046491A661FCE0B41B0CAA2AD3415268",
+					"amount": 9999999999
+				}
+			]
+		},
+		{
+			"pub_key": [
+				1,
+				"7F53D78C526F96C87ACBD0D2B9DB2E9FC176981623D26B1DB1CF59748EE9F4CF"
+			],
+			"amount": 9999999998,
+			"name": "genesis-file-maker-test_validator_000",
+			"unbond_to": [
+				{
+					"address": "E96CB7910001320B6F1E2266A8431D5E98FF0183",
+					"amount": 9999999998
+				}
+			]
+		}
+	]
+}`
+
+var accountsCSV string = `F0BD5CE45D306D61C9AB73CE5268C2B59D52CAF7127EF0E3B65523302254350A,9999999999,genesis-file-maker-test_developer_000,14430,16383
+238E1A77CC7CDCD13F4D77841F1FE4A46A77DB691EC89718CD0D4CB3409F61D2,99999999999999,genesis-file-maker-test_full_000,16383,16383
+E37A655E560D53721C9BB06BA742398323504DFE2EB2C67E71F8D16E71E0471B,9999999999,genesis-file-maker-test_participant_000,2118,16383
+EC0E38CC8308EC9E720EE839242A7BC5C781D1F852E962FAC5A8E0599CE5B224,9999999999,genesis-file-maker-test_root_000,16383,16383
+7F53D78C526F96C87ACBD0D2B9DB2E9FC176981623D26B1DB1CF59748EE9F4CF,9999999999,genesis-file-maker-test_validator_000,32,16383`
+
+var validatorsCSV string = `238E1A77CC7CDCD13F4D77841F1FE4A46A77DB691EC89718CD0D4CB3409F61D2,9999999999,genesis-file-maker-test_full_000,16383,16383
+7F53D78C526F96C87ACBD0D2B9DB2E9FC176981623D26B1DB1CF59748EE9F4CF,9999999998,genesis-file-maker-test_validator_000,32,16383`
+
+func TestKnownCSV(t *testing.T) {
+	// make temp dir
+	dir, err := ioutil.TempDir(os.TempDir(), "genesis-file-maker-test")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	defer func() {
+		//cleanup
+		os.RemoveAll(dir)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+	}()
+
+	// set the filepaths to be written to
+	accountsCSVpath := filepath.Join(dir, "accounts.csv")
+	validatorsCSVpath := filepath.Join(dir, "validators.csv")
+
+	// write the accounts.csv
+	if err := ioutil.WriteFile(accountsCSVpath, []byte(accountsCSV), 0600); err != nil {
+		t.Fatal(err)
+	}
+
+	// write the validators.csv
+	if err := ioutil.WriteFile(validatorsCSVpath, []byte(validatorsCSV), 0600); err != nil {
+		t.Fatal(err)
+	}
+
+	// create the genesis file
+	genesisFileWritten, err := GenerateKnown(chainID, accountsCSVpath, validatorsCSVpath)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// compare
+	if !bytes.Equal([]byte(genesisFileExpected), []byte(genesisFileWritten)) {
+		t.Fatalf("Bad genesis file: got (%s), expected (%s)", genesisFileWritten, genesisFileExpected)
+	}
+
+}
diff --git a/genesis/make_genesis_file.go b/genesis/make_genesis_file.go
new file mode 100644
index 0000000000000000000000000000000000000000..c4324366ccf4f1c6195684ce32c59592ddc37712
--- /dev/null
+++ b/genesis/make_genesis_file.go
@@ -0,0 +1,199 @@
+package genesis
+
+import (
+	"bytes"
+	"encoding/csv"
+	"encoding/hex"
+	"encoding/json"
+	"fmt"
+	"os"
+	"strconv"
+
+	ptypes "github.com/eris-ltd/eris-db/permission/types"
+	"github.com/eris-ltd/eris-db/util"
+
+	"github.com/tendermint/go-crypto"
+	wire "github.com/tendermint/go-wire"
+)
+
+//------------------------------------------------------------------------------------
+// core functions
+
+func GenerateKnown(chainID, accountsPathCSV, validatorsPathCSV string) (string, error) {
+	var genDoc *GenesisDoc
+
+	// TODO [eb] eliminate reading priv_val ... [zr] where?
+	if accountsPathCSV == "" || validatorsPathCSV == "" {
+		return "", fmt.Errorf("both accounts.csv and validators.csv is required")
+	}
+
+	pubkeys, amts, names, perms, setbits, err := parseCsv(validatorsPathCSV)
+	if err != nil {
+		return "", err
+	}
+
+	pubkeysA, amtsA, namesA, permsA, setbitsA, err := parseCsv(accountsPathCSV)
+	if err != nil {
+		return "", err
+	}
+
+	genDoc = newGenDoc(chainID, len(pubkeys), len(pubkeysA))
+	for i, pk := range pubkeys {
+		genDocAddValidator(genDoc, pk, amts[i], names[i], perms[i], setbits[i], i)
+	}
+	for i, pk := range pubkeysA {
+		genDocAddAccount(genDoc, pk, amtsA[i], namesA[i], permsA[i], setbitsA[i], i)
+	}
+
+	buf, buf2, n := new(bytes.Buffer), new(bytes.Buffer), new(int)
+	wire.WriteJSON(genDoc, buf, n, &err)
+	if err != nil {
+		return "", err
+	}
+	if err := json.Indent(buf2, buf.Bytes(), "", "\t"); err != nil {
+		return "", err
+	}
+
+	return buf2.String(), nil
+}
+
+//-----------------------------------------------------------------------------
+// gendoc convenience functions
+
+func newGenDoc(chainID string, nVal, nAcc int) *GenesisDoc {
+	genDoc := GenesisDoc{
+		ChainID: chainID,
+		// GenesisTime: time.Now(),
+	}
+	genDoc.Accounts = make([]GenesisAccount, nAcc)
+	genDoc.Validators = make([]GenesisValidator, nVal)
+	return &genDoc
+}
+
+func genDocAddAccount(genDoc *GenesisDoc, pubKey crypto.PubKeyEd25519, amt int64, name string, perm, setbit ptypes.PermFlag, index int) {
+	addr := pubKey.Address()
+	acc := GenesisAccount{
+		Address: addr,
+		Amount:  amt,
+		Name:    name,
+		Permissions: &ptypes.AccountPermissions{
+			Base: ptypes.BasePermissions{
+				Perms:  perm,
+				SetBit: setbit,
+			},
+		},
+	}
+	if index < 0 {
+		genDoc.Accounts = append(genDoc.Accounts, acc)
+	} else {
+		genDoc.Accounts[index] = acc
+	}
+}
+
+func genDocAddValidator(genDoc *GenesisDoc, pubKey crypto.PubKeyEd25519, amt int64, name string, perm, setbit ptypes.PermFlag, index int) {
+	addr := pubKey.Address()
+	genDoc.Validators[index] = GenesisValidator{
+		PubKey: pubKey,
+		Amount: amt,
+		Name:   name,
+		UnbondTo: []BasicAccount{
+			{
+				Address: addr,
+				Amount:  amt,
+			},
+		},
+	}
+	// [zr] why no index < 0 like in genDocAddAccount?
+}
+
+//-----------------------------------------------------------------------------
+// util functions
+
+// convert hex strings to ed25519 pubkeys
+func pubKeyStringsToPubKeys(pubkeys []string) ([]crypto.PubKeyEd25519, error) {
+	pubKeys := make([]crypto.PubKeyEd25519, len(pubkeys))
+	for i, k := range pubkeys {
+		pubBytes, err := hex.DecodeString(k)
+		if err != nil {
+			return pubKeys, err
+		}
+		copy(pubKeys[i][:], pubBytes)
+	}
+	return pubKeys, nil
+}
+
+// empty is over written
+func ifExistsElse(list []string, index int, defaultValue string) string {
+	if len(list) > index {
+		if list[index] != "" {
+			return list[index]
+		}
+	}
+	return defaultValue
+}
+
+// takes a csv in the following format: pubkey, starting balance, name, permissions, setbit
+func parseCsv(filePath string) (pubKeys []crypto.PubKeyEd25519, amts []int64, names []string, perms, setbits []ptypes.PermFlag, err error) {
+
+	csvFile, err := os.Open(filePath)
+	if err != nil {
+		util.Fatalf("Couldn't open file: %s: %v", filePath, err)
+	}
+	defer csvFile.Close()
+
+	r := csv.NewReader(csvFile)
+	//r.FieldsPerRecord = # of records expected
+	params, err := r.ReadAll()
+	if err != nil {
+		util.Fatalf("Couldn't read file: %v", err)
+
+	}
+
+	pubkeys := make([]string, len(params))
+	amtS := make([]string, len(params))
+	names = make([]string, len(params))
+	permsS := make([]string, len(params))
+	setbitS := make([]string, len(params))
+	for i, each := range params {
+		pubkeys[i] = each[0]
+		amtS[i] = ifExistsElse(each, 1, "1000")
+		names[i] = ifExistsElse(each, 2, "")
+		permsS[i] = ifExistsElse(each, 3, fmt.Sprintf("%d", ptypes.DefaultPermFlags))
+		setbitS[i] = ifExistsElse(each, 4, permsS[i])
+	}
+
+	//TODO convert int to uint64, see issue #25
+	perms = make([]ptypes.PermFlag, len(permsS))
+	for i, perm := range permsS {
+		pflag, err := strconv.Atoi(perm)
+		if err != nil {
+			util.Fatalf("Permissions (%v) must be an integer", perm)
+		}
+		perms[i] = ptypes.PermFlag(pflag)
+	}
+	setbits = make([]ptypes.PermFlag, len(setbitS))
+	for i, setbit := range setbitS {
+		setbitsFlag, err := strconv.Atoi(setbit)
+		if err != nil {
+			util.Fatalf("SetBits (%v) must be an integer", setbit)
+		}
+		setbits[i] = ptypes.PermFlag(setbitsFlag)
+	}
+
+	// convert amts to ints
+	amts = make([]int64, len(amtS))
+	for i, a := range amtS {
+		if amts[i], err = strconv.ParseInt(a, 10, 64); err != nil {
+			err = fmt.Errorf("Invalid amount: %v", err)
+			return
+		}
+	}
+
+	// convert pubkey hex strings to struct
+	pubKeys, err = pubKeyStringsToPubKeys(pubkeys)
+	if err != nil {
+		return
+	}
+
+	return pubKeys, amts, names, perms, setbits, nil
+}
diff --git a/genesis/types.go b/genesis/types.go
new file mode 100644
index 0000000000000000000000000000000000000000..d03e6197645b67ab9f306a9d71106a2ab0d7b338
--- /dev/null
+++ b/genesis/types.go
@@ -0,0 +1,67 @@
+package genesis
+
+import (
+	"fmt"
+	"os"
+	"time"
+
+	ptypes "github.com/eris-ltd/eris-db/permission/types"
+	"github.com/tendermint/go-crypto"
+	"github.com/tendermint/go-wire"
+)
+
+//------------------------------------------------------------
+// we store the GenesisDoc in the db under this key
+
+var GenDocKey = []byte("GenDocKey")
+
+//------------------------------------------------------------
+// core types for a genesis definition
+
+type BasicAccount struct {
+	Address []byte `json:"address"`
+	Amount  int64  `json:"amount"`
+}
+
+type GenesisAccount struct {
+	Address     []byte                     `json:"address"`
+	Amount      int64                      `json:"amount"`
+	Name        string                     `json:"name"`
+	Permissions *ptypes.AccountPermissions `json:"permissions"`
+}
+
+type GenesisValidator struct {
+	PubKey   crypto.PubKey  `json:"pub_key"`
+	Amount   int64          `json:"amount"`
+	Name     string         `json:"name"`
+	UnbondTo []BasicAccount `json:"unbond_to"`
+}
+
+type GenesisParams struct {
+	GlobalPermissions *ptypes.AccountPermissions `json:"global_permissions"`
+}
+
+//------------------------------------------------------------
+// GenesisDoc is stored in the state database
+
+type GenesisDoc struct {
+	GenesisTime time.Time          `json:"genesis_time"`
+	ChainID     string             `json:"chain_id"`
+	Params      *GenesisParams     `json:"params"`
+	Accounts    []GenesisAccount   `json:"accounts"`
+	Validators  []GenesisValidator `json:"validators"`
+}
+
+//------------------------------------------------------------
+// Make genesis state from file
+
+func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) {
+	var err error
+	wire.ReadJSONPtr(&genState, jsonBlob, &err)
+	if err != nil {
+		fmt.Printf("Couldn't read GenesisDoc: %v", err)
+		// TODO: on error return error, not exit
+		os.Exit(1)
+	}
+	return
+}
diff --git a/glide.lock b/glide.lock
index 76c7e0b2c0dc6b668890e5388cc0820b9d4b430c..62620c5ded26d6461e28801b4c5ce280fdfcb974 100644
--- a/glide.lock
+++ b/glide.lock
@@ -23,15 +23,8 @@ imports:
   version: f0aeabca5a127c4078abb8c8d64298b147264b55
 - name: github.com/davecgh/go-spew
   version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
-- name: github.com/eris-ltd/common
-  version: 8ca15f5455104403db4202c995e2f6e161654c02
-  subpackages:
-  - go/docs
-  - go/common
 - name: github.com/eris-ltd/eris-keys
   version: 114ebc77443db9a153692233294e48bc7e184215
-- name: github.com/eris-ltd/eris-logger
-  version: ea48a395d6ecc0eccc67a26da9fc7a6106fabb84
 - name: github.com/fsnotify/fsnotify
   version: 30411dbcefb7a1da7e84f75530ad3abe4011b4f8
 - name: github.com/gin-gonic/gin
@@ -215,4 +208,26 @@ imports:
   version: ecde8c8f16df93a994dda8936c8f60f0c26c28ab
 - name: gopkg.in/yaml.v2
   version: a83829b6f1293c91addabc89d0571c246397bbf4
+- name: github.com/go-kit/kit
+  version: f66b0e13579bfc5a48b9e2a94b1209c107ea1f41
+  subpackages:
+  - log
+- name: github.com/eapache/channels
+  version: 47238d5aae8c0fefd518ef2bee46290909cf8263
+- name: github.com/eapache/queue
+  version: 44cc805cf13205b55f69e14bcb69867d1ae92f98
+- name: github.com/go-logfmt/logfmt
+  version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5
+- name: github.com/go-stack/stack
+  version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82
+- name: github.com/Sirupsen/logrus
+  version: d26492970760ca5d33129d2d799e34be5c4782eb
+- name: github.com/inconshreveable/log15
+  version: 46a701a619de90c65a78c04d1a58bf02585e9701
+  subpackages:
+  - term
+- name: github.com/streadway/simpleuuid
+  version: 6617b501e485b77e61b98cd533aefff9e258b5a7
+- name: github.com/Masterminds/glide
+  version: 84607742b10f492430762d038e954236bbaf23f7
 devImports: []
diff --git a/glide.yaml b/glide.yaml
index ca8e52b5c438cf55e2cb5a85ca74617f8d53c485..d832bc2213309b201c8c52bda69fbb9e87ef52f5 100644
--- a/glide.yaml
+++ b/glide.yaml
@@ -1,6 +1,5 @@
 package: github.com/eris-ltd/eris-db
 import:
-- package: github.com/eris-ltd/eris-logger
 - package: github.com/eris-ltd/eris-keys
 - package: github.com/spf13/cobra
 - package: github.com/spf13/viper
@@ -15,5 +14,20 @@ import:
   - ripemd160
 - package: gopkg.in/fatih/set.v0
 - package: gopkg.in/tylerb/graceful.v1
-- package: golang.org/x/net/http2
-- package: github.com/eris-ltd/common
+- package: golang.org/x/net
+  subpackages:
+  - http2
+- package: github.com/go-kit/kit
+  version: ^0.3.0
+- package: github.com/eapache/channels
+  version: ~1.1.0
+- package: github.com/go-logfmt/logfmt
+  version: ^0.3.0
+- package: github.com/go-stack/stack
+  version: ^1.5.2
+- package: github.com/inconshreveable/log15
+- package: github.com/Sirupsen/logrus
+  version: ^0.11.0
+- package: github.com/streadway/simpleuuid
+- package: github.com/Masterminds/glide
+  version: ~0.12.3
\ No newline at end of file
diff --git a/hell b/hell
new file mode 100644
index 0000000000000000000000000000000000000000..392907b3a74800038d60cc899e866c97f0afc28d
--- /dev/null
+++ b/hell
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+
+go run ./util/hell/cmd/hell/main.go "$@"
\ No newline at end of file
diff --git a/keys/key_client.go b/keys/key_client.go
index e841f50abd71f68ce4bdd9fc838acf764e5f3b1f..2431bc643500016bc5859d91c8a48955d451dbf2 100644
--- a/keys/key_client.go
+++ b/keys/key_client.go
@@ -19,6 +19,9 @@ package keys
 import (
 	"encoding/hex"
 	"fmt"
+
+	"github.com/eris-ltd/eris-db/logging"
+	"github.com/eris-ltd/eris-db/logging/loggers"
 )
 
 type KeyClient interface {
@@ -29,31 +32,33 @@ type KeyClient interface {
 	PublicKey(address []byte) (publicKey []byte, err error)
 }
 
-// NOTE [ben] Compiler check to ensure ErisKeyClient successfully implements
+// NOTE [ben] Compiler check to ensure erisKeyClient successfully implements
 // eris-db/keys.KeyClient
-var _ KeyClient = (*ErisKeyClient)(nil)
+var _ KeyClient = (*erisKeyClient)(nil)
 
-type ErisKeyClient struct {
+type erisKeyClient struct {
 	rpcString string
+	logger    loggers.InfoTraceLogger
 }
 
-// ErisKeyClient.New returns a new eris-keys client for provided rpc location
+// erisKeyClient.New returns a new eris-keys client for provided rpc location
 // Eris-keys connects over http request-responses
-func NewErisKeyClient(rpcString string) *ErisKeyClient {
-	return &ErisKeyClient{
+func NewErisKeyClient(rpcString string, logger loggers.InfoTraceLogger) *erisKeyClient {
+	return &erisKeyClient{
 		rpcString: rpcString,
+		logger:    logging.WithScope(logger, "ErisKeysClient"),
 	}
 }
 
 // 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(signBytesString string, signAddress []byte) (signature []byte, err error) {
+func (erisKeys *erisKeyClient) Sign(signBytesString string, signAddress []byte) (signature []byte, err error) {
 	args := map[string]string{
 		"msg":  signBytesString,
 		"hash": signBytesString, // TODO:[ben] backwards compatibility
 		"addr": fmt.Sprintf("%X", signAddress),
 	}
-	sigS, err := RequestResponse(erisKeys.rpcString, "sign", args)
+	sigS, err := RequestResponse(erisKeys.rpcString, "sign", args, erisKeys.logger)
 	if err != nil {
 		return
 	}
@@ -66,11 +71,11 @@ func (erisKeys *ErisKeyClient) Sign(signBytesString string, signAddress []byte)
 
 // 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) {
+func (erisKeys *erisKeyClient) PublicKey(address []byte) (publicKey []byte, err error) {
 	args := map[string]string{
 		"addr": fmt.Sprintf("%X", address),
 	}
-	pubS, err := RequestResponse(erisKeys.rpcString, "pub", args)
+	pubS, err := RequestResponse(erisKeys.rpcString, "pub", args, erisKeys.logger)
 	if err != nil {
 		return
 	}
diff --git a/keys/key_client_util.go b/keys/key_client_util.go
index e9e5f4783d51ce2df4e590cea008535ed5f1a4d7..5fd1cbce41f55d1348584aeb29d55dacf7fcc1d8 100644
--- a/keys/key_client_util.go
+++ b/keys/key_client_util.go
@@ -26,7 +26,8 @@ import (
 	"io/ioutil"
 	"net/http"
 
-	log "github.com/eris-ltd/eris-logger"
+	"github.com/eris-ltd/eris-db/logging"
+	"github.com/eris-ltd/eris-db/logging/loggers"
 )
 
 // Eris-Keys server connects over http request-response structures
@@ -36,17 +37,17 @@ type HTTPResponse struct {
 	Error    string
 }
 
-func RequestResponse(addr, method string, args map[string]string) (string, error) {
-	b, err := json.Marshal(args)
+func RequestResponse(addr, method string, args map[string]string, logger loggers.InfoTraceLogger) (string, error) {
+	body, 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("Eris-client: Sending request body to key server")
-	req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(b))
+	logging.TraceMsg(logger, "Sending request to key server",
+		"key_server_endpoint", endpoint,
+		"request_body", string(body),
+	)
+	req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(body))
 	if err != nil {
 		return "", err
 	}
@@ -58,11 +59,11 @@ func RequestResponse(addr, method string, args map[string]string) (string, 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")
+	logging.TraceMsg(logger, "Received response from key server",
+		"endpoint", endpoint,
+		"request body", string(body),
+		"response", res,
+	)
 	return res, nil
 }
 
diff --git a/logging/adapters/logrus/logrus.go b/logging/adapters/logrus/logrus.go
new file mode 100644
index 0000000000000000000000000000000000000000..fe974768f69179c01c1d3a5b4b6334ba7d6ce7d2
--- /dev/null
+++ b/logging/adapters/logrus/logrus.go
@@ -0,0 +1,22 @@
+package adapters
+
+import (
+	"github.com/Sirupsen/logrus"
+	kitlog "github.com/go-kit/kit/log"
+)
+
+type logrusLogger struct {
+	logger logrus.Logger
+}
+
+var _ kitlog.Logger = (*logrusLogger)(nil)
+
+func NewLogrusLogger(logger logrus.Logger) *logrusLogger {
+	return &logrusLogger{
+		logger: logger,
+	}
+}
+
+func (ll *logrusLogger) Log(keyvals ...interface{}) error {
+	return nil
+}
diff --git a/logging/adapters/stdlib/capture.go b/logging/adapters/stdlib/capture.go
new file mode 100644
index 0000000000000000000000000000000000000000..b5fadacc9d87cd3f71a8a603d29040f8dc4d8921
--- /dev/null
+++ b/logging/adapters/stdlib/capture.go
@@ -0,0 +1,26 @@
+package stdlib
+
+import (
+	"io"
+	"log"
+
+	"github.com/eris-ltd/eris-db/logging/loggers"
+	kitlog "github.com/go-kit/kit/log"
+)
+
+func Capture(stdLibLogger log.Logger,
+	logger loggers.InfoTraceLogger) io.Writer {
+	adapter := newAdapter(logger)
+	stdLibLogger.SetOutput(adapter)
+	return adapter
+}
+
+func CaptureRootLogger(logger loggers.InfoTraceLogger) io.Writer {
+	adapter := newAdapter(logger)
+	log.SetOutput(adapter)
+	return adapter
+}
+
+func newAdapter(logger loggers.InfoTraceLogger) io.Writer {
+	return kitlog.NewStdlibAdapter(logger)
+}
diff --git a/logging/adapters/tendermint_log15/capture.go b/logging/adapters/tendermint_log15/capture.go
new file mode 100644
index 0000000000000000000000000000000000000000..afc5d29eedcc61aacbbe8318775da50e74be0468
--- /dev/null
+++ b/logging/adapters/tendermint_log15/capture.go
@@ -0,0 +1,47 @@
+package adapters
+
+import (
+	"github.com/eris-ltd/eris-db/logging/loggers"
+	kitlog "github.com/go-kit/kit/log"
+	"github.com/tendermint/log15"
+)
+
+type infoTraceLoggerAsLog15Handler struct {
+	logger loggers.InfoTraceLogger
+}
+
+var _ log15.Handler = (*infoTraceLoggerAsLog15Handler)(nil)
+
+type log15HandlerAsKitLogger struct {
+	handler log15.Handler
+}
+
+var _ kitlog.Logger = (*log15HandlerAsKitLogger)(nil)
+
+func (l *log15HandlerAsKitLogger) Log(keyvals ...interface{}) error {
+	record := LogLineToRecord(keyvals...)
+	return l.handler.Log(record)
+}
+
+func (h *infoTraceLoggerAsLog15Handler) Log(record *log15.Record) error {
+	if record.Lvl < log15.LvlDebug {
+		// Send to Critical, Warning, Error, and Info to the Info channel
+		h.logger.Info(RecordToLogLine(record)...)
+	} else {
+		// Send to Debug to the Trace channel
+		h.logger.Trace(RecordToLogLine(record)...)
+	}
+	return nil
+}
+
+func Log15HandlerAsKitLogger(handler log15.Handler) kitlog.Logger {
+	return &log15HandlerAsKitLogger{
+		handler: handler,
+	}
+}
+
+func InfoTraceLoggerAsLog15Handler(logger loggers.InfoTraceLogger) log15.Handler {
+	return &infoTraceLoggerAsLog15Handler{
+		logger: logger,
+	}
+}
diff --git a/logging/adapters/tendermint_log15/convert.go b/logging/adapters/tendermint_log15/convert.go
new file mode 100644
index 0000000000000000000000000000000000000000..f7be8420866a67a987afb39f84b2e93cea849018
--- /dev/null
+++ b/logging/adapters/tendermint_log15/convert.go
@@ -0,0 +1,70 @@
+package adapters
+
+import (
+	"time"
+
+	"github.com/eris-ltd/eris-db/logging/loggers"
+	"github.com/eris-ltd/eris-db/logging/structure"
+	. "github.com/eris-ltd/eris-db/util/slice"
+	"github.com/go-stack/stack"
+	"github.com/tendermint/log15"
+)
+
+// Convert a go-kit log line (i.e. keyvals... interface{}) into a log15 record
+// This allows us to use log15 output handlers
+func LogLineToRecord(keyvals ...interface{}) *log15.Record {
+	vals, ctx := structure.ValuesAndContext(keyvals, structure.TimeKey,
+		structure.MessageKey, structure.CallerKey, structure.LevelKey)
+
+	// Mapping of log line to Record is on a best effort basis
+	theTime, _ := vals[structure.TimeKey].(time.Time)
+	call, _ := vals[structure.CallerKey].(stack.Call)
+	level, _ := vals[structure.LevelKey].(string)
+	message, _ := vals[structure.MessageKey].(string)
+
+	return &log15.Record{
+		Time: theTime,
+		Lvl:  Log15LvlFromString(level),
+		Msg:  message,
+		Call: call,
+		Ctx:  ctx,
+		KeyNames: log15.RecordKeyNames{
+			Time: structure.TimeKey,
+			Msg:  structure.MessageKey,
+			Lvl:  structure.LevelKey,
+		}}
+}
+
+// Convert a log15 record to a go-kit log line (i.e. keyvals... interface{})
+// This allows us to capture output from dependencies using log15
+func RecordToLogLine(record *log15.Record) []interface{} {
+	return Concat(
+		Slice(
+			structure.TimeKey, record.Time,
+			structure.CallerKey, record.Call,
+			structure.LevelKey, record.Lvl.String(),
+		),
+		record.Ctx,
+		Slice(
+			structure.MessageKey, record.Msg,
+		))
+}
+
+// Collapse our weak notion of leveling and log15's into a log15.Lvl
+func Log15LvlFromString(level string) log15.Lvl {
+	if level == "" {
+		return log15.LvlDebug
+	}
+	switch level {
+	case loggers.InfoLevelName:
+		return log15.LvlInfo
+	case loggers.TraceLevelName:
+		return log15.LvlDebug
+	default:
+		lvl, err := log15.LvlFromString(level)
+		if err == nil {
+			return lvl
+		}
+		return log15.LvlDebug
+	}
+}
diff --git a/logging/config.go b/logging/config.go
new file mode 100644
index 0000000000000000000000000000000000000000..b43b1b5545ba47586cb3e61a01589bac4de564c0
--- /dev/null
+++ b/logging/config.go
@@ -0,0 +1,11 @@
+package logging
+
+type (
+	SinkConfig struct {
+		Channels []string
+	}
+
+	LoggingConfig struct {
+		Sinks []SinkConfig
+	}
+)
diff --git a/logging/convention.go b/logging/convention.go
new file mode 100644
index 0000000000000000000000000000000000000000..0f5d3372c13bb9903ea1db88ef24035bfc33a9b5
--- /dev/null
+++ b/logging/convention.go
@@ -0,0 +1,58 @@
+package logging
+
+import (
+	"github.com/eris-ltd/eris-db/logging/loggers"
+	"github.com/eris-ltd/eris-db/logging/structure"
+	"github.com/eris-ltd/eris-db/util/slice"
+	kitlog "github.com/go-kit/kit/log"
+)
+
+// Helper functions for InfoTraceLoggers, sort of extension methods to loggers
+// to centralise and establish logging conventions on top of in with the base
+// logging interface
+
+// Record structured Info log line with a message and conventional keys
+func InfoMsgVals(logger loggers.InfoTraceLogger, message string, vals ...interface{}) {
+	MsgVals(kitlog.LoggerFunc(logger.Info), message, vals...)
+}
+
+// Record structured Trace log line with a message and conventional keys
+func TraceMsgVals(logger loggers.InfoTraceLogger, message string, vals ...interface{}) {
+	MsgVals(kitlog.LoggerFunc(logger.Trace), message, vals...)
+}
+
+// Record structured Info log line with a message
+func InfoMsg(logger loggers.InfoTraceLogger, message string, keyvals ...interface{}) {
+	Msg(kitlog.LoggerFunc(logger.Info), message, keyvals...)
+}
+
+// Record structured Trace log line with a message
+func TraceMsg(logger loggers.InfoTraceLogger, message string, keyvals ...interface{}) {
+	Msg(kitlog.LoggerFunc(logger.Trace), message, keyvals...)
+}
+
+// Establish or extend the scope of this logger by appending scopeName to the Scope vector.
+// Like With the logging scope is append only but can be used to provide parenthetical scopes by hanging on to the
+// parent scope and using once the scope has been exited. The scope mechanism does is agnostic to the type of scope
+// so can be used to identify certain segments of the call stack, a lexical scope, or any other nested scope.
+func WithScope(logger loggers.InfoTraceLogger, scopeName string) loggers.InfoTraceLogger {
+	// InfoTraceLogger will collapse successive (ScopeKey, scopeName) pairs into a vector in the order which they appear
+	return logger.With(structure.ScopeKey, scopeName)
+}
+
+// Record a structured log line with a message
+func Msg(logger kitlog.Logger, message string, keyvals ...interface{}) error {
+	prepended := slice.CopyPrepend(keyvals, structure.MessageKey, message)
+	return logger.Log(prepended...)
+}
+
+// Record a structured log line with a message and conventional keys
+func MsgVals(logger kitlog.Logger, message string, vals ...interface{}) error {
+	keyvals := make([]interface{}, len(vals)*2)
+	for i := 0; i < len(vals); i++ {
+		kv := i * 2
+		keyvals[kv] = structure.KeyFromValue(vals[i])
+		keyvals[kv+1] = vals[i]
+	}
+	return Msg(logger, message, keyvals)
+}
diff --git a/logging/lifecycle/lifecycle.go b/logging/lifecycle/lifecycle.go
new file mode 100644
index 0000000000000000000000000000000000000000..ce0faef40ace4decfb9f71a09126d3d22ce4e41d
--- /dev/null
+++ b/logging/lifecycle/lifecycle.go
@@ -0,0 +1,58 @@
+package lifecycle
+
+// No package in ./logging/... should depend on lifecycle
+import (
+	"os"
+
+	"time"
+
+	"github.com/eris-ltd/eris-db/logging"
+	"github.com/eris-ltd/eris-db/logging/adapters/stdlib"
+	tmLog15adapter "github.com/eris-ltd/eris-db/logging/adapters/tendermint_log15"
+	"github.com/eris-ltd/eris-db/logging/loggers"
+	"github.com/eris-ltd/eris-db/logging/structure"
+
+	kitlog "github.com/go-kit/kit/log"
+	"github.com/streadway/simpleuuid"
+	tmLog15 "github.com/tendermint/log15"
+)
+
+// Lifecycle provides a canonical source for eris loggers. Components should use the functions here
+// to set up their root logger and capture any other logging output.
+
+// Obtain a logger from a LoggingConfig
+func NewLoggerFromLoggingConfig(LoggingConfig *logging.LoggingConfig) loggers.InfoTraceLogger {
+	return NewStdErrLogger()
+}
+
+func NewStdErrLogger() loggers.InfoTraceLogger {
+	logger := tmLog15adapter.Log15HandlerAsKitLogger(
+		tmLog15.StreamHandler(os.Stderr, tmLog15.TerminalFormat()))
+	return NewLogger(logger, logger)
+}
+
+// Provided a standard eris logger that outputs to the supplied underlying info and trace
+// loggers
+func NewLogger(infoLogger, traceLogger kitlog.Logger) loggers.InfoTraceLogger {
+	infoTraceLogger := loggers.NewInfoTraceLogger(
+		loggers.ErisFormatLogger(infoLogger),
+		loggers.ErisFormatLogger(traceLogger))
+	// Create a random ID based on start time
+	uuid, _ := simpleuuid.NewTime(time.Now())
+	var runId string
+	if uuid != nil {
+		runId = uuid.String()
+	}
+	return logging.WithMetadata(infoTraceLogger.With(structure.RunId, runId))
+}
+
+func CaptureTendermintLog15Output(infoTraceLogger loggers.InfoTraceLogger) {
+	tmLog15.Root().SetHandler(
+		tmLog15adapter.InfoTraceLoggerAsLog15Handler(infoTraceLogger.
+			With(structure.CapturedLoggingSourceKey, "tendermint_log15")))
+}
+
+func CaptureStdlibLogOutput(infoTraceLogger loggers.InfoTraceLogger) {
+	stdlib.CaptureRootLogger(infoTraceLogger.
+		With(structure.CapturedLoggingSourceKey, "stdlib_log"))
+}
diff --git a/logging/loggers/channel_logger.go b/logging/loggers/channel_logger.go
new file mode 100644
index 0000000000000000000000000000000000000000..20e824ef804deaf9de64266d94c1ef8338d47344
--- /dev/null
+++ b/logging/loggers/channel_logger.go
@@ -0,0 +1,73 @@
+package loggers
+
+import (
+	"github.com/eapache/channels"
+	kitlog "github.com/go-kit/kit/log"
+)
+
+const (
+	LoggingRingBufferCap channels.BufferCap = 100
+)
+
+type ChannelLogger struct {
+	ch channels.Channel
+}
+
+var _ kitlog.Logger = (*ChannelLogger)(nil)
+
+// Creates a Logger that uses a uses a non-blocking channel.
+//
+// We would like calls to Log to never block so we use a channel implementation
+// that is non-blocking on writes and is able to be so by using a finite ring
+// buffer.
+func newChannelLogger() *ChannelLogger {
+	return &ChannelLogger{
+		ch: channels.NewRingChannel(LoggingRingBufferCap),
+	}
+}
+
+func (cl *ChannelLogger) Log(keyvals ...interface{}) error {
+	cl.ch.In() <- keyvals
+	// We don't have a way to pass on any logging errors, but that's okay: Log is
+	// a maximal interface and the error return type is only there for special
+	// cases.
+	return nil
+}
+
+// Read a log line by waiting until one is available and returning it
+func (cl *ChannelLogger) WaitReadLogLine() []interface{} {
+	log := <-cl.ch.Out()
+	// We are passing slices of interfaces down this channel (go-kit log's Log
+	// interface type), a panic is the right thing to do if this type assertion
+	// fails.
+	return log.([]interface{})
+}
+
+// Tries to read a log line from the channel buffer or returns nil if none is
+// immediately available
+func (cl *ChannelLogger) ReadLogLine() []interface{} {
+	select {
+	case log := <-cl.ch.Out():
+		// See WaitReadLogLine
+		return log.([]interface{})
+	default:
+		return nil
+	}
+}
+
+// Enters an infinite loop that will drain any log lines from the passed logger.
+//
+// Exits if the channel is closed.
+func (cl *ChannelLogger) DrainChannelToLogger(logger kitlog.Logger) {
+	for cl.ch.Out() != nil {
+		logger.Log(cl.WaitReadLogLine()...)
+	}
+}
+
+// Wraps an underlying Logger baseLogger to provide a Logger that is
+// is non-blocking on calls to Log.
+func NonBlockingLogger(logger kitlog.Logger) *ChannelLogger {
+	cl := newChannelLogger()
+	go cl.DrainChannelToLogger(logger)
+	return cl
+}
diff --git a/logging/loggers/channel_logger_test.go b/logging/loggers/channel_logger_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..14b50fa0edc621761e7f9e78df2c304478a4a31e
--- /dev/null
+++ b/logging/loggers/channel_logger_test.go
@@ -0,0 +1,33 @@
+package loggers
+
+import (
+	"testing"
+
+	"fmt"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestChannelLogger(t *testing.T) {
+	cl := newChannelLogger()
+
+	// Push a larger number of log messages than will fit into ring buffer
+	for i := 0; i < int(LoggingRingBufferCap)+10; i++ {
+		cl.Log("log line", i)
+	}
+
+	// Observe that oldest 10 messages are overwritten (so first message is 10)
+	for i := 0; i < int(LoggingRingBufferCap); i++ {
+		ll := cl.WaitReadLogLine()
+		assert.Equal(t, 10+i, ll[1])
+	}
+
+	assert.Nil(t, cl.ReadLogLine(), "Since we have drained the buffer there "+
+		"should be no more log lines.")
+}
+
+func TestBlether(t *testing.T) {
+	var bs []byte
+	ext := append(bs)
+	fmt.Println(ext)
+}
diff --git a/logging/loggers/eris_format_logger.go b/logging/loggers/eris_format_logger.go
new file mode 100644
index 0000000000000000000000000000000000000000..20cdd8d653ad1ef09076c24e547d7f0f89e58297
--- /dev/null
+++ b/logging/loggers/eris_format_logger.go
@@ -0,0 +1,39 @@
+package loggers
+
+import (
+	"fmt"
+
+	"github.com/eris-ltd/eris-db/logging/structure"
+
+	kitlog "github.com/go-kit/kit/log"
+)
+
+// Logger that implements some formatting conventions for eris-db and eris-client
+// This is intended for applying consistent value formatting before the final 'output' logger;
+// we should avoid prematurely formatting values here if it is useful to let the output logger
+// decide how it wants to display values. Ideal candidates for 'early' formatting here are types that
+// we control and generic output loggers are unlikely to know about.
+type erisFormatLogger struct {
+	logger kitlog.Logger
+}
+
+var _ kitlog.Logger = &erisFormatLogger{}
+
+func (efl *erisFormatLogger) Log(keyvals ...interface{}) error {
+	return efl.logger.Log(structure.MapKeyValues(keyvals, erisFormatKeyValueMapper)...)
+}
+
+func erisFormatKeyValueMapper(key, value interface{}) (interface{}, interface{}) {
+	switch key {
+	default:
+		switch v := value.(type) {
+		case []byte:
+			return key, fmt.Sprintf("%X", v)
+		}
+	}
+	return key, value
+}
+
+func ErisFormatLogger(logger kitlog.Logger) *erisFormatLogger {
+	return &erisFormatLogger{logger: logger}
+}
diff --git a/logging/loggers/info_trace_logger.go b/logging/loggers/info_trace_logger.go
new file mode 100644
index 0000000000000000000000000000000000000000..6cbdcee6bd9f0aeb2d7965465835f304226eb42a
--- /dev/null
+++ b/logging/loggers/info_trace_logger.go
@@ -0,0 +1,127 @@
+package loggers
+
+import (
+	"github.com/eris-ltd/eris-db/logging/structure"
+	kitlog "github.com/go-kit/kit/log"
+)
+
+const (
+	InfoChannelName  = "Info"
+	TraceChannelName = "Trace"
+
+	InfoLevelName  = InfoChannelName
+	TraceLevelName = TraceChannelName
+)
+
+type infoTraceLogger struct {
+	infoLogger  *kitlog.Context
+	traceLogger *kitlog.Context
+}
+
+// InfoTraceLogger maintains two independent concurrently-safe channels of
+// logging. The idea behind the independence is that you can ignore one channel
+// with no performance penalty. For more fine grained filtering or aggregation
+// the Info and Trace loggers can be decorated loggers that perform arbitrary
+// filtering/routing/aggregation on log messages.
+type InfoTraceLogger interface {
+	// Send a log message to the default channel
+	kitlog.Logger
+
+	// Send an log message to the Info channel, formed of a sequence of key value
+	// pairs. Info messages should be operationally interesting to a human who is
+	// monitoring the logs. But not necessarily a human who is trying to
+	// understand or debug the system. Any handled errors or warnings should be
+	// sent to the Info channel (where you may wish to tag them with a suitable
+	// key-value pair to categorise them as such).
+	Info(keyvals ...interface{}) error
+
+	// Send an log message to the Trace channel, formed of a sequence of key-value
+	// pairs. Trace messages can be used for any state change in the system that
+	// may be of interest to a machine consumer or a human who is trying to debug
+	// the system or trying to understand the system in detail. If the messages
+	// are very point-like and contain little structure, consider using a metric
+	// instead.
+	Trace(keyvals ...interface{}) error
+
+	// A logging context (see go-kit log's Context). Takes a sequence key values
+	// via With or WithPrefix and ensures future calls to log will have those
+	// contextual values appended to the call to an underlying logger.
+	// Values can be dynamic by passing an instance of the kitlog.Valuer interface
+	// This provides an interface version of the kitlog.Context struct to be used
+	// For implementations that wrap a kitlog.Context. In addition it makes no
+	// assumption about the name or signature of the logging method(s).
+	// See InfoTraceLogger
+
+	// Establish a context by appending contextual key-values to any existing
+	// contextual values
+	With(keyvals ...interface{}) InfoTraceLogger
+
+	// Establish a context by prepending contextual key-values to any existing
+	// contextual values
+	WithPrefix(keyvals ...interface{}) InfoTraceLogger
+}
+
+// Interface assertions
+var _ InfoTraceLogger = (*infoTraceLogger)(nil)
+var _ kitlog.Logger = (InfoTraceLogger)(nil)
+
+func NewInfoTraceLogger(infoLogger, traceLogger kitlog.Logger) InfoTraceLogger {
+	// We will never halt the progress of a log emitter. If log output takes too
+	// long will start dropping log lines by using a ring buffer.
+	// We also guard against any concurrency bugs in underlying loggers by feeding
+	// them from a single channel
+	logger := kitlog.NewContext(NonBlockingLogger(VectorValuedLogger(
+		MultipleChannelLogger(
+			map[string]kitlog.Logger{
+				InfoChannelName:  infoLogger,
+				TraceChannelName: traceLogger,
+			}))))
+	return &infoTraceLogger{
+		infoLogger: logger.With(
+			structure.ChannelKey, InfoChannelName,
+			structure.LevelKey, InfoLevelName,
+		),
+		traceLogger: logger.With(
+			structure.ChannelKey, TraceChannelName,
+			structure.LevelKey, TraceLevelName,
+		),
+	}
+}
+
+func NewNoopInfoTraceLogger() InfoTraceLogger {
+	noopLogger := kitlog.NewNopLogger()
+	return NewInfoTraceLogger(noopLogger, noopLogger)
+}
+
+func (l *infoTraceLogger) With(keyvals ...interface{}) InfoTraceLogger {
+	return &infoTraceLogger{
+		infoLogger:  l.infoLogger.With(keyvals...),
+		traceLogger: l.traceLogger.With(keyvals...),
+	}
+}
+
+func (l *infoTraceLogger) WithPrefix(keyvals ...interface{}) InfoTraceLogger {
+	return &infoTraceLogger{
+		infoLogger:  l.infoLogger.WithPrefix(keyvals...),
+		traceLogger: l.traceLogger.WithPrefix(keyvals...),
+	}
+}
+
+func (l *infoTraceLogger) Info(keyvals ...interface{}) error {
+	// We send Info and Trace log lines down the same pipe to keep them ordered
+	return l.infoLogger.Log(keyvals...)
+}
+
+func (l *infoTraceLogger) Trace(keyvals ...interface{}) error {
+	return l.traceLogger.Log(keyvals...)
+}
+
+// If logged to as a plain kitlog logger presume the message is for Trace
+// This favours keeping Info reasonably quiet. Note that an InfoTraceLogger
+// aware adapter can make its own choices, but we tend to thing of logs from
+// dependencies as less interesting than logs generated by us or specifically
+// routed by us.
+func (l *infoTraceLogger) Log(keyvals ...interface{}) error {
+	l.Trace(keyvals...)
+	return nil
+}
diff --git a/logging/loggers/info_trace_logger_test.go b/logging/loggers/info_trace_logger_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..505f2606badbeb178f9046982a6d130030225456
--- /dev/null
+++ b/logging/loggers/info_trace_logger_test.go
@@ -0,0 +1,14 @@
+package loggers
+
+import (
+	"os"
+	"testing"
+
+	kitlog "github.com/go-kit/kit/log"
+)
+
+func TestLogger(t *testing.T) {
+	stderrLogger := kitlog.NewLogfmtLogger(os.Stderr)
+	logger := NewInfoTraceLogger(stderrLogger, stderrLogger)
+	logger.Trace("hello", "barry")
+}
diff --git a/logging/loggers/logging_test.go b/logging/loggers/logging_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..78166fc91ca74989abeb7e08daf793dce10595a2
--- /dev/null
+++ b/logging/loggers/logging_test.go
@@ -0,0 +1,21 @@
+package loggers
+
+import "errors"
+
+type testLogger struct {
+	logLines [][]interface{}
+	err      error
+}
+
+func newErrorLogger(errMessage string) *testLogger {
+	return &testLogger{err: errors.New(errMessage)}
+}
+
+func newTestLogger() *testLogger {
+	return &testLogger{}
+}
+
+func (tl *testLogger) Log(keyvals ...interface{}) error {
+	tl.logLines = append(tl.logLines, keyvals)
+	return tl.err
+}
diff --git a/logging/loggers/multiple_channel_logger.go b/logging/loggers/multiple_channel_logger.go
new file mode 100644
index 0000000000000000000000000000000000000000..47ee96065ae4d8b04a3130f5903bc192ea341aed
--- /dev/null
+++ b/logging/loggers/multiple_channel_logger.go
@@ -0,0 +1,37 @@
+package loggers
+
+import (
+	"fmt"
+
+	"github.com/eris-ltd/eris-db/logging/structure"
+	kitlog "github.com/go-kit/kit/log"
+)
+
+// This represents a 'SELECT ONE' type logger. When logged to it will search
+// for the ChannelKey field, look that up in its map and send the log line there
+// Otherwise logging is a noop (but an error will be returned - which is optional)
+type MultipleChannelLogger map[string]kitlog.Logger
+
+var _ kitlog.Logger = MultipleChannelLogger(nil)
+
+// Like go-kit log's Log method only logs a message to the specified channelName
+// which must be a member of this MultipleChannelLogger
+func (mcl MultipleChannelLogger) Log(keyvals ...interface{}) error {
+	channel := structure.Value(keyvals, structure.ChannelKey)
+	if channel == nil {
+		return fmt.Errorf("MultipleChannelLogger could not select channel because"+
+			" '%s' was not set in log message", structure.ChannelKey)
+	}
+	channelName, ok := channel.(string)
+	if !ok {
+		return fmt.Errorf("MultipleChannelLogger could not select channel because"+
+			" channel was set to non-string value %v", channel)
+	}
+	logger := mcl[channelName]
+	if logger == nil {
+		return fmt.Errorf("Could not log to channel '%s', since it is not "+
+			"registered with this MultipleChannelLogger (the underlying logger may "+
+			"have been nil when passed to NewMultipleChannelLogger)", channelName)
+	}
+	return logger.Log(keyvals...)
+}
diff --git a/logging/loggers/multiple_channel_logger_test.go b/logging/loggers/multiple_channel_logger_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..db8f5013a13c636af96c2791d0ee5e00a38695fc
--- /dev/null
+++ b/logging/loggers/multiple_channel_logger_test.go
@@ -0,0 +1,28 @@
+package loggers
+
+import (
+	"runtime"
+	"testing"
+	"time"
+
+	"github.com/eris-ltd/eris-db/logging/structure"
+	kitlog "github.com/go-kit/kit/log"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestMultipleChannelLogger(t *testing.T) {
+	boringLogger, interestingLogger := newTestLogger(), newTestLogger()
+	mcl := kitlog.NewContext(MultipleChannelLogger(map[string]kitlog.Logger{
+		"Boring":      boringLogger,
+		"Interesting": interestingLogger,
+	}))
+	err := mcl.With("time", kitlog.Valuer(func() interface{} { return "aa" })).
+		Log(structure.ChannelKey, "Boring", "foo", "bar")
+	assert.NoError(t, err, "Should log without an error")
+	// Wait for channel to drain
+	time.Sleep(time.Second)
+	runtime.Gosched()
+	assert.Equal(t, []interface{}{"time", "aa", structure.ChannelKey, "Boring",
+		"foo", "bar"},
+		boringLogger.logLines[0])
+}
diff --git a/logging/loggers/multiple_output_logger.go b/logging/loggers/multiple_output_logger.go
new file mode 100644
index 0000000000000000000000000000000000000000..9f3cb5326aa1a98d0f3fcfad6462051965749064
--- /dev/null
+++ b/logging/loggers/multiple_output_logger.go
@@ -0,0 +1,50 @@
+package loggers
+
+import (
+	"strings"
+
+	kitlog "github.com/go-kit/kit/log"
+)
+
+// This represents an 'AND' type logger. When logged to it will log to each of
+// the loggers in the slice.
+type MultipleOutputLogger []kitlog.Logger
+
+var _ kitlog.Logger = MultipleOutputLogger(nil)
+
+func (mol MultipleOutputLogger) Log(keyvals ...interface{}) error {
+	var errs []error
+	for _, logger := range mol {
+		err := logger.Log(keyvals...)
+		if err != nil {
+			errs = append(errs, err)
+		}
+	}
+	return combineErrors(errs)
+}
+
+// Creates a logger that forks log messages to each of its outputLoggers
+func NewMultipleOutputLogger(outputLoggers ...kitlog.Logger) kitlog.Logger {
+	return MultipleOutputLogger(outputLoggers)
+}
+
+type multipleErrors []error
+
+func combineErrors(errs []error) error {
+	switch len(errs) {
+	case 0:
+		return nil
+	case 1:
+		return errs[0]
+	default:
+		return multipleErrors(errs)
+	}
+}
+
+func (errs multipleErrors) Error() string {
+	var errStrings []string
+	for _, err := range errs {
+		errStrings = append(errStrings, err.Error())
+	}
+	return strings.Join(errStrings, ";")
+}
diff --git a/logging/loggers/multiple_output_logger_test.go b/logging/loggers/multiple_output_logger_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..786c60064947d3475c60275f45140ce39edde199
--- /dev/null
+++ b/logging/loggers/multiple_output_logger_test.go
@@ -0,0 +1,18 @@
+package loggers
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewMultipleOutputLogger(t *testing.T) {
+	a, b := newErrorLogger("error a"), newErrorLogger("error b")
+	mol := NewMultipleOutputLogger(a, b)
+	logLine := []interface{}{"msg", "hello"}
+	err := mol.Log(logLine...)
+	expected := [][]interface{}{logLine}
+	assert.Equal(t, expected, a.logLines)
+	assert.Equal(t, expected, b.logLines)
+	assert.IsType(t, multipleErrors{}, err)
+}
diff --git a/logging/loggers/vector_valued_logger.go b/logging/loggers/vector_valued_logger.go
new file mode 100644
index 0000000000000000000000000000000000000000..b8963db13f61a9e02fdf5273010d9f4910d51009
--- /dev/null
+++ b/logging/loggers/vector_valued_logger.go
@@ -0,0 +1,21 @@
+package loggers
+
+import (
+	"github.com/eris-ltd/eris-db/logging/structure"
+	kitlog "github.com/go-kit/kit/log"
+)
+
+// Treat duplicate key-values as consecutive entries in a vector-valued lookup
+type vectorValuedLogger struct {
+	logger kitlog.Logger
+}
+
+var _ kitlog.Logger = &vectorValuedLogger{}
+
+func (vvl *vectorValuedLogger) Log(keyvals ...interface{}) error {
+	return vvl.logger.Log(structure.Vectorise(keyvals)...)
+}
+
+func VectorValuedLogger(logger kitlog.Logger) *vectorValuedLogger {
+	return &vectorValuedLogger{logger: logger}
+}
diff --git a/logging/loggers/vector_valued_logger_test.go b/logging/loggers/vector_valued_logger_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..d6bc54ebdcfb76d0a7b3f20606828d458e6a5d13
--- /dev/null
+++ b/logging/loggers/vector_valued_logger_test.go
@@ -0,0 +1,17 @@
+package loggers
+
+import (
+	"testing"
+
+	. "github.com/eris-ltd/eris-db/util/slice"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestVectorValuedLogger(t *testing.T) {
+	logger := newTestLogger()
+	vvl := VectorValuedLogger(logger)
+	vvl.Log("foo", "bar", "seen", 1, "seen", 3, "seen", 2)
+
+	assert.Equal(t, Slice("foo", "bar", "seen", Slice(1, 3, 2)),
+		logger.logLines[0])
+}
diff --git a/logging/metadata.go b/logging/metadata.go
new file mode 100644
index 0000000000000000000000000000000000000000..d645699902e9a6b9a334390a8824c774aac5a4e6
--- /dev/null
+++ b/logging/metadata.go
@@ -0,0 +1,33 @@
+package logging
+
+import (
+	"time"
+
+	"github.com/eris-ltd/eris-db/logging/loggers"
+	"github.com/eris-ltd/eris-db/logging/structure"
+	kitlog "github.com/go-kit/kit/log"
+	"github.com/go-stack/stack"
+)
+
+const (
+	// To get the Caller information correct on the log, we need to count the
+	// number of calls from a log call in the code to the time it hits a kitlog
+	// context: [log call site (5), Info/Trace (4), MultipleChannelLogger.Log (3),
+	// kitlog.Context.Log (2), kitlog.bindValues (1) (binding occurs),
+	// kitlog.Caller (0), stack.caller]
+	infoTraceLoggerCallDepth = 5
+)
+
+var defaultTimestampUTCValuer kitlog.Valuer = func() interface{} {
+	return time.Now()
+}
+
+func WithMetadata(infoTraceLogger loggers.InfoTraceLogger) loggers.InfoTraceLogger {
+	return infoTraceLogger.With(structure.TimeKey, defaultTimestampUTCValuer,
+		structure.CallerKey, kitlog.Caller(infoTraceLoggerCallDepth),
+		"trace", TraceValuer())
+}
+
+func TraceValuer() kitlog.Valuer {
+	return func() interface{} { return stack.Trace() }
+}
diff --git a/logging/structure/structure.go b/logging/structure/structure.go
new file mode 100644
index 0000000000000000000000000000000000000000..a104e8c9dad8cc5ec7dc67880557e5593f071111
--- /dev/null
+++ b/logging/structure/structure.go
@@ -0,0 +1,146 @@
+package structure
+
+import (
+	"reflect"
+
+	. "github.com/eris-ltd/eris-db/util/slice"
+)
+
+const (
+	// Log time (time.Time)
+	TimeKey = "time"
+	// Call site for log invocation (go-stack.Call)
+	CallerKey = "caller"
+	// Level name (string)
+	LevelKey = "level"
+	// Channel name in a vector channel logging context
+	ChannelKey = "channel"
+	// Log message (string)
+	MessageKey = "message"
+	// Captured logging source (like tendermint_log15, stdlib_log)
+	CapturedLoggingSourceKey = "captured_logging_source"
+	// Top-level component (choose one) name
+	ComponentKey = "component"
+	// Vector-valued scope
+	ScopeKey = "scope"
+	// Globally unique identifier persisting while a single instance (root process)
+	// of this program/service is running
+	RunId = "run_id"
+)
+
+// Pull the specified values from a structured log line into a map.
+// Assumes keys are single-valued.
+// Returns a map of the key-values from the requested keys and
+// the unmatched remainder keyvals as context as a slice of key-values.
+func ValuesAndContext(keyvals []interface{},
+	keys ...interface{}) (map[interface{}]interface{}, []interface{}) {
+	vals := make(map[interface{}]interface{}, len(keys))
+	context := make([]interface{}, len(keyvals))
+	copy(context, keyvals)
+	deletions := 0
+	// We can't really do better than a linear scan of both lists here. N is small
+	// so screw the asymptotics.
+	// Guard against odd-length list
+	for i := 0; i < 2*(len(keyvals)/2); i += 2 {
+		for k := 0; k < len(keys); k++ {
+			if keyvals[i] == keys[k] {
+				// Pull the matching key-value pair into vals to return
+				vals[keys[k]] = keyvals[i+1]
+				// Delete the key once it's found
+				keys = DeleteAt(keys, k)
+				// And remove the key-value pair from context
+				context = Delete(context, i-deletions, 2)
+				// Keep a track of how much we've shrunk the context to offset next
+				// deletion
+				deletions += 2
+				break
+			}
+		}
+	}
+	return vals, context
+}
+
+// Stateful index that tracks the location of a possible vector value
+type vectorValueindex struct {
+	// Location of the value belonging to a key in output slice
+	valueIndex int
+	// Whether or not the value is currently a vector
+	vector bool
+}
+
+// 'Vectorises' values associated with repeated string keys member by collapsing many values into a single vector value.
+// The result is a copy of keyvals where the first occurrence of each matching key and its first value are replaced by
+// that key and all of its values in a single slice.
+func Vectorise(keyvals []interface{}, vectorKeys ...string) []interface{} {
+	// We rely on working against a single backing array, so we use a capacity that is the maximum possible size of the
+	// slice after vectorising (in the case there are no duplicate keys and this is a no-op)
+	outputKeyvals := make([]interface{}, 0, len(keyvals))
+	// Track the location and vector status of the values in the output
+	valueIndices := make(map[string]*vectorValueindex, len(vectorKeys))
+	elided := 0
+	for i := 0; i < 2*(len(keyvals)/2); i += 2 {
+		key := keyvals[i]
+		val := keyvals[i+1]
+
+		// Only attempt to vectorise string keys
+		if k, ok := key.(string); ok {
+			if valueIndices[k] == nil {
+				// Record that this key has been seen once
+				valueIndices[k] = &vectorValueindex{
+					valueIndex: i + 1 - elided,
+				}
+				// Copy the key-value to output with the single value
+				outputKeyvals = append(outputKeyvals, key, val)
+			} else {
+				// We have seen this key before
+				vi := valueIndices[k]
+				if !vi.vector {
+					// This must be the only second occurrence of the key so now vectorise the value
+					outputKeyvals[vi.valueIndex] = []interface{}{outputKeyvals[vi.valueIndex]}
+					vi.vector = true
+				}
+				// Grow the vector value
+				outputKeyvals[vi.valueIndex] = append(outputKeyvals[vi.valueIndex].([]interface{}), val)
+				// We are now running two more elements behind the input keyvals because we have absorbed this key-value pair
+				elided += 2
+			}
+		} else {
+			// Just copy the key-value to the output for non-string keys
+			outputKeyvals = append(outputKeyvals, key, val)
+		}
+	}
+	return outputKeyvals
+}
+
+// Return a single value corresponding to key in keyvals
+func Value(keyvals []interface{}, key interface{}) interface{} {
+	for i := 0; i < 2*(len(keyvals)/2); i += 2 {
+		if keyvals[i] == key {
+			return keyvals[i+1]
+		}
+	}
+	return nil
+}
+
+// Obtain a canonical key from a value. Useful for structured logging where the
+// type of value alone may be sufficient to determine its key. Providing this
+// function centralises any convention over type names
+func KeyFromValue(val interface{}) string {
+	switch val.(type) {
+	case string:
+		return "text"
+	default:
+		return reflect.TypeOf(val).Name()
+	}
+}
+
+// Maps key values pairs with a function (key, value) -> (new key, new value)
+func MapKeyValues(keyvals []interface{}, fn func(interface{}, interface{}) (interface{}, interface{})) []interface{} {
+	mappedKeyvals := make([]interface{}, len(keyvals))
+	for i := 0; i < 2*(len(keyvals)/2); i += 2 {
+		key := keyvals[i]
+		val := keyvals[i+1]
+		mappedKeyvals[i], mappedKeyvals[i+1] = fn(key, val)
+	}
+	return mappedKeyvals
+}
diff --git a/logging/structure/structure_test.go b/logging/structure/structure_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..fde15ff4ae247a02166d91fe7850328c01417e33
--- /dev/null
+++ b/logging/structure/structure_test.go
@@ -0,0 +1,36 @@
+package structure
+
+import (
+	"testing"
+
+	. "github.com/eris-ltd/eris-db/util/slice"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestValuesAndContext(t *testing.T) {
+	keyvals := Slice("hello", 1, "dog", 2, "fish", 3, "fork", 5)
+	vals, ctx := ValuesAndContext(keyvals, "hello", "fish")
+	assert.Equal(t, map[interface{}]interface{}{"hello": 1, "fish": 3}, vals)
+	assert.Equal(t, Slice("dog", 2, "fork", 5), ctx)
+}
+
+func TestVectorise(t *testing.T) {
+	kvs := Slice(
+		"scope", "lawnmower",
+		"hub", "budub",
+		"occupation", "fish brewer",
+		"scope", "hose pipe",
+		"flub", "dub",
+		"scope", "rake",
+		"flub", "brub",
+	)
+
+	kvsVector := Vectorise(kvs, "occupation", "scope")
+	assert.Equal(t, Slice(
+		"scope", Slice("lawnmower", "hose pipe", "rake"),
+		"hub", "budub",
+		"occupation", "fish brewer",
+		"flub", Slice("dub", "brub"),
+	),
+		kvsVector)
+}
diff --git a/logging/terminal.go b/logging/terminal.go
new file mode 100644
index 0000000000000000000000000000000000000000..e8eea7d8107484f1f5bd79a745f042e77fa49e8f
--- /dev/null
+++ b/logging/terminal.go
@@ -0,0 +1,29 @@
+package logging
+
+import (
+	"github.com/eris-ltd/eris-db/logging/structure"
+	"github.com/go-kit/kit/log/term"
+)
+
+func Colors(keyvals ...interface{}) term.FgBgColor {
+	for i := 0; i < len(keyvals)-1; i += 2 {
+		if keyvals[i] != structure.LevelKey {
+			continue
+		}
+		switch keyvals[i+1] {
+		case "debug":
+			return term.FgBgColor{Fg: term.DarkGray}
+		case "info":
+			return term.FgBgColor{Fg: term.Gray}
+		case "warn":
+			return term.FgBgColor{Fg: term.Yellow}
+		case "error":
+			return term.FgBgColor{Fg: term.Red}
+		case "crit":
+			return term.FgBgColor{Fg: term.Gray, Bg: term.DarkRed}
+		default:
+			return term.FgBgColor{}
+		}
+	}
+	return term.FgBgColor{}
+}
diff --git a/manager/eris-mint/eris-mint.go b/manager/eris-mint/eris-mint.go
index 2c08757c1a2b6539992a51bd1eb22c6043b1ea04..90f9af757bd1548845b1e304e7953a12289f6948 100644
--- a/manager/eris-mint/eris-mint.go
+++ b/manager/eris-mint/eris-mint.go
@@ -25,7 +25,8 @@ import (
 	wire "github.com/tendermint/go-wire"
 	tmsp "github.com/tendermint/tmsp/types"
 
-	log "github.com/eris-ltd/eris-logger"
+	"github.com/eris-ltd/eris-db/logging"
+	"github.com/eris-ltd/eris-db/logging/loggers"
 
 	sm "github.com/eris-ltd/eris-db/manager/eris-mint/state"
 	manager_types "github.com/eris-ltd/eris-db/manager/types"
@@ -47,7 +48,8 @@ type ErisMint struct {
 	evc  *tendermint_events.EventCache
 	evsw *tendermint_events.EventSwitch
 
-	nTxs int // count txs in a block
+	nTxs   int // count txs in a block
+	logger loggers.InfoTraceLogger
 }
 
 // NOTE [ben] Compiler check to ensure ErisMint successfully implements
@@ -71,13 +73,14 @@ func (app *ErisMint) GetCheckCache() *sm.BlockCache {
 	return app.checkCache
 }
 
-func NewErisMint(s *sm.State, evsw *tendermint_events.EventSwitch) *ErisMint {
+func NewErisMint(s *sm.State, evsw *tendermint_events.EventSwitch, logger loggers.InfoTraceLogger) *ErisMint {
 	return &ErisMint{
 		state:      s,
 		cache:      sm.NewBlockCache(s),
 		checkCache: sm.NewBlockCache(s),
 		evc:        tendermint_events.NewEventCache(evsw),
 		evsw:       evsw,
+		logger:     logging.WithScope(logger, "ErisMint"),
 	}
 }
 
@@ -145,18 +148,17 @@ func (app *ErisMint) Commit() (res tmsp.Result) {
 	defer app.mtx.Unlock()
 
 	app.state.LastBlockHeight += 1
-	log.WithFields(log.Fields{
-		"blockheight": app.state.LastBlockHeight,
-	}).Info("Commit block")
+	logging.InfoMsg(app.logger, "Committing block",
+		"last_block_height", app.state.LastBlockHeight)
 
 	// sync the AppendTx cache
 	app.cache.Sync()
 
 	// Refresh the checkCache with the latest commited state
-	log.WithFields(log.Fields{
-		"txs": app.nTxs,
-	}).Info("Reset checkCache")
+	logging.InfoMsg(app.logger, "Resetting checkCache",
+		"txs", app.nTxs)
 	app.checkCache = sm.NewBlockCache(app.state)
+
 	app.nTxs = 0
 
 	// save state to disk
diff --git a/manager/eris-mint/pipe.go b/manager/eris-mint/pipe.go
index dc9b64875319a7fb50c6fb6c269778f2d9272d5a..014de66207a1c2b0f27de336016d15291f9c8de6 100644
--- a/manager/eris-mint/pipe.go
+++ b/manager/eris-mint/pipe.go
@@ -28,8 +28,6 @@ import (
 	tm_types "github.com/tendermint/tendermint/types"
 	tmsp_types "github.com/tendermint/tmsp/types"
 
-	log "github.com/eris-ltd/eris-logger"
-
 	"github.com/eris-ltd/eris-db/account"
 	blockchain_types "github.com/eris-ltd/eris-db/blockchain/types"
 	imath "github.com/eris-ltd/eris-db/common/math/integral"
@@ -38,9 +36,11 @@ import (
 	core_types "github.com/eris-ltd/eris-db/core/types"
 	"github.com/eris-ltd/eris-db/definitions"
 	edb_event "github.com/eris-ltd/eris-db/event"
+	genesis "github.com/eris-ltd/eris-db/genesis"
+	"github.com/eris-ltd/eris-db/logging"
+	"github.com/eris-ltd/eris-db/logging/loggers"
 	vm "github.com/eris-ltd/eris-db/manager/eris-mint/evm"
 	"github.com/eris-ltd/eris-db/manager/eris-mint/state"
-	state_types "github.com/eris-ltd/eris-db/manager/eris-mint/state/types"
 	manager_types "github.com/eris-ltd/eris-db/manager/types"
 	rpc_tm_types "github.com/eris-ltd/eris-db/rpc/tendermint/core/types"
 	"github.com/eris-ltd/eris-db/txs"
@@ -57,8 +57,9 @@ type erisMintPipe struct {
 	namereg         *namereg
 	transactor      *transactor
 	// Genesis cache
-	genesisDoc   *state_types.GenesisDoc
+	genesisDoc   *genesis.GenesisDoc
 	genesisState *state.State
+	logger       loggers.InfoTraceLogger
 }
 
 // NOTE [ben] Compiler check to ensure erisMintPipe successfully implements
@@ -70,7 +71,8 @@ var _ definitions.Pipe = (*erisMintPipe)(nil)
 var _ definitions.TendermintPipe = (*erisMintPipe)(nil)
 
 func NewErisMintPipe(moduleConfig *config.ModuleConfig,
-	eventSwitch *go_events.EventSwitch) (*erisMintPipe, error) {
+	eventSwitch *go_events.EventSwitch,
+	logger loggers.InfoTraceLogger) (*erisMintPipe, error) {
 
 	startedState, genesisDoc, err := startState(moduleConfig.DataDir,
 		moduleConfig.Config.GetString("db_backend"), moduleConfig.GenesisFile,
@@ -78,17 +80,17 @@ func NewErisMintPipe(moduleConfig *config.ModuleConfig,
 	if err != nil {
 		return nil, fmt.Errorf("Failed to start state: %v", err)
 	}
+	logger = logging.WithScope(logger, "ErisMintPipe")
 	// assert ChainId matches genesis ChainId
-	log.WithFields(log.Fields{
-		"chainId":         startedState.ChainID,
-		"lastBlockHeight": startedState.LastBlockHeight,
-		"lastBlockHash":   startedState.LastBlockHash,
-	}).Debug("Loaded state")
+	logging.InfoMsg(logger, "Loaded state",
+		"chainId", startedState.ChainID,
+		"lastBlockHeight", startedState.LastBlockHeight,
+		"lastBlockHash", startedState.LastBlockHash)
 	// start the application
-	erisMint := NewErisMint(startedState, eventSwitch)
+	erisMint := NewErisMint(startedState, eventSwitch, logger)
 
 	// initialise the components of the pipe
-	events := edb_event.NewEvents(eventSwitch)
+	events := edb_event.NewEvents(eventSwitch, logger)
 	accounts := newAccounts(erisMint)
 	namereg := newNameReg(erisMint)
 
@@ -108,6 +110,7 @@ func NewErisMintPipe(moduleConfig *config.ModuleConfig,
 		// authority - this is a sort of dependency injection pattern
 		consensusEngine: nil,
 		blockchain:      nil,
+		logger:          logger,
 	}
 
 	// NOTE: [Silas]
@@ -139,7 +142,7 @@ func NewErisMintPipe(moduleConfig *config.ModuleConfig,
 // If no state can be loaded, the JSON genesis file will be loaded into the
 // state database as the zero state.
 func startState(dataDir, backend, genesisFile, chainId string) (*state.State,
-	*state_types.GenesisDoc, error) {
+	*genesis.GenesisDoc, error) {
 	// avoid Tendermints PanicSanity and return a clean error
 	if backend != db.DBBackendMemDB &&
 		backend != db.DBBackendLevelDB {
@@ -149,18 +152,18 @@ func startState(dataDir, backend, genesisFile, chainId string) (*state.State,
 
 	stateDB := db.NewDB("erismint", backend, dataDir)
 	newState := state.LoadState(stateDB)
-	var genesisDoc *state_types.GenesisDoc
+	var genesisDoc *genesis.GenesisDoc
 	if newState == nil {
 		genesisDoc, newState = state.MakeGenesisStateFromFile(stateDB, genesisFile)
 		newState.Save()
 		buf, n, err := new(bytes.Buffer), new(int), new(error)
 		wire.WriteJSON(genesisDoc, buf, n, err)
-		stateDB.Set(state_types.GenDocKey, buf.Bytes())
+		stateDB.Set(genesis.GenDocKey, buf.Bytes())
 		if *err != nil {
 			return nil, nil, fmt.Errorf("Unable to write genesisDoc to db: %v", err)
 		}
 	} else {
-		loadedGenesisDocBytes := stateDB.Get(state_types.GenDocKey)
+		loadedGenesisDocBytes := stateDB.Get(genesis.GenDocKey)
 		err := new(error)
 		wire.ReadJSONPtr(&genesisDoc, loadedGenesisDocBytes, err)
 		if *err != nil {
@@ -168,12 +171,8 @@ func startState(dataDir, backend, genesisFile, chainId string) (*state.State,
 		}
 		// assert loaded genesis doc has the same chainId as the provided chainId
 		if genesisDoc.ChainID != chainId {
-			log.WithFields(log.Fields{
-				"chainId from loaded genesis": genesisDoc.ChainID,
-				"chainId from configuration":  chainId,
-			}).Warn("Conflicting chainIds")
-			// return nil, nil, fmt.Errorf("ChainId (%s) loaded from genesis document in existing database does not match configuration chainId (%s).",
-			// genesisDoc.ChainID, chainId)
+			return nil, nil, fmt.Errorf("ChainId (%s) loaded from genesis document in existing database does not match"+
+				" configuration chainId (%s).", genesisDoc.ChainID, chainId)
 		}
 	}
 
@@ -183,6 +182,10 @@ func startState(dataDir, backend, genesisFile, chainId string) (*state.State,
 //------------------------------------------------------------------------------
 // Implement definitions.Pipe for erisMintPipe
 
+func (pipe *erisMintPipe) Logger() loggers.InfoTraceLogger {
+	return pipe.logger
+}
+
 func (pipe *erisMintPipe) Accounts() definitions.Accounts {
 	return pipe.accounts
 }
@@ -253,11 +256,9 @@ func (pipe *erisMintPipe) Subscribe(event string,
 	subscriptionId, err := edb_event.GenerateSubId()
 	if err != nil {
 		return nil, err
+		logging.InfoMsg(pipe.logger, "Subscribing to event",
+			"event", event, "subscriptionId", subscriptionId)
 	}
-
-	log.WithFields(log.Fields{"event": event, "subscriptionId": subscriptionId}).
-		Info("Subscribing to event")
-
 	pipe.consensusAndManagerEvents().Subscribe(subscriptionId, event,
 		func(eventData txs.EventData) {
 			result := rpc_tm_types.ErisDBResult(&rpc_tm_types.ResultEvent{event,
@@ -272,8 +273,8 @@ func (pipe *erisMintPipe) Subscribe(event string,
 }
 
 func (pipe *erisMintPipe) Unsubscribe(subscriptionId string) (*rpc_tm_types.ResultUnsubscribe, error) {
-	log.WithFields(log.Fields{"subscriptionId": subscriptionId}).
-		Info("Unsubscribing from event")
+	logging.InfoMsg(pipe.logger, "Unsubscribing from event",
+		"subscriptionId", subscriptionId)
 	pipe.consensusAndManagerEvents().Unsubscribe(subscriptionId)
 	return &rpc_tm_types.ResultUnsubscribe{SubscriptionId: subscriptionId}, nil
 }
@@ -351,13 +352,8 @@ func (pipe *erisMintPipe) Genesis() (*rpc_tm_types.ResultGenesis, error) {
 func (pipe *erisMintPipe) GetAccount(address []byte) (*rpc_tm_types.ResultGetAccount,
 	error) {
 	cache := pipe.erisMint.GetCheckCache()
-	// cache := mempoolReactor.Mempool.GetCache()
 	account := cache.GetAccount(address)
-	if account == nil {
-		log.Warn("Nil Account")
-		return &rpc_tm_types.ResultGetAccount{nil}, nil
-	}
-	return &rpc_tm_types.ResultGetAccount{account}, nil
+	return &rpc_tm_types.ResultGetAccount{Account: account}, nil
 }
 
 func (pipe *erisMintPipe) ListAccounts() (*rpc_tm_types.ResultListAccounts, error) {
@@ -589,10 +585,11 @@ func (pipe *erisMintPipe) BroadcastTxSync(tx txs.Tx) (*rpc_tm_types.ResultBroadc
 	case tmsp_types.CodeType_InternalError:
 		return resultBroadCastTx, fmt.Errorf(resultBroadCastTx.Log)
 	default:
-		log.WithFields(log.Fields{
-			"application":    GetErisMintVersion().GetVersionString(),
-			"TMSP_code_type": responseCheckTx.Code,
-		}).Warn("Unknown error returned from Tendermint CheckTx on BroadcastTxSync")
+		logging.InfoMsg(pipe.logger, "Unknown error returned from Tendermint CheckTx on BroadcastTxSync",
+			"application", GetErisMintVersion().GetVersionString(),
+			"TMSP_code_type", responseCheckTx.Code,
+			"TMSP_log", responseCheckTx.Log,
+		)
 		return resultBroadCastTx, fmt.Errorf("Unknown error returned: " + responseCheckTx.Log)
 	}
 }
diff --git a/manager/eris-mint/state/execution.go b/manager/eris-mint/state/execution.go
index ceefbbd8368a2491e1a743f87b823fd40558935e..9d9fd85bd3f85fc9d0cd2c50402b86a3376d98cf 100644
--- a/manager/eris-mint/state/execution.go
+++ b/manager/eris-mint/state/execution.go
@@ -203,6 +203,12 @@ func getOrMakeOutputs(state AccountGetter, accounts map[string]*acm.Account, out
 	return accounts, nil
 }
 
+// Since all ethereum accounts implicitly exist we sometimes lazily create an Account object to represent them
+// only when needed. Sometimes we need to create an unknown Account knowing only its address (which is expected to
+// be a deterministic hash of its associated public key) and not its public key. When we eventually receive a
+// transaction acting on behalf of that account we will be given a public key that we can check matches the address.
+// If it does then we will associate the public key with the stub account already registered in the system once and
+// for all time.
 func checkInputPubKey(acc *acm.Account, in *txs.TxInput) error {
 	if acc.PubKey == nil {
 		if in.PubKey == nil {
diff --git a/manager/eris-mint/state/genesis_test.go b/manager/eris-mint/state/genesis_test.go
index 73e7f92f4d4ad04f135f1270f6173dc5cae61832..3bc2fdf005fc50686e698ef061a1340def5e8619 100644
--- a/manager/eris-mint/state/genesis_test.go
+++ b/manager/eris-mint/state/genesis_test.go
@@ -9,7 +9,7 @@ import (
 	"time"
 
 	acm "github.com/eris-ltd/eris-db/account"
-	. "github.com/eris-ltd/eris-db/manager/eris-mint/state/types"
+	genesis "github.com/eris-ltd/eris-db/genesis"
 	ptypes "github.com/eris-ltd/eris-db/permission/types"
 
 	. "github.com/tendermint/go-common"
@@ -60,7 +60,7 @@ var g1 = fmt.Sprintf(`
 `, chain_id, addr1, amt1, accName, perms, setbit, roles1[0], roles1[1])
 
 func TestGenesisReadable(t *testing.T) {
-	genDoc := GenesisDocFromJSON([]byte(g1))
+	genDoc := genesis.GenesisDocFromJSON([]byte(g1))
 	if genDoc.ChainID != chain_id {
 		t.Fatalf("Incorrect chain id. Got %d, expected %d\n", genDoc.ChainID, chain_id)
 	}
@@ -82,7 +82,7 @@ func TestGenesisReadable(t *testing.T) {
 }
 
 func TestGenesisMakeState(t *testing.T) {
-	genDoc := GenesisDocFromJSON([]byte(g1))
+	genDoc := genesis.GenesisDocFromJSON([]byte(g1))
 	db := tdb.NewMemDB()
 	st := MakeGenesisState(db, genDoc)
 	acc := st.GetAccount(addr1)
@@ -118,27 +118,27 @@ func RandAccount(randBalance bool, minBalance int64) (*acm.Account, *acm.PrivAcc
 	return acc, privAccount
 }
 
-func RandGenesisDoc(numAccounts int, randBalance bool, minBalance int64, numValidators int, randBonded bool, minBonded int64) (*GenesisDoc, []*acm.PrivAccount, []*types.PrivValidator) {
-	accounts := make([]GenesisAccount, numAccounts)
+func RandGenesisDoc(numAccounts int, randBalance bool, minBalance int64, numValidators int, randBonded bool, minBonded int64) (*genesis.GenesisDoc, []*acm.PrivAccount, []*types.PrivValidator) {
+	accounts := make([]genesis.GenesisAccount, numAccounts)
 	privAccounts := make([]*acm.PrivAccount, numAccounts)
 	defaultPerms := ptypes.DefaultAccountPermissions
 	for i := 0; i < numAccounts; i++ {
 		account, privAccount := RandAccount(randBalance, minBalance)
-		accounts[i] = GenesisAccount{
+		accounts[i] = genesis.GenesisAccount{
 			Address:     account.Address,
 			Amount:      account.Balance,
 			Permissions: &defaultPerms, // This will get copied into each state.Account.
 		}
 		privAccounts[i] = privAccount
 	}
-	validators := make([]GenesisValidator, numValidators)
+	validators := make([]genesis.GenesisValidator, numValidators)
 	privValidators := make([]*types.PrivValidator, numValidators)
 	for i := 0; i < numValidators; i++ {
 		valInfo, privVal := types.RandValidator(randBonded, minBonded)
-		validators[i] = GenesisValidator{
+		validators[i] = genesis.GenesisValidator{
 			PubKey: valInfo.PubKey,
 			Amount: valInfo.VotingPower,
-			UnbondTo: []BasicAccount{
+			UnbondTo: []genesis.BasicAccount{
 				{
 					Address: valInfo.PubKey.Address(),
 					Amount:  valInfo.VotingPower,
@@ -148,7 +148,7 @@ func RandGenesisDoc(numAccounts int, randBalance bool, minBalance int64, numVali
 		privValidators[i] = privVal
 	}
 	sort.Sort(types.PrivValidatorsByAddress(privValidators))
-	return &GenesisDoc{
+	return &genesis.GenesisDoc{
 		GenesisTime: time.Now(),
 		ChainID:     "tendermint_test",
 		Accounts:    accounts,
diff --git a/manager/eris-mint/state/permissions_test.go b/manager/eris-mint/state/permissions_test.go
index 523a5ffc8b2a75a13749c8ba924d4c6ded2e824a..8632313647c5ef8a0d03a1f6716c661f8b75274c 100644
--- a/manager/eris-mint/state/permissions_test.go
+++ b/manager/eris-mint/state/permissions_test.go
@@ -9,8 +9,8 @@ import (
 	"time"
 
 	acm "github.com/eris-ltd/eris-db/account"
+	genesis "github.com/eris-ltd/eris-db/genesis"
 	"github.com/eris-ltd/eris-db/manager/eris-mint/evm"
-	. "github.com/eris-ltd/eris-db/manager/eris-mint/state/types"
 	ptypes "github.com/eris-ltd/eris-db/permission/types"
 	"github.com/eris-ltd/eris-db/txs"
 
@@ -111,30 +111,30 @@ var (
 	PermsAllFalse = ptypes.ZeroAccountPermissions
 )
 
-func newBaseGenDoc(globalPerm, accountPerm ptypes.AccountPermissions) GenesisDoc {
-	genAccounts := []GenesisAccount{}
+func newBaseGenDoc(globalPerm, accountPerm ptypes.AccountPermissions) genesis.GenesisDoc {
+	genAccounts := []genesis.GenesisAccount{}
 	for _, u := range user[:5] {
 		accountPermCopy := accountPerm // Create new instance for custom overridability.
-		genAccounts = append(genAccounts, GenesisAccount{
+		genAccounts = append(genAccounts, genesis.GenesisAccount{
 			Address:     u.Address,
 			Amount:      1000000,
 			Permissions: &accountPermCopy,
 		})
 	}
 
-	return GenesisDoc{
+	return genesis.GenesisDoc{
 		GenesisTime: time.Now(),
 		ChainID:     chainID,
-		Params: &GenesisParams{
+		Params: &genesis.GenesisParams{
 			GlobalPermissions: &globalPerm,
 		},
 		Accounts: genAccounts,
-		Validators: []GenesisValidator{
-			GenesisValidator{
+		Validators: []genesis.GenesisValidator{
+			genesis.GenesisValidator{
 				PubKey: user[0].PubKey.(crypto.PubKeyEd25519),
 				Amount: 10,
-				UnbondTo: []BasicAccount{
-					BasicAccount{
+				UnbondTo: []genesis.BasicAccount{
+					genesis.BasicAccount{
 						Address: user[0].Address,
 					},
 				},
diff --git a/manager/eris-mint/state/state.go b/manager/eris-mint/state/state.go
index a36bc8ec2f607eaa505fcf8b1b7ad66c04a44033..082971acde2828e6534f04efff6cba38dd048db2 100644
--- a/manager/eris-mint/state/state.go
+++ b/manager/eris-mint/state/state.go
@@ -8,17 +8,17 @@ import (
 	"time"
 
 	acm "github.com/eris-ltd/eris-db/account"
-	. "github.com/eris-ltd/eris-db/manager/eris-mint/state/types"
+	genesis "github.com/eris-ltd/eris-db/genesis"
 	ptypes "github.com/eris-ltd/eris-db/permission/types"
 	"github.com/eris-ltd/eris-db/txs"
 
-	. "github.com/tendermint/go-common"
 	dbm "github.com/tendermint/go-db"
 	"github.com/tendermint/go-events"
 	"github.com/tendermint/go-merkle"
 	"github.com/tendermint/go-wire"
 
 	core_types "github.com/eris-ltd/eris-db/core/types"
+	"github.com/eris-ltd/eris-db/util"
 	"github.com/tendermint/tendermint/types"
 )
 
@@ -77,7 +77,7 @@ func LoadState(db dbm.DB) *State {
 		s.nameReg.Load(nameRegHash)
 		if *err != nil {
 			// DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
-			Exit(Fmt("Data has been corrupted or its spec has changed: %v\n", *err))
+			util.Fatalf("Data has been corrupted or its spec has changed: %v\n", *err)
 		}
 		// TODO: ensure that buf is completely read.
 	}
@@ -101,7 +101,10 @@ func (s *State) Save() {
 	//wire.WriteByteSlice(s.validatorInfos.Hash(), buf, n, err)
 	wire.WriteByteSlice(s.nameReg.Hash(), buf, n, err)
 	if *err != nil {
-		PanicCrisis(*err)
+		// TODO: [Silas] Do something better than this, really serialising ought to
+		// be error-free
+		util.Fatalf("Could not serialise state in order to save the state, "+
+			"cannot continue, error: %s", *err)
 	}
 	s.DB.Set(stateKey, buf.Bytes())
 }
@@ -153,9 +156,9 @@ func (s *State) ComputeBlockStateHash(block *types.Block) error {
 }
 */
 
-func (s *State) GetGenesisDoc() (*GenesisDoc, error) {
-	var genesisDoc *GenesisDoc
-	loadedGenesisDocBytes := s.DB.Get(GenDocKey)
+func (s *State) GetGenesisDoc() (*genesis.GenesisDoc, error) {
+	var genesisDoc *genesis.GenesisDoc
+	loadedGenesisDocBytes := s.DB.Get(genesis.GenDocKey)
 	err := new(error)
 	wire.ReadJSONPtr(&genesisDoc, loadedGenesisDocBytes, err)
 	if *err != nil {
@@ -398,18 +401,18 @@ func (s *State) SetFireable(evc events.Fireable) {
 //-----------------------------------------------------------------------------
 // Genesis
 
-func MakeGenesisStateFromFile(db dbm.DB, genDocFile string) (*GenesisDoc, *State) {
+func MakeGenesisStateFromFile(db dbm.DB, genDocFile string) (*genesis.GenesisDoc, *State) {
 	jsonBlob, err := ioutil.ReadFile(genDocFile)
 	if err != nil {
-		Exit(Fmt("Couldn't read GenesisDoc file: %v", err))
+		util.Fatalf("Couldn't read GenesisDoc file: %v", err)
 	}
-	genDoc := GenesisDocFromJSON(jsonBlob)
+	genDoc := genesis.GenesisDocFromJSON(jsonBlob)
 	return genDoc, MakeGenesisState(db, genDoc)
 }
 
-func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State {
+func MakeGenesisState(db dbm.DB, genDoc *genesis.GenesisDoc) *State {
 	if len(genDoc.Validators) == 0 {
-		Exit(Fmt("The genesis file has no validators"))
+		util.Fatalf("The genesis file has no validators")
 	}
 
 	if genDoc.GenesisTime.IsZero() {
diff --git a/manager/manager.go b/manager/manager.go
index 406648cd3ef36bc56857a55689c3b233870e633a..6f71aefa92dfe857cc4bf8d65b9c3cb22fae6eac 100644
--- a/manager/manager.go
+++ b/manager/manager.go
@@ -21,12 +21,13 @@ import (
 
 	events "github.com/tendermint/go-events"
 
-	log "github.com/eris-ltd/eris-logger"
-
 	config "github.com/eris-ltd/eris-db/config"
 	definitions "github.com/eris-ltd/eris-db/definitions"
 	erismint "github.com/eris-ltd/eris-db/manager/eris-mint"
 	// types       "github.com/eris-ltd/eris-db/manager/types"
+
+	"github.com/eris-ltd/eris-db/logging"
+	"github.com/eris-ltd/eris-db/logging/loggers"
 )
 
 // NewApplicationPipe returns an initialised Pipe interface
@@ -35,18 +36,18 @@ import (
 // of an application.  It is feasible this will be insufficient to support
 // different types of applications later down the line.
 func NewApplicationPipe(moduleConfig *config.ModuleConfig,
-	evsw *events.EventSwitch, consensusMinorVersion string) (definitions.Pipe,
+	evsw *events.EventSwitch, logger loggers.InfoTraceLogger,
+	consensusMinorVersion string) (definitions.Pipe,
 	error) {
 	switch moduleConfig.Name {
 	case "erismint":
 		if err := erismint.AssertCompatibleConsensus(consensusMinorVersion); err != nil {
 			return nil, err
 		}
-		log.WithFields(log.Fields{
-			"compatibleConsensus": consensusMinorVersion,
-			"erisMintVersion":     erismint.GetErisMintVersion().GetVersionString(),
-		}).Debug("Loading ErisMint")
-		return erismint.NewErisMintPipe(moduleConfig, evsw)
+		logging.InfoMsg(logger, "Loading ErisMint",
+			"compatibleConsensus", consensusMinorVersion,
+			"erisMintVersion", erismint.GetErisMintVersion().GetVersionString())
+		return erismint.NewErisMintPipe(moduleConfig, evsw, logger)
 	}
 	return nil, fmt.Errorf("Failed to return Pipe for %s", moduleConfig.Name)
 }
diff --git a/rpc/tendermint/client/client_test.go b/rpc/tendermint/client/client_test.go
index 2580508d828b88b457cc683f78a21c145aee5e9b..3fe36f9795c6fe4fd68b6935ba33bf73f690b2b4 100644
--- a/rpc/tendermint/client/client_test.go
+++ b/rpc/tendermint/client/client_test.go
@@ -1,8 +1,9 @@
 package client
 
 import (
-	"github.com/stretchr/testify/assert"
 	"testing"
+
+	"github.com/stretchr/testify/assert"
 )
 
 func TestMapsAndValues(t *testing.T) {
@@ -30,5 +31,4 @@ func TestMapsAndValues(t *testing.T) {
 
 	_, _, err = mapAndValues("Foo", 4, 4, "Bar")
 	assert.Error(t, err, "Should be an error to provide non-string keys")
-
 }
diff --git a/rpc/tendermint/core/types/responses.go b/rpc/tendermint/core/types/responses.go
index c6494d919f3868507d562eeb3c0f0c30aa831f2e..98b72fbbd200d120c46c834a02d5cc237ff6b5a4 100644
--- a/rpc/tendermint/core/types/responses.go
+++ b/rpc/tendermint/core/types/responses.go
@@ -3,7 +3,7 @@ package types
 import (
 	acm "github.com/eris-ltd/eris-db/account"
 	core_types "github.com/eris-ltd/eris-db/core/types"
-	stypes "github.com/eris-ltd/eris-db/manager/eris-mint/state/types"
+	genesis "github.com/eris-ltd/eris-db/genesis"
 	"github.com/eris-ltd/eris-db/txs"
 	tendermint_types "github.com/tendermint/tendermint/types"
 
@@ -126,7 +126,7 @@ type ResultGetName struct {
 }
 
 type ResultGenesis struct {
-	Genesis *stypes.GenesisDoc `json:"genesis"`
+	Genesis *genesis.GenesisDoc `json:"genesis"`
 }
 
 type ResultSignTx struct {
diff --git a/rpc/tendermint/test/common.go b/rpc/tendermint/test/common.go
index 76909182df98801eab3e08db5b5c4bbfd03617cf..a060833c5091b00156e855c70a276753115d925f 100644
--- a/rpc/tendermint/test/common.go
+++ b/rpc/tendermint/test/common.go
@@ -6,6 +6,7 @@ package test
 import (
 	"fmt"
 
+	vm "github.com/eris-ltd/eris-db/manager/eris-mint/evm"
 	rpc_core "github.com/eris-ltd/eris-db/rpc/tendermint/core"
 	"github.com/eris-ltd/eris-db/test/fixtures"
 )
@@ -17,6 +18,7 @@ func TestWrapper(runner func() int) int {
 
 	defer ffs.RemoveAll()
 
+	vm.SetDebug(true)
 	err := initGlobalVariables(ffs)
 
 	if err != nil {
diff --git a/rpc/tendermint/test/shared.go b/rpc/tendermint/test/shared.go
index d211bd5d11c6fd7c078ce12665366f0a48d0a134..8c01b6a2f024d402cbea6dc1e7cea5f54dbf7f7e 100644
--- a/rpc/tendermint/test/shared.go
+++ b/rpc/tendermint/test/shared.go
@@ -21,6 +21,7 @@ import (
 
 	"path"
 
+	"github.com/eris-ltd/eris-db/logging/lifecycle"
 	state_types "github.com/eris-ltd/eris-db/manager/eris-mint/state/types"
 	"github.com/spf13/viper"
 	tm_common "github.com/tendermint/go-common"
@@ -83,7 +84,14 @@ func initGlobalVariables(ffs *fixtures.FileFixtures) error {
 	// Set up priv_validator.json before we start tendermint (otherwise it will
 	// create its own one.
 	saveNewPriv()
-	testCore, err = core.NewCore("testCore", consensusConfig, managerConfig)
+	logger := lifecycle.NewStdErrLogger()
+	// To spill tendermint logs on the floor:
+	// lifecycle.CaptureTendermintLog15Output(loggers.NewNoopInfoTraceLogger())
+	lifecycle.CaptureTendermintLog15Output(logger)
+	lifecycle.CaptureStdlibLogOutput(logger)
+
+	testCore, err = core.NewCore("testCore", consensusConfig, managerConfig,
+		logger)
 	if err != nil {
 		return err
 	}
diff --git a/rpc/v0/json_service.go b/rpc/v0/json_service.go
index 4c43f533432fdd1e2da6c1dc5f46233ede862069..ab4ce33f60b0be23622fc83b594d5005d48745f9 100644
--- a/rpc/v0/json_service.go
+++ b/rpc/v0/json_service.go
@@ -6,8 +6,6 @@ import (
 
 	"github.com/gin-gonic/gin"
 
-	log "github.com/eris-ltd/eris-logger"
-
 	definitions "github.com/eris-ltd/eris-db/definitions"
 	event "github.com/eris-ltd/eris-db/event"
 	rpc "github.com/eris-ltd/eris-db/rpc"
@@ -125,10 +123,8 @@ func (this *ErisDbJsonService) writeError(msg, id string, code int, w http.Respo
 
 // Helper for writing responses.
 func (this *ErisDbJsonService) writeResponse(id string, result interface{}, w http.ResponseWriter) {
-	log.Debug("Result: %v\n", result)
 	response := rpc.NewRPCResponse(id, result)
 	err := this.codec.Encode(response, w)
-	log.Debug("Response: %v\n", response)
 	if err != nil {
 		this.writeError("Internal error: "+err.Error(), id, rpc.INTERNAL_ERROR, w)
 		return
diff --git a/rpc/v0/wsService.go b/rpc/v0/wsService.go
index 35705ed39c179ded02944c931580f04e57d51d96..c7e89ff5b5950ea0900471ed971dd5f26c6c23f8 100644
--- a/rpc/v0/wsService.go
+++ b/rpc/v0/wsService.go
@@ -4,8 +4,6 @@ import (
 	"encoding/json"
 	"fmt"
 
-	log "github.com/eris-ltd/eris-logger"
-
 	definitions "github.com/eris-ltd/eris-db/definitions"
 	"github.com/eris-ltd/eris-db/event"
 	rpc "github.com/eris-ltd/eris-db/rpc"
@@ -36,7 +34,6 @@ func NewErisDbWsService(codec rpc.Codec,
 
 // Process a request.
 func (this *ErisDbWsService) Process(msg []byte, session *server.WSSession) {
-	log.Debug("REQUEST: %s\n", string(msg))
 	// Create new request object and unmarshal.
 	req := &rpc.RPCRequest{}
 	errU := json.Unmarshal(msg, req)
@@ -84,7 +81,6 @@ func (this *ErisDbWsService) writeResponse(id string, result interface{},
 	session *server.WSSession) error {
 	response := rpc.NewRPCResponse(id, result)
 	bts, err := this.codec.EncodeBytes(response)
-	log.Debug("RESPONSE: %v\n", response)
 	if err != nil {
 		this.writeError("Internal error: "+err.Error(), id, rpc.INTERNAL_ERROR, session)
 		return err
diff --git a/test/mock/pipe.go b/test/mock/pipe.go
index e279ad18253e2fc755f9948d8ec5108d89588a41..ecd245ec87e2c9934e360ae72869210e134ee820 100644
--- a/test/mock/pipe.go
+++ b/test/mock/pipe.go
@@ -14,6 +14,7 @@ import (
 	td "github.com/eris-ltd/eris-db/test/testdata/testdata"
 	"github.com/eris-ltd/eris-db/txs"
 
+	"github.com/eris-ltd/eris-db/logging/loggers"
 	"github.com/tendermint/go-crypto"
 	"github.com/tendermint/go-p2p"
 	mintTypes "github.com/tendermint/tendermint/types"
@@ -29,24 +30,20 @@ type MockPipe struct {
 	events          event.EventEmitter
 	namereg         definitions.NameReg
 	transactor      definitions.Transactor
+	logger          loggers.InfoTraceLogger
 }
 
 // Create a new mock tendermint pipe.
 func NewMockPipe(td *td.TestData) definitions.Pipe {
-	accounts := &accounts{td}
-	blockchain := &blockchain{td}
-	consensusEngine := &consensusEngine{td}
-	eventer := &eventer{td}
-	namereg := &namereg{td}
-	transactor := &transactor{td}
 	return &MockPipe{
-		td,
-		accounts,
-		blockchain,
-		consensusEngine,
-		eventer,
-		namereg,
-		transactor,
+		testData:        td,
+		accounts:        &accounts{td},
+		blockchain:      &blockchain{td},
+		consensusEngine: &consensusEngine{td},
+		events:          &eventer{td},
+		namereg:         &namereg{td},
+		transactor:      &transactor{td},
+		logger:          loggers.NewNoopInfoTraceLogger(),
 	}
 }
 
@@ -75,6 +72,10 @@ func (pipe *MockPipe) Transactor() definitions.Transactor {
 	return pipe.transactor
 }
 
+func (pipe *MockPipe) Logger() loggers.InfoTraceLogger {
+	return pipe.logger
+}
+
 func (pipe *MockPipe) GetApplication() manager_types.Application {
 	// TODO: [ben] mock application
 	return nil
diff --git a/txs/log.go b/txs/log.go
deleted file mode 100644
index b967a58d0ef7d4701fc15f5ab3dfee1f046b15f5..0000000000000000000000000000000000000000
--- a/txs/log.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package txs
-
-import (
-	"github.com/tendermint/go-logger"
-)
-
-var log = logger.New("module", "types")
diff --git a/util/hell/README.md b/util/hell/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..715cb5522ca5a98143a45edf7e9ec49517b6778d
--- /dev/null
+++ b/util/hell/README.md
@@ -0,0 +1,11 @@
+> Hell is other people's packages
+
+While we wait for working package management in go we need a way to make
+maintaining the glide.lock by hand less painful.
+
+To interactively add a package run from the root:
+
+```bash
+go run ./util/hell/cmd/hell/main.go get --interactive github.com/tendermint/tendermint
+```
+
diff --git a/util/hell/cmd/hell/main.go b/util/hell/cmd/hell/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..6736e496f29cb335239c8553117f13df355f588d
--- /dev/null
+++ b/util/hell/cmd/hell/main.go
@@ -0,0 +1,132 @@
+package main
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+
+	"github.com/eris-ltd/eris-db/util/hell"
+
+	"github.com/Masterminds/glide/action"
+	"github.com/Masterminds/glide/cache"
+	"github.com/Masterminds/glide/cfg"
+	"github.com/Masterminds/glide/msg"
+	"github.com/Masterminds/glide/path"
+	"github.com/Masterminds/glide/repo"
+	"github.com/Masterminds/glide/util"
+	"github.com/spf13/cobra"
+)
+
+func main() {
+	hellCmd := &cobra.Command{
+		Use:   "hell",
+		Short: "Hell makes the most of it being warm",
+		Long:  "",
+		Run:   func(cmd *cobra.Command, args []string) { cmd.Help() },
+	}
+
+	// Lock merge command
+	var baseGlideLockFile, depGlideLockFile string
+	lockMergeCmd := &cobra.Command{
+		Use:   "lock-merge",
+		Short: "Merge glide.lock files together",
+		Long: "This command merges two glide.lock files into a single one by copying all dependencies " +
+			"from a base glide.lock and an override glide.lock to an output glide.lock with dependencies " +
+			"from override taking precedence over those from base.",
+		Run: func(cmd *cobra.Command, args []string) {
+			baseLockFile, err := cfg.ReadLockFile(baseGlideLockFile)
+			if err != nil {
+				fmt.Printf("Could not read file: %s\n", err)
+				os.Exit(1)
+			}
+			overrideLockFile, err := cfg.ReadLockFile(depGlideLockFile)
+			if err != nil {
+				fmt.Printf("Could not read file: %s\n", err)
+				os.Exit(1)
+			}
+			mergedLockFile, err := hell.MergeGlideLockFiles(baseLockFile, overrideLockFile)
+			if err != nil {
+				fmt.Printf("Could not merge lock files: %s\n", err)
+				os.Exit(1)
+			}
+			mergedBytes, err := mergedLockFile.Marshal()
+			if err != nil {
+				fmt.Printf("Could not marshal lock file: %s\n", err)
+				os.Exit(1)
+			}
+			os.Stdout.Write(mergedBytes)
+		},
+	}
+	lockMergeCmd.PersistentFlags().StringVarP(&baseGlideLockFile, "base", "b", "", "base lock file")
+	lockMergeCmd.PersistentFlags().StringVarP(&depGlideLockFile, "override", "o", "", "override lock file")
+
+	// Lock update
+	interactive := false
+	getTransitiveCmd := &cobra.Command{
+		Use:   "get",
+		Short: "gets a remote dependency to this project along with its transtive dependencies.",
+		Long: "Gets a remote dependency and its transitive dependencies by adding the remote " +
+			"depednency to this project's glide.yaml and merging the remote dependency's " +
+			"glide.lock into this project's glide.lock",
+		Run: func(cmd *cobra.Command, args []string) {
+			if len(args) != 1 {
+				msg.Die("%s requires a single argument of the remote dependency\n", cmd.Name())
+			}
+			rootPackage, _ := util.NormalizeName(args[0])
+			// Add dependency to glide
+			installer := repo.NewInstaller()
+			action.Get(args, installer, false, true, false, !interactive, false)
+			// Now hunt down the repo cache
+			dep := action.EnsureConfig().Imports.Get(rootPackage)
+
+			key, err := cache.Key(dep.Remote())
+			if err != nil {
+				msg.Die("%s requires a single argument of the remote dependency\n", cmd.Name())
+			}
+			cacheDir := filepath.Join(cache.Location(), "src", key)
+			repos, err := dep.GetRepo(cacheDir)
+			if err != nil {
+				msg.Die("Could not get repo: %s", err)
+			}
+			version, err := repos.Version()
+			if err != nil {
+				msg.Die("Could not get version: %s", err)
+			}
+			dep.Pin = version
+			lockPath := filepath.Join(".", path.LockFile)
+			baseLockFile, err := cfg.ReadLockFile(lockPath)
+			if err != nil {
+				msg.Die("Could not read base lock file: %s", err)
+			}
+			overrideLockFile := &cfg.Lockfile{}
+			if path.HasLock(cacheDir) {
+				msg.Info("Found dependency lock file so merging into project lock file")
+				overrideLockFile, err = cfg.ReadLockFile(filepath.Join(cacheDir, path.LockFile))
+				if err != nil {
+					msg.Die("Could not read dependency lock file: %s", err)
+				}
+			}
+			// Add the package to glide lock too!
+			overrideLockFile.Imports = append(overrideLockFile.Imports, cfg.LockFromDependency(dep))
+
+			mergedLockFile, err := hell.MergeGlideLockFiles(baseLockFile, overrideLockFile)
+			fmt.Printf("%#v\n", mergedLockFile.Imports)
+			if err != nil {
+				msg.Die("Could not merge lock files: %s\n", err)
+			}
+			err = mergedLockFile.WriteFile(lockPath)
+			if err != nil {
+				msg.Die("Could not write merged lock file: %s", err)
+			}
+
+			action.Install(installer, false)
+		},
+	}
+
+	getTransitiveCmd.PersistentFlags().BoolVarP(&interactive, "interactive", "i", false,
+		"set dependency verion interactively")
+
+	hellCmd.AddCommand(lockMergeCmd)
+	hellCmd.AddCommand(getTransitiveCmd)
+	lockMergeCmd.Execute()
+}
diff --git a/util/hell/merge.go b/util/hell/merge.go
new file mode 100644
index 0000000000000000000000000000000000000000..915001f398d7fa229df015f4d03e13f2a87ade5a
--- /dev/null
+++ b/util/hell/merge.go
@@ -0,0 +1,87 @@
+package hell
+
+import (
+	"crypto/sha256"
+	"fmt"
+	"sort"
+
+	"github.com/Masterminds/glide/cfg"
+)
+
+// Merges two glide lock files together, letting dependencies from 'base' be overwritten
+// by those from 'override'. Returns the resultant glide lock file bytes
+func MergeGlideLockFiles(baseLockFile, overrideLockFile *cfg.Lockfile) (*cfg.Lockfile, error) {
+	imports := make(map[string]*cfg.Lock, len(baseLockFile.Imports))
+	devImports := make(map[string]*cfg.Lock, len(baseLockFile.DevImports))
+	// Copy the base dependencies into a map
+	for _, lock := range baseLockFile.Imports {
+		imports[lock.Name] = lock
+	}
+	for _, lock := range baseLockFile.DevImports {
+		devImports[lock.Name] = lock
+	}
+	// Override base dependencies and add any extra ones
+	for _, lock := range overrideLockFile.Imports {
+		imports[lock.Name] = mergeLocks(imports[lock.Name], lock)
+	}
+	for _, lock := range overrideLockFile.DevImports {
+		devImports[lock.Name] = mergeLocks(imports[lock.Name], lock)
+	}
+
+	deps := make([]*cfg.Dependency, 0, len(imports))
+	devDeps := make([]*cfg.Dependency, 0, len(devImports))
+
+	// Flatten to Dependencies
+	for _, lock := range imports {
+		deps = append(deps, pinnedDependencyFromLock(lock))
+	}
+
+	for _, lock := range devImports {
+		devDeps = append(devDeps, pinnedDependencyFromLock(lock))
+	}
+
+	hasher := sha256.New()
+	hasher.Write(([]byte)(baseLockFile.Hash))
+	hasher.Write(([]byte)(overrideLockFile.Hash))
+
+	return cfg.NewLockfile(deps, devDeps, fmt.Sprintf("%x", hasher.Sum(nil)))
+}
+
+func mergeLocks(baseLock, overrideLock *cfg.Lock) *cfg.Lock {
+	lock := overrideLock.Clone()
+	if baseLock == nil {
+		return lock
+	}
+
+	// Merge and dedupe subpackages
+	subpackages := make([]string, 0, len(lock.Subpackages)+len(baseLock.Subpackages))
+	for _, sp := range lock.Subpackages {
+		subpackages = append(subpackages, sp)
+	}
+	for _, sp := range baseLock.Subpackages {
+		subpackages = append(subpackages, sp)
+	}
+
+	sort.Stable(sort.StringSlice(subpackages))
+
+	dedupeSubpackages := make([]string, 0, len(subpackages))
+
+	lastSp := ""
+	elided := 0
+	for _, sp := range subpackages {
+		if lastSp == sp {
+			elided++
+		} else {
+			dedupeSubpackages = append(dedupeSubpackages, sp)
+			lastSp = sp
+		}
+	}
+	lock.Subpackages = dedupeSubpackages[:len(dedupeSubpackages)-elided]
+	return lock
+}
+
+func pinnedDependencyFromLock(lock *cfg.Lock) *cfg.Dependency {
+	dep := cfg.DependencyFromLock(lock)
+	dep.Pin = lock.Version
+	return dep
+}
diff --git a/util/hell/merge_test.go b/util/hell/merge_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..df44045a7d6ab111c441bdaa8d6d96e6fcd58517
--- /dev/null
+++ b/util/hell/merge_test.go
@@ -0,0 +1,71 @@
+package hell
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/Masterminds/glide/cfg"
+	"github.com/stretchr/testify/assert"
+)
+
+const baseLockYml = `imports:
+- name: github.com/gogo/protobuf
+  version: 82d16f734d6d871204a3feb1a73cb220cc92574c
+- name: github.com/tendermint/tendermint
+  version: aaea0c5d2e3ecfbf29f2608f9d43649ec7f07f50
+  subpackages:
+  - node
+  - proxy
+  - types
+  - version
+  - consensus
+  - rpc/core/types
+  - blockchain
+  - mempool
+  - rpc/core
+  - state
+`
+const overrideLockYml = `imports:
+- name: github.com/tendermint/tendermint
+  version: 764091dfbb035f1b28da4b067526e04c6a849966
+  subpackages:
+  - benchmarks
+  - proxy
+  - types
+  - version
+`
+const expectedLockYml = `imports:
+- name: github.com/gogo/protobuf
+  version: 82d16f734d6d871204a3feb1a73cb220cc92574c
+- name: github.com/tendermint/tendermint
+  version: 764091dfbb035f1b28da4b067526e04c6a849966
+  subpackages:
+  - benchmarks
+  - blockchain
+  - consensus
+  - mempool
+  - node
+  - proxy
+  - rpc/core
+  - rpc/core/types
+testImports: []
+`
+
+func TestMergeGlideLockFiles(t *testing.T) {
+	baseLockFile, err := cfg.LockfileFromYaml(([]byte)(baseLockYml))
+	assert.NoError(t, err, "Lockfile should parse")
+
+	overrideLockFile, err := cfg.LockfileFromYaml(([]byte)(overrideLockYml))
+	assert.NoError(t, err, "Lockfile should parse")
+
+	mergedLockFile, err := MergeGlideLockFiles(baseLockFile, overrideLockFile)
+	assert.NoError(t, err, "Lockfiles should merge")
+
+	mergedYmlBytes, err := mergedLockFile.Marshal()
+	assert.NoError(t, err, "Lockfile should marshal")
+
+	ymlLines := strings.Split(string(mergedYmlBytes), "\n")
+	// Drop the updated and hash lines
+	actualYml := strings.Join(ymlLines[2:], "\n")
+	assert.Equal(t, expectedLockYml, actualYml)
+}
diff --git a/util/os.go b/util/os.go
new file mode 100644
index 0000000000000000000000000000000000000000..cc682918d88264505a5b48ab0d8819ff612d5395
--- /dev/null
+++ b/util/os.go
@@ -0,0 +1,12 @@
+package util
+
+import (
+	"fmt"
+	"os"
+)
+
+// Prints an error message to stderr and exits with status code 1
+func Fatalf(format string, args ...interface{}) {
+	fmt.Fprintf(os.Stderr, format, args...)
+	os.Exit(1)
+}
diff --git a/util/slice/slice.go b/util/slice/slice.go
new file mode 100644
index 0000000000000000000000000000000000000000..2c51efd21f89c3478a185d623e54dcc9d4ea3064
--- /dev/null
+++ b/util/slice/slice.go
@@ -0,0 +1,91 @@
+package slice
+
+func Slice(elements ...interface{}) []interface{} {
+	return elements
+}
+
+func EmptySlice() []interface{} {
+	return []interface{}{}
+}
+
+// Like append but on the interface{} type and always to a fresh backing array
+// so can be used safely with slices over arrays you did not create.
+func CopyAppend(slice []interface{}, elements ...interface{}) []interface{} {
+	sliceLength := len(slice)
+	newSlice := make([]interface{}, sliceLength+len(elements))
+	for i, e := range slice {
+		newSlice[i] = e
+	}
+	for i, e := range elements {
+		newSlice[sliceLength+i] = e
+	}
+	return newSlice
+}
+
+// Prepend elements to slice in the order they appear
+func CopyPrepend(slice []interface{}, elements ...interface{}) []interface{} {
+	elementsLength := len(elements)
+	newSlice := make([]interface{}, len(slice)+elementsLength)
+	for i, e := range elements {
+		newSlice[i] = e
+	}
+	for i, e := range slice {
+		newSlice[elementsLength+i] = e
+	}
+	return newSlice
+}
+
+// Concatenate slices into a single slice
+func Concat(slices ...[]interface{}) []interface{} {
+	offset := 0
+	for _, slice := range slices {
+		offset += len(slice)
+	}
+	concat := make([]interface{}, offset)
+	offset = 0
+	for _, slice := range slices {
+		for i, e := range slice {
+			concat[offset+i] = e
+		}
+		offset += len(slice)
+	}
+	return concat
+}
+
+// Deletes n elements starting with the ith from a slice by splicing.
+// Beware uses append so the underlying backing array will be modified!
+func Delete(slice []interface{}, i int, n int) []interface{} {
+	return append(slice[:i], slice[i+n:]...)
+}
+
+// Delete an element at a specific index and return the contracted list
+func DeleteAt(slice []interface{}, i int) []interface{} {
+	return Delete(slice, i, 1)
+}
+
+// Flatten a slice by a list by splicing any elements of the list that are
+// themselves lists into the slice elements to the list in place of slice itself
+func Flatten(slice []interface{}) []interface{} {
+	return DeepFlatten(slice, 1)
+}
+
+// Recursively flattens a list by splicing any sub-lists into their parent until
+// depth is reached. If a negative number is passed for depth then it continues
+// until no elements of the returned list are lists
+func DeepFlatten(slice []interface{}, depth int) []interface{} {
+	if depth == 0 {
+		return slice
+	}
+	returnSlice := []interface{}{}
+
+	for _, element := range slice {
+		if s, ok := element.([]interface{}); ok {
+			returnSlice = append(returnSlice, DeepFlatten(s, depth-1)...)
+		} else {
+			returnSlice = append(returnSlice, element)
+		}
+
+	}
+
+	return returnSlice
+}
diff --git a/util/slice/slice_test.go b/util/slice/slice_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b3d1c9b6a530b1fe050427b3ba41ae8f9f23c388
--- /dev/null
+++ b/util/slice/slice_test.go
@@ -0,0 +1,43 @@
+package slice
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestCopyAppend(t *testing.T) {
+	assert.Equal(t, Slice(1, "two", "three", 4),
+		CopyAppend(Slice(1, "two"), "three", 4))
+	assert.Equal(t, EmptySlice(), CopyAppend(nil))
+	assert.Equal(t, Slice(1), CopyAppend(nil, 1))
+	assert.Equal(t, Slice(1), CopyAppend(Slice(1)))
+}
+
+func TestCopyPrepend(t *testing.T) {
+	assert.Equal(t, Slice("three", 4, 1, "two"),
+		CopyPrepend(Slice(1, "two"), "three", 4))
+	assert.Equal(t, EmptySlice(), CopyPrepend(nil))
+	assert.Equal(t, Slice(1), CopyPrepend(nil, 1))
+	assert.Equal(t, Slice(1), CopyPrepend(Slice(1)))
+}
+
+func TestConcat(t *testing.T) {
+	assert.Equal(t, Slice(1, 2, 3, 4, 5), Concat(Slice(1, 2, 3, 4, 5)))
+	assert.Equal(t, Slice(1, 2, 3, 4, 5), Concat(Slice(1, 2, 3), Slice(4, 5)))
+	assert.Equal(t, Slice(1, 2, 3, 4, 5), Concat(Slice(1), Slice(2, 3), Slice(4, 5)))
+	assert.Equal(t, EmptySlice(), Concat(nil))
+	assert.Equal(t, Slice(1), Concat(nil, Slice(), Slice(1)))
+	assert.Equal(t, Slice(1), Concat(Slice(1), Slice(), nil))
+}
+
+func TestDelete(t *testing.T) {
+	assert.Equal(t, Slice(1, 2, 4, 5), Delete(Slice(1, 2, 3, 4, 5), 2, 1))
+}
+
+func TestDeepFlatten(t *testing.T) {
+	assert.Equal(t, Flatten(Slice(Slice(1, 2), 3, 4)), Slice(1, 2, 3, 4))
+	nestedSlice := Slice(Slice(1, Slice(Slice(2))), Slice(3, 4))
+	assert.Equal(t, DeepFlatten(nestedSlice, -1), Slice(1, 2, 3, 4))
+	assert.Equal(t, DeepFlatten(nestedSlice, 2), Slice(1, Slice(2), 3, 4))
+}