diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644
index 0000000000000000000000000000000000000000..b4abbe9bd1cd21db3023d582cc922ca58973f574
--- /dev/null
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,70 @@
+# Contributing to `eris-db`:
+Forked from Docker's [contributing guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md)
+
+## Bug Reporting
+
+A great way to contribute to the project is to send a detailed report when you encounter an issue. We always appreciate a well-written, thorough bug report, and will thank you for it!
+
+Check that the issue doesn't already exist before submitting an issue. If you find a match, you can use the "subscribe" button to get notified on updates. Add a :+1: if you've also encountered this issue. If you have ways to reproduce the issue or have additional information that may help resolving the issue, please leave a comment.
+
+Also include the steps required to reproduce the problem if possible and applicable. This information will help us review and fix your issue faster. When sending lengthy log-files, post them as a gist (https://gist.github.com). Don't forget to remove sensitive data from your log files before posting (you can replace those parts with "REDACTED").
+
+Our [ISSUE_TEMPLATE.md](ISSUE_TEMPLATE.md) will autopopulate the new issue.
+
+## Contribution Tips and Guidelines
+
+### Pull requests are always welcome (to `develop` rather than `master`).
+
+Not sure if that typo is worth a pull request? Found a bug and know how to fix it? Do it! We will appreciate it. Any significant improvement should be documented as a GitHub issue or discussed in [The Marmot Den](https://slack.monax.io) Slack community prior to beginning.
+
+We are always thrilled to receive pull requests (and bug reports!) and we do our best to process them quickly. 
+
+## Conventions
+
+Fork the repository and make changes on your fork in a feature branch (branched from develop), create an issue outlining your feature or a bug, or use an open one.
+
+    If it's a bug fix branch, name it something-XXXX where XXXX is the number of the issue.
+    If it's a feature branch, create an enhancement issue to announce your intentions, and name it something-XXXX where XXXX is the number of the issue.
+
+Submit unit tests for your changes. Go has a great test framework built in; use it! Take a look at existing tests for inspiration. Run the full test suite on your branch before submitting a pull request.
+
+Update the documentation when creating or modifying features. Test your documentation changes for clarity, concision, and correctness, as well as a clean documentation build. 
+
+Write clean code. Universally formatted code promotes ease of writing, reading, and maintenance. Always run `gofmt -s -w file.go` on each changed file before committing your changes. Most editors have plug-ins that do this automatically.
+
+Pull request descriptions should be as clear as possible and include a reference to all the issues that they address.
+
+Commit messages must start with a short summary (max. 50 chars) written in the imperative, followed by an optional, more detailed explanatory text which is separated from the summary by an empty line.
+
+Code review comments may be added to your pull request. Discuss, then make the suggested modifications and push additional commits to your feature branch. 
+
+Pull requests must be cleanly rebased on top of develop without multiple branches mixed into the PR.
+
+*Git tip:* If your PR no longer merges cleanly, use `git rebase develop` in your feature branch to update your pull request rather than merge develop.
+
+Before you make a pull request, squash your commits into logical units of work using `git rebase -i` and `git push -f`. A logical unit of work is a consistent set of patches that should be reviewed together: for example, upgrading the version of a vendored dependency and taking advantage of its now available new feature constitute two separate units of work. Implementing a new function and calling it in another file constitute a single logical unit of work. The very high majority of submissions should have a single commit, so if in doubt: squash down to one.
+
+After every commit, make sure the test suite passes. Include documentation changes in the same pull request so that a revert would remove all traces of the feature or fix.
+
+### Merge approval
+
+We use LGTM (Looks Good To Me) in commands on the code review to indicate acceptance. 
+
+## Errors and Log Messages Style
+
+TODO
+
+## Coding Style
+
+Unless explicitly stated, we follow all coding guidelines from the Go community. While some of these standards may seem arbitrary, they somehow seem to result in a solid, consistent codebase.
+
+It is possible that the code base does not currently comply with these guidelines. We are not looking for a massive PR that fixes this, since that goes against the spirit of the guidelines. All new contributions should make a best effort to clean up and make the code base better than they left it. Obviously, apply your best judgement. Remember, the goal here is to make the code base easier for humans to navigate and understand. Always keep that in mind when nudging others to comply.
+
+* All code should be formatted with `gofmt -s`.
+* All code should follow the guidelines covered in [Effective Go](https://golang.org/doc/effective_go.html) and [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments).
+* Comment the code. Tell us the why, the history and the context.
+* Document all declarations and methods, even private ones. Declare expectations, caveats and anything else that may be important. If a type gets exported, having the comments already there will ensure it's ready.
+* Variable name length should be proportional to it's context and no longer. noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo. In practice, short methods will have short variable names and globals will have longer names.
+* No underscores in package names. If you need a compound name, step back, and re-examine why you need a compound name. If you still think you need a compound name, lose the underscore.
+* No utils or helpers packages. If a function is not general enough to warrant its own package, it has not been written generally enough to be a part of a `util` package. Just leave it unexported and well-documented.
+* All tests should run with `go test` and outside tooling should not be required. No, we don't need another unit testing framework. Assertion packages are acceptable if they provide real incremental value.
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000000000000000000000000000000000000..e3867662dde61707fc52857e4a110850585c352b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,12 @@
+Please include in your bug report:
+
+- [ ] `eris-db version` (docker image tag or branch if built from source)
+- [ ] `eris version` (if applicable)
+- [ ] `go version` (if applicable)
+- [ ] `docker version` (if applicable)
+- [ ] operating system details (osx/windows/linux)
+- [ ] what you expected to happen
+- [ ] what actually happened
+- [ ] steps to reproduction
+
+If issue is a feature request, tell us why this feature is useful.
diff --git a/.gitignore b/.gitignore
index d2482dea1fddf517c65e3d27012dfaffb6fcb809..12be4236c891d5077c99a4a556c9813fc1b86284 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,11 @@
 # do not vendor in dependencies
 vendor
+
+# build target
+target
+
 # Temporary / cached
 *.swp
 debug
 .idea
+.vscode
diff --git a/Dockerfile.deploy b/Dockerfile.deploy
index c264cef4a2d67273b3140331791a9a6e78d84ef2..dfe3b3e8a632a1c8af4c5590592202b60f7edaa4 100644
--- a/Dockerfile.deploy
+++ b/Dockerfile.deploy
@@ -3,13 +3,8 @@ MAINTAINER Monax <support@monax.io>
 
 ENV TARGET eris-db
 
-# runtime customization start here
-COPY ./eris-client $INSTALL_BASE/eris-client
-COPY ./bin/start_eris_db $INSTALL_BASE/erisdb-wrapper
-# runtime customization end here
-
-# Get the binary from the artifact in pwd
-COPY ./"$TARGET"_build_artifact $INSTALL_BASE/$TARGET
+# Get the binary from the artefact in pwd/target/docker
+COPY ./target/docker/"$TARGET".dockerartefact $INSTALL_BASE/$TARGET
 RUN chmod +x --recursive $INSTALL_BASE
 
 # Finalize
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..29bbd1a874f96fb7813e15138112d522399992d6
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,156 @@
+# ----------------------------------------------------------
+# REQUIREMENTS
+
+# - go installed locally
+# - for build_docker: docker installed locally
+
+# ----------------------------------------------------------
+
+SHELL := /bin/bash
+REPO := $(shell pwd)
+GOFILES_NOVENDOR := $(shell find ${REPO} -type f -name '*.go' -not -path "${REPO}/vendor/*")
+PACKAGES_NOVENDOR := $(shell go list github.com/eris-ltd/eris-db/... | grep -v /vendor/)
+VERSION := $(shell cat ${REPO}/version/version.go | tail -n 1 | cut -d \  -f 4 | tr -d '"')
+VERSION_MIN := $(shell echo ${VERSION} | cut -d . -f 1-2)
+COMMIT_SHA := $(shell echo `git rev-parse --short --verify HEAD`)
+
+DOCKER_NAMESPACE := quay.io/eris
+
+
+.PHONY: greet
+greet:
+	@echo "Hi! I'm the marmot that will help you with eris-db v${VERSION}"
+
+### Formatting, linting and vetting
+
+# check the code for style standards; currently enforces go formatting.
+# display output first, then check for success	
+.PHONY: check
+check:
+	@echo "Checking code for formatting style compliance."
+	@gofmt -l -d ${GOFILES_NOVENDOR}
+	@gofmt -l ${GOFILES_NOVENDOR} | read && echo && echo "Your marmot has found a problem with the formatting style of the code." 1>&2 && exit 1 || true
+
+# fmt runs gofmt -w on the code, modifying any files that do not match
+# the style guide.
+.PHONY: fmt
+fmt:
+	@echo "Correcting any formatting style corrections."
+	@gofmt -l -w ${GOFILES_NOVENDOR}
+
+# lint installs golint and prints recommendations for coding style.
+lint: 
+	@echo "Running lint checks."
+	go get -u github.com/golang/lint/golint
+	@for file in $(GOFILES_NOVENDOR); do \
+		echo; \
+		golint --set_exit_status $${file}; \
+	done
+
+# vet runs extended compilation checks to find recommendations for
+# suspicious code constructs.
+.PHONY: vet
+vet:
+	@echo "Running go vet."
+	@go vet ${PACKAGES_NOVENDOR}
+
+### Dependency management for github.com/eris-ltd/eris-db
+
+# erase vendor wipes the full vendor directory
+.PHONY: erase_vendor
+erase_vendor:
+	rm -rf ${REPO}/vendor/
+
+# install vendor uses glide to install vendored dependencies
+.PHONY: install_vendor
+install_vendor:
+	go get github.com/Masterminds/glide
+	glide install
+
+# hell runs utility tool hell to selectively update glide dependencies
+.PHONY: hell
+hell:
+	go build -o ${REPO}/target/hell ./util/hell/cmd/hell/main.go
+	./target/hell $(filter-out $@,$(MAKECMDGOALS))
+
+### Building github.com/eris-ltd/eris-db
+
+# build all targets in github.com/eris-ltd/eris-db
+.PHONY: build
+build:	check build_db build_client build_keys
+
+# build all targets in github.com/eris-ltd/eris-db with checks for race conditions
+.PHONY: build_race
+build_race:	check build_race_db build_race_client build_race_keys
+
+# build eris-db
+.PHONY: build_db
+build_db:
+	go build -o ${REPO}/target/eris-db-${COMMIT_SHA} ./cmd/eris-db
+
+# build eris-client
+.PHONY: build_client
+build_client:
+	go build -o ${REPO}/target/eris-client-${COMMIT_SHA} ./client/cmd/eris-client
+
+# build eris-keys
+.PHONY: build_keys
+build_keys:
+	@echo "Marmots need to complete moving repository eris-keys into eris-db."
+
+# build eris-db with checks for race conditions
+.PHONY: build_race_db
+build_race_db:
+	go build -race -o ${REPO}/target/eris-db-${COMMIT_SHA} ./cmd/eris-db
+
+# build eris-client with checks for race conditions
+.PHONY: build_race_client
+build_race_client:
+	go build -race -o ${REPO}/target/eris-client-${COMMIT_SHA} ./client/cmd/eris-client
+
+# build eris-keys with checks for race conditions
+.PHONY: build_race_keys
+build_race_keys:
+	@echo "Marmots need to complete moving repository eris-keys into eris-db."
+
+### Testing github.com/eris-ltd/eris-db
+
+# test eris-db
+.PHONY: test
+test: build
+	@go test ${PACKAGES_NOVENDOR} -tags integration
+
+# test eris-db with checks for race conditions
+.PHONY: test_race
+test_race: build_race
+	@go test -race ${PACKAGES_NOVENDOR}
+
+### Build docker images for github.com/eris-ltd/eris-db
+
+# build docker image for eris-db
+.PHONY: build_docker_db
+build_docker_db: check
+	@mkdir -p ${REPO}/target/docker
+	docker build -t ${DOCKER_NAMESPACE}/db:build-${COMMIT_SHA} ${REPO}
+	docker run --rm --entrypoint cat ${DOCKER_NAMESPACE}/db:build-${COMMIT_SHA} /usr/local/bin/eris-db > ${REPO}/target/docker/eris-db.dockerartefact
+	docker run --rm --entrypoint cat ${DOCKER_NAMESPACE}/db:build-${COMMIT_SHA} /usr/local/bin/eris-client > ${REPO}/target/docker/eris-client.dockerartefact
+	docker build -t ${DOCKER_NAMESPACE}/db:${VERSION} -f Dockerfile.deploy ${REPO}
+
+	@rm ${REPO}/target/docker/eris-db.dockerartefact
+	@rm ${REPO}/target/docker/eris-client.dockerartefact
+	docker rmi ${DOCKER_NAMESPACE}/db:build-${COMMIT_SHA}
+
+### Test docker images for github.com/eris-ltd/eris-db
+
+# test docker image for eris-db
+.PHONY: test_docker_db
+test_docker_db: check
+	docker build -t ${DOCKER_NAMESPACE}/db:build-${COMMIT_SHA} ${REPO}
+	docker run ${DOCKER_NAMESPACE}/db:build-${COMMIT_SHA} glide nv | xargs go test -tags integration
+
+### Clean up
+
+# clean removes the target folder containing build artefacts
+.PHONY: clean
+clean:
+	-rm -r ./target 
\ No newline at end of file
diff --git a/README.md b/README.md
index 923114c13dbb4df8cd7dd83e704d3b8198d2faa7..fd30fd6b44f82d2ba9d5e9d88715a76b4247871e 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@ A commented template config will be written as part of the `eris chains make` [p
 
 ## Contribute
 
-See the [eris platform contributing file here](https://github.com/eris-ltd/coding/blob/master/github/CONTRIBUTING.md).
+See the [CONTRIBUTING.md](.github/CONTRIBUTING.md) for more details.
 
 ## License
 
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 24eeb0338629fdbaae0aa28367a074e436e1d415..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..3dc213f4eef06f0c1ac93779393a15e6ea3037cd
--- /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(16),
+		GenesisRelativePath: "genesis.json",
+	}
+	chainConsensusModule := &ConfigChainModule{
+		Name:               "tendermint",
+		MajorVersion:       uint8(0),
+		MinorVersion:       uint8(6),
+		ModuleRelativeRoot: "tendermint",
+	}
+	chainApplicationManagerModule := &ConfigChainModule{
+		Name:               "erismint",
+		MajorVersion:       uint8(0),
+		MinorVersion:       uint8(16),
+		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..66a4e5b88c9e8374ceeaf941d388e12978b96358
--- /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.16.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/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 772174fe10117394b79e49ac0b98c98d08fd07ff..29050723ae9e89850b0ebbb91d432591ffdb4ef1 100644
--- a/manager/eris-mint/eris-mint.go
+++ b/manager/eris-mint/eris-mint.go
@@ -26,7 +26,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"
@@ -48,7 +49,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
@@ -72,13 +74,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"),
 	}
 }
 
@@ -146,18 +149,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 6a3945ae3ed9fb075aef188a23b0d59d056f8e1c..a9dd607fff77964105a6da01e0aedf1035b47a24 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/eris-mint/version.go b/manager/eris-mint/version.go
index 34cdb4b5324a9b8a52eeaa36334ffda0b01057b1..ad15d4a34d7cf0b2eb78a32d4831194e6db9aeb6 100644
--- a/manager/eris-mint/version.go
+++ b/manager/eris-mint/version.go
@@ -28,7 +28,7 @@ const (
 	// Major version component of the current release
 	erisMintVersionMajor = 0
 	// Minor version component of the current release
-	erisMintVersionMinor = 12
+	erisMintVersionMinor = 16
 	// Patch version component of the current release
 	erisMintVersionPatch = 0
 )
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/codec.go b/rpc/codec.go
index d6fa05bb1a23917d474e760804cd83052c50032f..7afe5a393cd4bc4ad4662cc8d7eaa64a2fd6f654 100644
--- a/rpc/codec.go
+++ b/rpc/codec.go
@@ -1,3 +1,18 @@
+// Copyright 2015-2017 Monax Industries Limited.
+// This file is part of the Monax platform (Monax)
+
+// Monax is free software: you can use, redistribute it and/or modify
+// it only under the terms of the GNU General Public License, version
+// 3, as published by the Free Software Foundation.
+
+// Monax is distributed WITHOUT ANY WARRANTY pursuant to
+// the terms of the Gnu General Public Licence, version 3, including
+// (but not limited to) Clause 15 thereof. See the text of the
+// GNU General Public License, version 3 for full terms.
+
+// You should have received a copy of the GNU General Public License,
+// version 3, with Monax.  If not, see <http://www.gnu.org/licenses/>.
+
 package rpc
 
 import (
diff --git a/rpc/jsonrpc.go b/rpc/jsonrpc.go
index 72115138ad53350c8619c1fdce1ba67720e78a17..73cb0cb6b78862227ecc27afb98bc1f7423244c7 100644
--- a/rpc/jsonrpc.go
+++ b/rpc/jsonrpc.go
@@ -1,3 +1,18 @@
+// Copyright 2015-2017 Monax Industries Limited.
+// This file is part of the Monax platform (Monax)
+
+// Monax is free software: you can use, redistribute it and/or modify
+// it only under the terms of the GNU General Public License, version
+// 3, as published by the Free Software Foundation.
+
+// Monax is distributed WITHOUT ANY WARRANTY pursuant to
+// the terms of the Gnu General Public Licence, version 3, including
+// (but not limited to) Clause 15 thereof. See the text of the
+// GNU General Public License, version 3 for full terms.
+
+// You should have received a copy of the GNU General Public License,
+// version 3, with Monax.  If not, see <http://www.gnu.org/licenses/>.
+
 package rpc
 
 import (
@@ -14,6 +29,7 @@ const (
 )
 
 // Request and Response objects. Id is a string. Error data not used.
+// Refer to JSON-RPC specification http://www.jsonrpc.org/specification
 type (
 	RPCRequest struct {
 		JSONRPC string          `json:"jsonrpc"`
@@ -22,35 +38,61 @@ type (
 		Id      string          `json:"id"`
 	}
 
-	RPCResponse struct {
+	// RPCResponse MUST follow the JSON-RPC specification for Response object
+	// reference: http://www.jsonrpc.org/specification#response_object
+	RPCResponse interface {
+		AssertIsRPCResponse() bool
+	}
+
+	// RPCResultResponse MUST NOT contain the error member if no error occurred
+	RPCResultResponse struct {
 		Result  interface{} `json:"result"`
-		Error   *RPCError   `json:"error"`
 		Id      string      `json:"id"`
 		JSONRPC string      `json:"jsonrpc"`
 	}
 
+	// RPCErrorResponse MUST NOT contain the result member if an error occured
+	RPCErrorResponse struct {
+		Error   *RPCError `json:"error"`
+		Id      string    `json:"id"`
+		JSONRPC string    `json:"jsonrpc"`
+	}
+
+	// RPCError MUST be included in the Response object if an error occured
 	RPCError struct {
 		Code    int    `json:"code"`
 		Message string `json:"message"`
+		// Note: Data is currently unused, and the data member may be omitted
+		// Data  interface{} `json:"data"`
 	}
 )
 
-// Create a new response object from a result.
-func NewRPCResponse(id string, res interface{}) *RPCResponse {
-	return &RPCResponse{
+// NewRPCResponse creates a new response object from a result
+func NewRPCResponse(id string, res interface{}) RPCResponse {
+	return RPCResponse(&RPCResultResponse{
 		Result:  res,
-		Error:   nil,
 		Id:      id,
 		JSONRPC: "2.0",
-	}
+	})
 }
 
-// Create a new error-response object from the error code and message.
-func NewRPCErrorResponse(id string, code int, message string) *RPCResponse {
-	return &RPCResponse{
-		Result:  nil,
+// NewRPCErrorResponse creates a new error-response object from the error code and message
+func NewRPCErrorResponse(id string, code int, message string) RPCResponse {
+	return RPCResponse(&RPCErrorResponse{
 		Error:   &RPCError{code, message},
 		Id:      id,
 		JSONRPC: "2.0",
-	}
+	})
+}
+
+// AssertIsRPCResponse implements a marker method for RPCResultResponse
+// to implement the interface RPCResponse
+func (rpcResultResponse *RPCResultResponse) AssertIsRPCResponse() bool {
+	return true
+}
+
+// AssertIsRPCResponse implements a marker method for RPCErrorResponse
+// to implement the interface RPCResponse
+func (rpcErrorResponse *RPCErrorResponse) AssertIsRPCResponse() bool {
+	return true
 }
diff --git a/rpc/rpc_test.go b/rpc/rpc_test.go
index f728d822bd1251f9147aaeac9e199672b7092d25..a63a155101009371cdeec9f8dd7b1a7535e03fec 100644
--- a/rpc/rpc_test.go
+++ b/rpc/rpc_test.go
@@ -1,3 +1,18 @@
+// Copyright 2015-2017 Monax Industries Limited.
+// This file is part of the Monax platform (Monax)
+
+// Monax is free software: you can use, redistribute it and/or modify
+// it only under the terms of the GNU General Public License, version
+// 3, as published by the Free Software Foundation.
+
+// Monax is distributed WITHOUT ANY WARRANTY pursuant to
+// the terms of the Gnu General Public Licence, version 3, including
+// (but not limited to) Clause 15 thereof. See the text of the
+// GNU General Public License, version 3 for full terms.
+
+// You should have received a copy of the GNU General Public License,
+// version 3, with Monax.  If not, see <http://www.gnu.org/licenses/>.
+
 package rpc
 
 import (
@@ -10,12 +25,11 @@ import (
 func TestNewJsonRpcResponse(t *testing.T) {
 	id := "testId"
 	data := "a string"
-	resp := &RPCResponse{
+	resp := RPCResponse(&RPCResultResponse{
 		Result:  data,
-		Error:   nil,
 		Id:      id,
 		JSONRPC: "2.0",
-	}
+	})
 	respGen := NewRPCResponse(id, data)
 	assert.Equal(t, respGen, resp)
 }
@@ -25,12 +39,11 @@ func TestNewJsonRpcErrorResponse(t *testing.T) {
 	id := "testId"
 	code := 100
 	message := "the error"
-	resp := &RPCResponse{
-		Result:  nil,
+	resp := RPCResponse(&RPCErrorResponse{
 		Error:   &RPCError{code, message},
 		Id:      id,
 		JSONRPC: "2.0",
-	}
+	})
 	respGen := NewRPCErrorResponse(id, code, message)
 	assert.Equal(t, respGen, resp)
 }
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/config.go b/rpc/tendermint/test/config.go
index ff33cb30f4aca019aea9a400e434e7539a786bb9..fcf964bbfc7c0b53183c8838dd521b822eb84a31 100644
--- a/rpc/tendermint/test/config.go
+++ b/rpc/tendermint/test/config.go
@@ -61,7 +61,7 @@ genesis_file = "genesis.json"
   # version is the major and minor semantic version;
   # the version will be asserted on
   major_version = 0
-  minor_version = 12
+  minor_version = 16
   # relative path to application manager root folder
   relative_root = "erismint"
 
@@ -207,7 +207,7 @@ private_validator_file = "priv_validator.json"
 ################################################################################
 ##
 ## Eris-Mint
-## version 0.12.0
+## version 0.16.0
 ##
 ## The original Ethereum virtual machine with IAVL merkle trees
 ## and tendermint/go-wire encoding
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/tests/build_tool.sh b/tests/build_tool.sh
index b32f941b823b28d3850858dbab7345c9b0e8f5ab..1f377e91eacd659791df20bef4dd87c5d311a215 100755
--- a/tests/build_tool.sh
+++ b/tests/build_tool.sh
@@ -35,14 +35,15 @@ release_min=$(cat $REPO/version/version.go | tail -n 1 | cut -d \  -f 4 | tr -d
 release_maj=$(echo $release_min | cut -d . -f 1-2)
 
 # Build
+mkdir -p $REPO/target/docker
 docker build -t $IMAGE:build $REPO
-docker run --rm --entrypoint cat $IMAGE:build /usr/local/bin/$TARGET > $REPO/"$TARGET"_build_artifact
-docker run --rm --entrypoint cat $IMAGE:build /usr/local/bin/eris-client > $REPO/eris-client
+docker run --rm --entrypoint cat $IMAGE:build /usr/local/bin/$TARGET > $REPO/target/docker/eris-db.dockerartefact
+docker run --rm --entrypoint cat $IMAGE:build /usr/local/bin/eris-client > $REPO/target/docker/eris-client.dockerartefact
 docker build -t $IMAGE:$release_min -f Dockerfile.deploy $REPO
 
 # Cleanup
-rm $REPO/"$TARGET"_build_artifact
-rm $REPO/eris-client
+rm $REPO/target/docker/eris-db.dockerartefact
+rm $REPO/target/docker/eris-client.dockerartefact
 
 # Extra Tags
 if [[ "$branch" = "release" ]]
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))
+}
diff --git a/version/version.go b/version/version.go
index 36e4f305ab6f03fc4c49b20d7dfb4f8d9e95254c..d38381a33ca53bc9a691a7c78286a93f53ea7ae6 100644
--- a/version/version.go
+++ b/version/version.go
@@ -32,9 +32,9 @@ const (
 	// Major version component of the current release
 	erisVersionMajor = 0
 	// Minor version component of the current release
-	erisVersionMinor = 12
+	erisVersionMinor = 16
 	// Patch version component of the current release
-	erisVersionPatch = 1
+	erisVersionPatch = 0
 )
 
 var erisVersion *VersionIdentifier
@@ -129,4 +129,4 @@ func (version *VersionIdentifier) MatchesMinorVersion(
 
 // IMPORTANT: Eris-DB version must be on the last line of this file for
 // the deployment script tests/build_tool.sh to pick up the right label.
-const VERSION = "0.12.1"
+const VERSION = "0.16.0"