diff --git a/Gopkg.lock b/Gopkg.lock
index afab6930cff90332d0d79f55adc8f24ef6a55064..4267290a7b3c05a706ffc813139ff873f0327cde 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -349,6 +349,22 @@
   revision = "b4c50a2b199d93b13dc15e78929cfb23bfdf21ab"
   version = "v1.1.1"
 
+[[projects]]
+  name = "github.com/wayn3h0/go-uuid"
+  packages = [
+    ".",
+    "internal/dcesecurity",
+    "internal/layout",
+    "internal/namebased",
+    "internal/namebased/md5",
+    "internal/namebased/sha1",
+    "internal/random",
+    "internal/timebased",
+    "internal/version"
+  ]
+  revision = "1622016a49b50139b1ac263e6ef2804226b3dec6"
+  version = "v2.2.1"
+
 [[projects]]
   branch = "master"
   name = "golang.org/x/crypto"
@@ -458,6 +474,6 @@
 [solve-meta]
   analyzer-name = "dep"
   analyzer-version = 1
-  inputs-digest = "0618ffbeb3a7333a1d9e7b24f256ac5b7719f25957d07cfd935156b355b2e5e2"
+  inputs-digest = "ecec019d5ac7140b4607333907f5a4b211fb3551f7cfe51b92e6cc8e3c515488"
   solver-name = "gps-cdcl"
   solver-version = 1
diff --git a/cmd/burrow/commands/configure.go b/cmd/burrow/commands/configure.go
new file mode 100644
index 0000000000000000000000000000000000000000..de13421a319bce30a6291396e7fe44dfd4971c50
--- /dev/null
+++ b/cmd/burrow/commands/configure.go
@@ -0,0 +1,173 @@
+package commands
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+
+	"github.com/hyperledger/burrow/config"
+	"github.com/hyperledger/burrow/config/source"
+	"github.com/hyperledger/burrow/execution"
+	"github.com/hyperledger/burrow/genesis"
+	"github.com/hyperledger/burrow/genesis/spec"
+	"github.com/hyperledger/burrow/keys"
+	"github.com/hyperledger/burrow/keys/mock"
+	"github.com/hyperledger/burrow/logging"
+	logging_config "github.com/hyperledger/burrow/logging/config"
+	"github.com/hyperledger/burrow/logging/config/presets"
+	"github.com/jawher/mow.cli"
+)
+
+func Configure(cmd *cli.Cmd) {
+	genesisSpecOpt := cmd.StringOpt("s genesis-spec", "",
+		"A GenesisSpec to use as a template for a GenesisDoc that will be created along with keys")
+
+	jsonOutOpt := cmd.BoolOpt("j json", false, "Emit config in JSON rather than TOML "+
+		"suitable for further processing")
+
+	keysUrlOpt := cmd.StringOpt("k keys-url", "", fmt.Sprintf("Provide keys URL, default: %s",
+		keys.DefaultKeysConfig().URL))
+
+	configOpt := cmd.StringOpt("c base-config", "", "Use the a specified burrow config file as a base")
+
+	genesisDocOpt := cmd.StringOpt("g genesis-doc", "", "GenesisDoc in JSON or TOML to embed in config")
+
+	generateKeysOpt := cmd.StringOpt("x generate-keys", "",
+		"File to output containing secret keys as JSON or according to a custom template (see --keys-template). "+
+			"Note that using this options means the keys will not be generated in the default keys instance")
+
+	keysTemplateOpt := cmd.StringOpt("z keys-template", mock.DefaultDumpKeysFormat,
+		fmt.Sprintf("Go text/template template (left delim: %s right delim: %s) to generate secret keys "+
+			"file specified with --generate-keys.", mock.LeftTemplateDelim, mock.RightTemplateDelim))
+
+	separateGenesisDoc := cmd.StringOpt("w separate-genesis-doc", "", "Emit a separate genesis doc as JSON or TOML")
+
+	loggingOpt := cmd.StringOpt("l logging", "",
+		"Comma separated list of logging instructions which form a 'program' which is a depth-first "+
+			"pre-order of instructions that will build the root logging sink. See 'burrow help' for more information.")
+
+	describeLoggingOpt := cmd.BoolOpt("describe-logging", false,
+		"Print an exhaustive list of logging instructions available with the --logging option")
+
+	debugOpt := cmd.BoolOpt("d debug", false, "Include maximal debug options in config "+
+		"including logging opcodes and dumping EVM tokens to disk these can be later pruned from the "+
+		"generated config.")
+
+	chainNameOpt := cmd.StringOpt("n chain-name", "", "Default chain name")
+
+	cmd.Spec = "[--keys-url=<keys URL> | (--generate-keys=<secret keys files> [--keys-template=<text template for each key>])] " +
+		"[--genesis-spec=<GenesisSpec file> | --genesis-doc=<GenesisDoc file>] " +
+		"[--separate-genesis-doc=<genesis JSON file>] [--chain-name] [--json] " +
+		"[--logging=<logging program>] [--describe-logging] [--debug]"
+
+	cmd.Action = func() {
+		conf := config.DefaultBurrowConfig()
+
+		if *configOpt != "" {
+			// If explicitly given a config file use it as a base:
+			err := source.FromFile(*configOpt, conf)
+			if err != nil {
+				fatalf("could not read base config file (as TOML): %v", err)
+			}
+		}
+
+		if *describeLoggingOpt {
+			fmt.Printf("Usage:\n  burrow configure -l INSTRUCTION[,...]\n\nBuilds a logging " +
+				"configuration by constructing a tree of logging sinks assembled from preset instructions " +
+				"that generate the tree while traversing it.\n\nLogging Instructions:\n")
+			for _, instruction := range presets.Instructons() {
+				fmt.Printf("  %-15s\t%s\n", instruction.Name(), instruction.Description())
+			}
+			fmt.Printf("\nExample Usage:\n  burrow configure -l include-any,info,stderr\n")
+			return
+		}
+
+		if *keysUrlOpt != "" {
+			conf.Keys.URL = *keysUrlOpt
+		}
+
+		// Genesis Spec
+		if *genesisSpecOpt != "" {
+			genesisSpec := new(spec.GenesisSpec)
+			err := source.FromFile(*genesisSpecOpt, genesisSpec)
+			if err != nil {
+				fatalf("Could not read GenesisSpec: %v", err)
+			}
+			if *generateKeysOpt != "" {
+				keyClient := mock.NewMockKeyClient()
+				conf.GenesisDoc, err = genesisSpec.GenesisDoc(keyClient)
+				if err != nil {
+					fatalf("Could not generate GenesisDoc from GenesisSpec using MockKeyClient: %v", err)
+				}
+
+				secretKeysString, err := keyClient.DumpKeys(*keysTemplateOpt)
+				if err != nil {
+					fatalf("Could not dump keys: %v", err)
+				}
+				err = ioutil.WriteFile(*generateKeysOpt, []byte(secretKeysString), 0700)
+				if err != nil {
+					fatalf("Could not write secret keys: %v", err)
+				}
+			} else {
+				conf.GenesisDoc, err = genesisSpec.GenesisDoc(keys.NewKeyClient(conf.Keys.URL, logging.NewNoopLogger()))
+			}
+			if err != nil {
+				fatalf("could not realise GenesisSpec: %v", err)
+			}
+		} else if *genesisDocOpt != "" {
+			genesisDoc := new(genesis.GenesisDoc)
+			err := source.FromFile(*genesisSpecOpt, genesisDoc)
+			if err != nil {
+				fatalf("could not read GenesisSpec: %v", err)
+			}
+			conf.GenesisDoc = genesisDoc
+		}
+
+		// Logging
+		if *loggingOpt != "" {
+			ops := strings.Split(*loggingOpt, ",")
+			sinkConfig, err := presets.BuildSinkConfig(ops...)
+			if err != nil {
+				fatalf("could not build logging configuration: %v\n\nTo see possible logging "+
+					"instructions run:\n  burrow configure --describe-logging", err)
+			}
+			conf.Logging = &logging_config.LoggingConfig{
+				RootSink: sinkConfig,
+			}
+		}
+
+		if *debugOpt {
+			conf.Execution = &execution.ExecutionConfig{
+				VMOptions: []execution.VMOption{execution.DumpTokens, execution.DebugOpcodes},
+			}
+		}
+
+		if *chainNameOpt != "" {
+			if conf.GenesisDoc == nil {
+				fatalf("Unable to set ChainName since no GenesisDoc/GenesisSpec provided.")
+			}
+			conf.GenesisDoc.ChainName = *chainNameOpt
+		}
+
+		if *separateGenesisDoc != "" {
+			if conf.GenesisDoc == nil {
+				fatalf("Cannot write separate genesis doc since no GenesisDoc/GenesisSpec provided.")
+			}
+			genesisDocJSON, err := conf.GenesisDoc.JSONBytes()
+			if err != nil {
+				fatalf("Could not form GenesisDoc JSON: %v", err)
+			}
+			err = ioutil.WriteFile(*separateGenesisDoc, genesisDocJSON, 0700)
+			if err != nil {
+				fatalf("Could not write GenesisDoc JSON: %v", err)
+			}
+			conf.GenesisDoc = nil
+		}
+		if *jsonOutOpt {
+			os.Stdout.WriteString(conf.JSONString())
+		} else {
+			os.Stdout.WriteString(conf.TOMLString())
+		}
+	}
+}
diff --git a/cmd/burrow/commands/helpers.go b/cmd/burrow/commands/helpers.go
new file mode 100644
index 0000000000000000000000000000000000000000..232c1f2eb33229e03cf56c43972d5f5c86963d91
--- /dev/null
+++ b/cmd/burrow/commands/helpers.go
@@ -0,0 +1,52 @@
+package commands
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/hyperledger/burrow/config"
+	"github.com/hyperledger/burrow/config/source"
+	"github.com/hyperledger/burrow/genesis"
+)
+
+// Print informational output to Stderr
+func printf(format string, args ...interface{}) {
+	fmt.Fprintf(os.Stderr, format+"\n", args...)
+}
+
+func fatalf(format string, args ...interface{}) {
+	fmt.Fprintf(os.Stderr, format+"\n", args...)
+	os.Exit(1)
+}
+
+func burrowConfigProvider(configFile string) source.ConfigProvider {
+	return source.FirstOf(
+		// Will fail if file doesn't exist, but still skipped it configFile == ""
+		source.File(configFile, false),
+		source.Environment(config.DefaultBurrowConfigJSONEnvironmentVariable),
+		// Try working directory
+		source.File(config.DefaultBurrowConfigTOMLFileName, true),
+		source.Default(config.DefaultBurrowConfig()))
+}
+
+func genesisDocProvider(genesisFile string, skipNonExistent bool) source.ConfigProvider {
+	return source.NewConfigProvider(fmt.Sprintf("genesis file at %s", genesisFile),
+		source.ShouldSkipFile(genesisFile, skipNonExistent),
+		func(baseConfig interface{}) error {
+			conf, ok := baseConfig.(*config.BurrowConfig)
+			if !ok {
+				return fmt.Errorf("config passed was not BurrowConfig")
+			}
+			if conf.GenesisDoc != nil {
+				return fmt.Errorf("sourcing GenesisDoc from file %v, but GenesisDoc was defined in earlier "+
+					"config source, only specify GenesisDoc in one place", genesisFile)
+			}
+			genesisDoc := new(genesis.GenesisDoc)
+			err := source.FromFile(genesisFile, genesisDoc)
+			if err != nil {
+				return err
+			}
+			conf.GenesisDoc = genesisDoc
+			return nil
+		})
+}
diff --git a/cmd/burrow/commands/spec.go b/cmd/burrow/commands/spec.go
new file mode 100644
index 0000000000000000000000000000000000000000..634006311b0a54082ed4266f7edfb2baed406b5d
--- /dev/null
+++ b/cmd/burrow/commands/spec.go
@@ -0,0 +1,67 @@
+package commands
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/hyperledger/burrow/config/source"
+	"github.com/hyperledger/burrow/genesis/spec"
+	"github.com/jawher/mow.cli"
+)
+
+func Spec(cmd *cli.Cmd) {
+	tomlOpt := cmd.BoolOpt("t toml", false, "Emit GenesisSpec as TOML rather than the "+
+		"default JSON")
+
+	baseSpecsArg := cmd.StringsArg("BASE", nil, "Provide a base GenesisSpecs on top of which any "+
+		"additional GenesisSpec presets specified by other flags will be merged. GenesisSpecs appearing "+
+		"later take precedent over those appearing early if multiple --base flags are provided")
+
+	accountNamePrefixOpt := cmd.StringOpt("name-prefix", "", "Prefix added to the names of accounts in GenesisSpec")
+	fullOpt := cmd.IntOpt("f full-accounts", 0, "Number of preset Full type accounts")
+	validatorOpt := cmd.IntOpt("v validator-accounts", 0, "Number of preset Validator type accounts")
+	rootOpt := cmd.IntOpt("r root-accounts", 0, "Number of preset Root type accounts")
+	developerOpt := cmd.IntOpt("d developer-accounts", 0, "Number of preset Developer type accounts")
+	participantsOpt := cmd.IntOpt("p participant-accounts", 0, "Number of preset Participant type accounts")
+	chainNameOpt := cmd.StringOpt("n chain-name", "", "Default chain name")
+
+	cmd.Spec = "[--full-accounts] [--validator-accounts] [--root-accounts] [--developer-accounts] " +
+		"[--participant-accounts] [--chain-name] [--toml] [BASE...]"
+
+	cmd.Action = func() {
+		specs := make([]spec.GenesisSpec, 0, *participantsOpt+*fullOpt)
+		for _, baseSpec := range *baseSpecsArg {
+			genesisSpec := new(spec.GenesisSpec)
+			err := source.FromFile(baseSpec, genesisSpec)
+			if err != nil {
+				fatalf("could not read GenesisSpec: %v", err)
+			}
+			specs = append(specs, *genesisSpec)
+		}
+		for i := 0; i < *fullOpt; i++ {
+			specs = append(specs, spec.FullAccount(fmt.Sprintf("%sFull_%v", *accountNamePrefixOpt, i)))
+		}
+		for i := 0; i < *validatorOpt; i++ {
+			specs = append(specs, spec.ValidatorAccount(fmt.Sprintf("%sValidator_%v", *accountNamePrefixOpt, i)))
+		}
+		for i := 0; i < *rootOpt; i++ {
+			specs = append(specs, spec.RootAccount(fmt.Sprintf("%sRoot_%v", *accountNamePrefixOpt, i)))
+		}
+		for i := 0; i < *developerOpt; i++ {
+			specs = append(specs, spec.DeveloperAccount(fmt.Sprintf("%sDeveloper_%v", *accountNamePrefixOpt, i)))
+		}
+		for i := 0; i < *participantsOpt; i++ {
+			specs = append(specs, spec.ParticipantAccount(fmt.Sprintf("%sParticipant_%v", *accountNamePrefixOpt, i)))
+		}
+		genesisSpec := spec.MergeGenesisSpecs(specs...)
+		if *chainNameOpt != "" {
+			genesisSpec.ChainName = *chainNameOpt
+		}
+		if *tomlOpt {
+			os.Stdout.WriteString(source.TOMLString(genesisSpec))
+		} else {
+			os.Stdout.WriteString(source.JSONString(genesisSpec))
+		}
+		os.Stdout.WriteString("\n")
+	}
+}
diff --git a/cmd/burrow/commands/start.go b/cmd/burrow/commands/start.go
new file mode 100644
index 0000000000000000000000000000000000000000..5efb282a6b8e8eed18b0bbe985df051fa3ecacd5
--- /dev/null
+++ b/cmd/burrow/commands/start.go
@@ -0,0 +1,113 @@
+package commands
+
+import (
+	"context"
+
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/config"
+	"github.com/hyperledger/burrow/config/source"
+	logging_config "github.com/hyperledger/burrow/logging/config"
+	"github.com/jawher/mow.cli"
+)
+
+func Start(cmd *cli.Cmd) {
+	genesisOpt := cmd.StringOpt("g genesis", "",
+		"Use the specified genesis JSON file rather than a key in the main config, use - to read from STDIN")
+
+	configOpt := cmd.StringOpt("c config", "", "Use the a specified burrow config file")
+
+	validatorIndexOpt := cmd.Int(cli.IntOpt{
+		Name:   "v validator-index",
+		Desc:   "Validator index (in validators list - GenesisSpec or GenesisDoc) from which to set ValidatorAddress",
+		Value:  -1,
+		EnvVar: "BURROW_VALIDATOR_INDEX",
+	})
+
+	validatorAddressOpt := cmd.String(cli.StringOpt{
+		Name:   "a validator-address",
+		Desc:   "The address of the the signing key of this validator",
+		EnvVar: "BURROW_VALIDATOR_ADDRESS",
+	})
+
+	validatorPassphraseOpt := cmd.String(cli.StringOpt{
+		Name:   "p validator-passphrase",
+		Desc:   "The passphrase of the signing key of this validator (currently unimplemented but planned for future version of our KeyClient interface)",
+		EnvVar: "BURROW_VALIDATOR_PASSPHRASE",
+	})
+
+	validatorMonikerOpt := cmd.String(cli.StringOpt{
+		Name:   "m validator-moniker",
+		Desc:   "An optional human-readable moniker to identify this validator amongst Tendermint peers in logs and status queries",
+		EnvVar: "BURROW_VALIDATOR_MONIKER",
+	})
+
+	cmd.Spec = "[--config=<config file>] [--validator-moniker=<human readable moniker>] " +
+		"[--validator-index=<index of validator in GenesisDoc> | --validator-address=<address of validator signing key>] " +
+		"[--genesis=<genesis json file>]"
+
+	cmd.Action = func() {
+
+		// We need to reflect on whether this obscures where values are coming from
+		conf := config.DefaultBurrowConfig()
+		// We treat logging a little differently in that if anything is set for logging we will not
+		// set default outputs
+		conf.Logging = nil
+		err := source.EachOf(
+			burrowConfigProvider(*configOpt),
+			source.FirstOf(
+				genesisDocProvider(*genesisOpt, false),
+				// Try working directory
+				genesisDocProvider(config.DefaultGenesisDocJSONFileName, true)),
+		).Apply(conf)
+
+		// If no logging config was provided use the default
+		if conf.Logging == nil {
+			conf.Logging = logging_config.DefaultNodeLoggingConfig()
+		}
+		if err != nil {
+			fatalf("could not obtain config: %v", err)
+		}
+
+		// Which validator am I?
+		if *validatorAddressOpt != "" {
+			address, err := acm.AddressFromHexString(*validatorAddressOpt)
+			if err != nil {
+				fatalf("could not read address for validator in '%s'", *validatorAddressOpt)
+			}
+			conf.ValidatorAddress = &address
+		} else if *validatorIndexOpt > -1 {
+			if conf.GenesisDoc == nil {
+				fatalf("Unable to set ValidatorAddress from provided validator-index since no " +
+					"GenesisDoc/GenesisSpec provided.")
+			}
+			if *validatorIndexOpt >= len(conf.GenesisDoc.Validators) {
+				fatalf("validator-index of %v given but only %v validators specified in GenesisDoc",
+					*validatorIndexOpt, len(conf.GenesisDoc.Validators))
+			}
+			conf.ValidatorAddress = &conf.GenesisDoc.Validators[*validatorIndexOpt].Address
+			printf("Using validator index %v (address: %s)", *validatorIndexOpt, *conf.ValidatorAddress)
+		}
+
+		if *validatorPassphraseOpt != "" {
+			conf.ValidatorPassphrase = validatorPassphraseOpt
+		}
+
+		if *validatorMonikerOpt != "" {
+			conf.Tendermint.Moniker = *validatorMonikerOpt
+		}
+
+		ctx, cancel := context.WithCancel(context.Background())
+		defer cancel()
+
+		kern, err := conf.Kernel(ctx)
+		if err != nil {
+			fatalf("could not create Burrow kernel: %v", err)
+		}
+
+		err = kern.Boot()
+		if err != nil {
+			fatalf("could not boot Burrow kernel: %v", err)
+		}
+		kern.WaitForShutdown()
+	}
+}
diff --git a/cmd/burrow/main.go b/cmd/burrow/main.go
index ef1956c6b5acfa63bac393e2006cb32f02db005b..6c57758674250090e19c3d0f386248942bf10cb4 100644
--- a/cmd/burrow/main.go
+++ b/cmd/burrow/main.go
@@ -1,23 +1,10 @@
 package main
 
 import (
-	"context"
 	"fmt"
-	"io/ioutil"
 	"os"
-	"strings"
 
-	acm "github.com/hyperledger/burrow/account"
-	"github.com/hyperledger/burrow/config"
-	"github.com/hyperledger/burrow/config/source"
-	"github.com/hyperledger/burrow/execution"
-	"github.com/hyperledger/burrow/genesis"
-	"github.com/hyperledger/burrow/genesis/spec"
-	"github.com/hyperledger/burrow/keys"
-	"github.com/hyperledger/burrow/keys/mock"
-	"github.com/hyperledger/burrow/logging"
-	logging_config "github.com/hyperledger/burrow/logging/config"
-	"github.com/hyperledger/burrow/logging/config/presets"
+	"github.com/hyperledger/burrow/cmd/burrow/commands"
 	"github.com/hyperledger/burrow/project"
 	"github.com/jawher/mow.cli"
 )
@@ -39,373 +26,15 @@ func burrow() *cli.Cli {
 		}
 	}
 
-	app.Command("serve", "",
-		func(cmd *cli.Cmd) {
-			genesisOpt := cmd.StringOpt("g genesis", "",
-				"Use the specified genesis JSON file rather than a key in the main config, use - to read from STDIN")
+	app.Command("start", "Start a Burrow node",
+		commands.Start)
 
-			configOpt := cmd.StringOpt("c config", "", "Use the a specified burrow config file")
-
-			validatorIndexOpt := cmd.Int(cli.IntOpt{
-				Name:   "v validator-index",
-				Desc:   "Validator index (in validators list - GenesisSpec or GenesisDoc) from which to set ValidatorAddress",
-				Value:  -1,
-				EnvVar: "BURROW_VALIDATOR_INDEX",
-			})
-
-			validatorAddressOpt := cmd.String(cli.StringOpt{
-				Name:   "a validator-address",
-				Desc:   "The address of the the signing key of this validator",
-				EnvVar: "BURROW_VALIDATOR_ADDRESS",
-			})
-
-			validatorPassphraseOpt := cmd.String(cli.StringOpt{
-				Name:   "p validator-passphrase",
-				Desc:   "The passphrase of the signing key of this validator (currently unimplemented but planned for future version of our KeyClient interface)",
-				EnvVar: "BURROW_VALIDATOR_PASSPHRASE",
-			})
-
-			validatorMonikerOpt := cmd.String(cli.StringOpt{
-				Name:   "m validator-moniker",
-				Desc:   "An optional human-readable moniker to identify this validator amongst Tendermint peers in logs and status queries",
-				EnvVar: "BURROW_VALIDATOR_MONIKER",
-			})
-
-			cmd.Spec = "[--config=<config file>] [--validator-moniker=<human readable moniker>] " +
-				"[--validator-index=<index of validator in GenesisDoc> | --validator-address=<address of validator signing key>] " +
-				"[--genesis=<genesis json file>]"
-
-			cmd.Action = func() {
-
-				// We need to reflect on whether this obscures where values are coming from
-				conf := config.DefaultBurrowConfig()
-				// We treat logging a little differently in that if anything is set for logging we will not
-				// set default outputs
-				conf.Logging = nil
-				err := source.EachOf(
-					burrowConfigProvider(*configOpt),
-					source.FirstOf(
-						genesisDocProvider(*genesisOpt, false),
-						// Try working directory
-						genesisDocProvider(config.DefaultGenesisDocJSONFileName, true)),
-				).Apply(conf)
-
-				// If no logging config was provided use the default
-				if conf.Logging == nil {
-					conf.Logging = logging_config.DefaultNodeLoggingConfig()
-				}
-				if err != nil {
-					fatalf("could not obtain config: %v", err)
-				}
-
-				// Which validator am I?
-				if *validatorAddressOpt != "" {
-					address, err := acm.AddressFromHexString(*validatorAddressOpt)
-					if err != nil {
-						fatalf("could not read address for validator in '%s'", *validatorAddressOpt)
-					}
-					conf.ValidatorAddress = &address
-				} else if *validatorIndexOpt > -1 {
-					if conf.GenesisDoc == nil {
-						fatalf("Unable to set ValidatorAddress from provided validator-index since no " +
-							"GenesisDoc/GenesisSpec provided.")
-					}
-					if *validatorIndexOpt >= len(conf.GenesisDoc.Validators) {
-						fatalf("validator-index of %v given but only %v validators specified in GenesisDoc",
-							*validatorIndexOpt, len(conf.GenesisDoc.Validators))
-					}
-					conf.ValidatorAddress = &conf.GenesisDoc.Validators[*validatorIndexOpt].Address
-					printf("Using validator index %v (address: %s)", *validatorIndexOpt, *conf.ValidatorAddress)
-				}
-
-				if *validatorPassphraseOpt != "" {
-					conf.ValidatorPassphrase = validatorPassphraseOpt
-				}
-
-				if *validatorMonikerOpt != "" {
-					conf.Tendermint.Moniker = *validatorMonikerOpt
-				}
-
-				ctx, cancel := context.WithCancel(context.Background())
-				defer cancel()
-
-				kern, err := conf.Kernel(ctx)
-				if err != nil {
-					fatalf("could not create Burrow kernel: %v", err)
-				}
-
-				err = kern.Boot()
-				if err != nil {
-					fatalf("could not boot Burrow kernel: %v", err)
-				}
-				kern.WaitForShutdown()
-			}
-		})
-
-	app.Command("spec",
-		"Build a GenesisSpec that acts as a template for a GenesisDoc and the configure command",
-		func(cmd *cli.Cmd) {
-			tomlOpt := cmd.BoolOpt("t toml", false, "Emit GenesisSpec as TOML rather than the "+
-				"default JSON")
-
-			baseOpt := cmd.StringsOpt("b base", nil, "Provide a base GenesisSpecs on top of which any "+
-				"additional GenesisSpec presets specified by other flags will be merged. GenesisSpecs appearing "+
-				"later take precedent over those appearing early if multiple --base flags are provided")
-
-			fullOpt := cmd.IntOpt("f full-accounts", 1, "Number of preset Full type accounts")
-			validatorOpt := cmd.IntOpt("v validator-accounts", 0, "Number of preset Validator type accounts")
-			rootOpt := cmd.IntOpt("r root-accounts", 0, "Number of preset Root type accounts")
-			developerOpt := cmd.IntOpt("d developer-accounts", 0, "Number of preset Developer type accounts")
-			participantsOpt := cmd.IntOpt("p participant-accounts", 1, "Number of preset Participant type accounts")
-			chainNameOpt := cmd.StringOpt("n chain-name", "", "Default chain name")
-
-			cmd.Spec = "[--base][--full-accounts] [--validator-accounts] [--root-accounts] [--developer-accounts] " +
-				"[--participant-accounts] [--chain-name] [--toml]"
-
-			cmd.Action = func() {
-				specs := make([]spec.GenesisSpec, 0, *participantsOpt+*fullOpt)
-				for _, baseSpec := range *baseOpt {
-					genesisSpec := new(spec.GenesisSpec)
-					err := source.FromFile(baseSpec, genesisSpec)
-					if err != nil {
-						fatalf("could not read GenesisSpec: %v", err)
-					}
-					specs = append(specs, *genesisSpec)
-				}
-				for i := 0; i < *fullOpt; i++ {
-					specs = append(specs, spec.FullAccount(i))
-				}
-				for i := 0; i < *validatorOpt; i++ {
-					specs = append(specs, spec.ValidatorAccount(i))
-				}
-				for i := 0; i < *rootOpt; i++ {
-					specs = append(specs, spec.RootAccount(i))
-				}
-				for i := 0; i < *developerOpt; i++ {
-					specs = append(specs, spec.DeveloperAccount(i))
-				}
-				for i := 0; i < *participantsOpt; i++ {
-					specs = append(specs, spec.ParticipantAccount(i))
-				}
-				genesisSpec := spec.MergeGenesisSpecs(specs...)
-				if *chainNameOpt != "" {
-					genesisSpec.ChainName = *chainNameOpt
-				}
-				if *tomlOpt {
-					os.Stdout.WriteString(source.TOMLString(genesisSpec))
-				} else {
-					os.Stdout.WriteString(source.JSONString(genesisSpec))
-				}
-			}
-		})
+	app.Command("spec", "Build a GenesisSpec that acts as a template for a GenesisDoc and the configure command",
+		commands.Spec)
 
 	app.Command("configure",
 		"Create Burrow configuration by consuming a GenesisDoc or GenesisSpec, creating keys, and emitting the config",
-		func(cmd *cli.Cmd) {
-			genesisSpecOpt := cmd.StringOpt("s genesis-spec", "",
-				"A GenesisSpec to use as a template for a GenesisDoc that will be created along with keys")
-
-			jsonOutOpt := cmd.BoolOpt("j json-out", false, "Emit config in JSON rather than TOML "+
-				"suitable for further processing or forming a separate genesis.json GenesisDoc")
-
-			keysUrlOpt := cmd.StringOpt("k keys-url", "", fmt.Sprintf("Provide keys URL, default: %s",
-				keys.DefaultKeysConfig().URL))
-
-			configOpt := cmd.StringOpt("c base-config", "", "Use the a specified burrow config file as a base")
-
-			genesisDocOpt := cmd.StringOpt("g genesis-doc", "", "GenesisDoc in JSON or TOML to embed in config")
-
-			generateKeysOpt := cmd.StringOpt("x generate-keys", "",
-				"File to output containing secret keys as JSON or according to a custom template (see --keys-template). "+
-					"Note that using this options means the keys will not be generated in the default keys instance")
-
-			keysTemplateOpt := cmd.StringOpt("z keys-template", mock.DefaultDumpKeysFormat,
-				fmt.Sprintf("Go text/template template (left delim: %s right delim: %s) to generate secret keys "+
-					"file specified with --generate-keys. Default:\n%s", mock.LeftTemplateDelim, mock.RightTemplateDelim,
-					mock.DefaultDumpKeysFormat))
-
-			separateGenesisDoc := cmd.StringOpt("w separate-genesis-doc", "", "Emit a separate genesis doc as JSON or TOML")
-
-			loggingOpt := cmd.StringOpt("l logging", "",
-				"Comma separated list of logging instructions which form a 'program' which is a depth-first "+
-					"pre-order of instructions that will build the root logging sink. See 'burrow help' for more information.")
-
-			describeLoggingOpt := cmd.BoolOpt("describe-logging", false,
-				"Print an exhaustive list of logging instructions available with the --logging option")
-
-			debugOpt := cmd.BoolOpt("d debug", false, "Include maximal debug options in config "+
-				"including logging opcodes and dumping EVM tokens to disk these can be later pruned from the "+
-				"generated config.")
-
-			chainNameOpt := cmd.StringOpt("n chain-name", "", "Default chain name")
-
-			cmd.Spec = "[--keys-url=<keys URL> | (--generate-keys=<secret keys files> [--keys-template=<text template for each key>])] " +
-				"[--genesis-spec=<GenesisSpec file> | --genesis-doc=<GenesisDoc file>] " +
-				"[--separate-genesis-doc=<genesis JSON file>] [--chain-name] [--json-out] " +
-				"[--logging=<logging program>] [--describe-logging] [--debug]"
-
-			cmd.Action = func() {
-				conf := config.DefaultBurrowConfig()
-
-				if *configOpt != "" {
-					// If explicitly given a config file use it as a base:
-					err := source.FromFile(*configOpt, conf)
-					if err != nil {
-						fatalf("could not read base config file (as TOML): %v", err)
-					}
-				}
-
-				if *describeLoggingOpt {
-					fmt.Printf("Usage:\n  burrow configure -l INSTRUCTION[,...]\n\nBuilds a logging " +
-						"configuration by constructing a tree of logging sinks assembled from preset instructions " +
-						"that generate the tree while traversing it.\n\nLogging Instructions:\n")
-					for _, instruction := range presets.Instructons() {
-						fmt.Printf("  %-15s\t%s\n", instruction.Name(), instruction.Description())
-					}
-					fmt.Printf("\nExample Usage:\n  burrow configure -l include-any,info,stderr\n")
-					return
-				}
-
-				if *keysUrlOpt != "" {
-					conf.Keys.URL = *keysUrlOpt
-				}
-
-				// Genesis Spec
-				if *genesisSpecOpt != "" {
-					genesisSpec := new(spec.GenesisSpec)
-					err := source.FromFile(*genesisSpecOpt, genesisSpec)
-					if err != nil {
-						fatalf("Could not read GenesisSpec: %v", err)
-					}
-					if *generateKeysOpt != "" {
-						keyClient := mock.NewMockKeyClient()
-						conf.GenesisDoc, err = genesisSpec.GenesisDoc(keyClient)
-						if err != nil {
-							fatalf("Could not generate GenesisDoc from GenesisSpec using MockKeyClient: %v", err)
-						}
-
-						secretKeysString, err := keyClient.DumpKeys(*keysTemplateOpt)
-						if err != nil {
-							fatalf("Could not dump keys: %v", err)
-						}
-						err = ioutil.WriteFile(*generateKeysOpt, []byte(secretKeysString), 0700)
-						if err != nil {
-							fatalf("Could not write secret keys: %v", err)
-						}
-					} else {
-						conf.GenesisDoc, err = genesisSpec.GenesisDoc(keys.NewKeyClient(conf.Keys.URL, logging.NewNoopLogger()))
-					}
-					if err != nil {
-						fatalf("could not realise GenesisSpec: %v", err)
-					}
-				} else if *genesisDocOpt != "" {
-					genesisDoc := new(genesis.GenesisDoc)
-					err := source.FromFile(*genesisSpecOpt, genesisDoc)
-					if err != nil {
-						fatalf("could not read GenesisSpec: %v", err)
-					}
-					conf.GenesisDoc = genesisDoc
-				}
-
-				// Logging
-				if *loggingOpt != "" {
-					ops := strings.Split(*loggingOpt, ",")
-					sinkConfig, err := presets.BuildSinkConfig(ops...)
-					if err != nil {
-						fatalf("could not build logging configuration: %v\n\nTo see possible logging "+
-							"instructions run:\n  burrow configure --describe-logging", err)
-					}
-					conf.Logging = &logging_config.LoggingConfig{
-						RootSink: sinkConfig,
-					}
-				}
-
-				if *debugOpt {
-					conf.Execution = &execution.ExecutionConfig{
-						VMOptions: []execution.VMOption{execution.DumpTokens, execution.DebugOpcodes},
-					}
-				}
-
-				if *chainNameOpt != "" {
-					if conf.GenesisDoc == nil {
-						fatalf("Unable to set ChainName since no GenesisDoc/GenesisSpec provided.")
-					}
-					conf.GenesisDoc.ChainName = *chainNameOpt
-				}
-
-				if *separateGenesisDoc != "" {
-					if conf.GenesisDoc == nil {
-						fatalf("Cannot write separate genesis doc since no GenesisDoc/GenesisSpec provided.")
-					}
-					genesisDocJSON, err := conf.GenesisDoc.JSONBytes()
-					if err != nil {
-						fatalf("Could not form GenesisDoc JSON: %v", err)
-					}
-					err = ioutil.WriteFile(*separateGenesisDoc, genesisDocJSON, 0700)
-					if err != nil {
-						fatalf("Could not write GenesisDoc JSON: %v", err)
-					}
-					conf.GenesisDoc = nil
-				}
-				if *jsonOutOpt {
-					os.Stdout.WriteString(conf.JSONString())
-				} else {
-					os.Stdout.WriteString(conf.TOMLString())
-				}
-			}
-		})
-
-	app.Command("help",
-		"Get more detailed or exhaustive options of selected commands or flags.",
-		func(cmd *cli.Cmd) {
-
-			cmd.Spec = "[--participant-accounts] [--full-accounts] [--toml]"
-
-			cmd.Action = func() {
-			}
-		})
+		commands.Configure)
 
 	return app
 }
-
-// Print informational output to Stderr
-func printf(format string, args ...interface{}) {
-	fmt.Fprintf(os.Stderr, format+"\n", args...)
-}
-
-func fatalf(format string, args ...interface{}) {
-	fmt.Fprintf(os.Stderr, format+"\n", args...)
-	os.Exit(1)
-}
-
-func burrowConfigProvider(configFile string) source.ConfigProvider {
-	return source.FirstOf(
-		// Will fail if file doesn't exist, but still skipped it configFile == ""
-		source.File(configFile, false),
-		source.Environment(config.DefaultBurrowConfigJSONEnvironmentVariable),
-		// Try working directory
-		source.File(config.DefaultBurrowConfigTOMLFileName, true),
-		source.Default(config.DefaultBurrowConfig()))
-}
-
-func genesisDocProvider(genesisFile string, skipNonExistent bool) source.ConfigProvider {
-	return source.NewConfigProvider(fmt.Sprintf("genesis file at %s", genesisFile),
-		source.ShouldSkipFile(genesisFile, skipNonExistent),
-		func(baseConfig interface{}) error {
-			conf, ok := baseConfig.(*config.BurrowConfig)
-			if !ok {
-				return fmt.Errorf("config passed was not BurrowConfig")
-			}
-			if conf.GenesisDoc != nil {
-				return fmt.Errorf("sourcing GenesisDoc from file %v, but GenesisDoc was defined in earlier "+
-					"config source, only specify GenesisDoc in one place", genesisFile)
-			}
-			genesisDoc := new(genesis.GenesisDoc)
-			err := source.FromFile(genesisFile, genesisDoc)
-			if err != nil {
-				return err
-			}
-			conf.GenesisDoc = genesisDoc
-			return nil
-		})
-}
diff --git a/genesis/spec/genesis_spec.go b/genesis/spec/genesis_spec.go
index c037c5c4f7e6eab92241caed7c41eb1b7d68d1fb..11549a0171f645cf1f2f4a194500170422cdc3c2 100644
--- a/genesis/spec/genesis_spec.go
+++ b/genesis/spec/genesis_spec.go
@@ -24,23 +24,24 @@ const DefaultAmountBonded uint64 = 10000
 // by interacting with the KeysClient it is passed and other information not known at
 // specification time
 type GenesisSpec struct {
-	GenesisTime       *time.Time        `json:",omitempty"`
-	ChainName         string            `json:",omitempty"`
-	Salt              []byte            `json:",omitempty"`
-	GlobalPermissions []string          `json:",omitempty"`
-	Accounts          []TemplateAccount `json:",omitempty"`
+	GenesisTime       *time.Time        `json:",omitempty" toml:",omitempty"`
+	ChainName         string            `json:",omitempty" toml:",omitempty"`
+	Salt              []byte            `json:",omitempty" toml:",omitempty"`
+	GlobalPermissions []string          `json:",omitempty" toml:",omitempty"`
+	Accounts          []TemplateAccount `json:",omitempty" toml:",omitempty"`
 }
 
 type TemplateAccount struct {
+	// Template accounts sharing a name will be merged when merging genesis specs
+	Name string `json:",omitempty" toml:",omitempty"`
 	// Address  is convenient to have in file for reference, but otherwise ignored since derived from PublicKey
-	Address   *acm.Address   `json:",omitempty"`
-	PublicKey *acm.PublicKey `json:",omitempty"`
-	Amount    *uint64        `json:",omitempty"`
+	Address   *acm.Address   `json:",omitempty" toml:",omitempty"`
+	PublicKey *acm.PublicKey `json:",omitempty" toml:",omitempty"`
+	Amount    *uint64        `json:",omitempty" toml:",omitempty"`
 	// If any bonded amount then this account is also a Validator
-	AmountBonded *uint64  `json:",omitempty"`
-	Name         string   `json:",omitempty"`
-	Permissions  []string `json:",omitempty"`
-	Roles        []string `json:",omitempty"`
+	AmountBonded *uint64  `json:",omitempty" toml:",omitempty"`
+	Permissions  []string `json:",omitempty" toml:",omitempty"`
+	Roles        []string `json:",omitempty" toml:",omitempty"`
 }
 
 func (ta TemplateAccount) Validator(keyClient keys.KeyClient, index int) (*genesis.Validator, error) {
diff --git a/genesis/spec/presets.go b/genesis/spec/presets.go
index 0298a44760bf7f04ad58886555de083bcd94b23d..312ec7a79a78888f687210a56e218c32ad16a4e4 100644
--- a/genesis/spec/presets.go
+++ b/genesis/spec/presets.go
@@ -1,8 +1,6 @@
 package spec
 
 import (
-	"fmt"
-
 	"sort"
 
 	"github.com/hyperledger/burrow/permission"
@@ -11,13 +9,13 @@ import (
 // Files here can be used as starting points for building various 'chain types' but are otherwise
 // a fairly unprincipled collection of GenesisSpecs that we find useful in testing and development
 
-func FullAccount(index int) GenesisSpec {
+func FullAccount(name string) GenesisSpec {
 	// Inheriting from the arbitrary figures used by monax tool for now
 	amount := uint64(99999999999999)
 	amountBonded := uint64(9999999999)
 	return GenesisSpec{
 		Accounts: []TemplateAccount{{
-			Name:         fmt.Sprintf("Full_%v", index),
+			Name:         name,
 			Amount:       &amount,
 			AmountBonded: &amountBonded,
 			Permissions:  []string{permission.AllString},
@@ -26,12 +24,12 @@ func FullAccount(index int) GenesisSpec {
 	}
 }
 
-func RootAccount(index int) GenesisSpec {
+func RootAccount(name string) GenesisSpec {
 	// Inheriting from the arbitrary figures used by monax tool for now
 	amount := uint64(99999999999999)
 	return GenesisSpec{
 		Accounts: []TemplateAccount{{
-			Name:        fmt.Sprintf("Root_%v", index),
+			Name:        name,
 			Amount:      &amount,
 			Permissions: []string{permission.AllString},
 		},
@@ -39,12 +37,12 @@ func RootAccount(index int) GenesisSpec {
 	}
 }
 
-func ParticipantAccount(index int) GenesisSpec {
+func ParticipantAccount(name string) GenesisSpec {
 	// Inheriting from the arbitrary figures used by monax tool for now
 	amount := uint64(9999999999)
 	return GenesisSpec{
 		Accounts: []TemplateAccount{{
-			Name:   fmt.Sprintf("Participant_%v", index),
+			Name:   name,
 			Amount: &amount,
 			Permissions: []string{permission.SendString, permission.CallString, permission.NameString,
 				permission.HasRoleString},
@@ -52,12 +50,12 @@ func ParticipantAccount(index int) GenesisSpec {
 	}
 }
 
-func DeveloperAccount(index int) GenesisSpec {
+func DeveloperAccount(name string) GenesisSpec {
 	// Inheriting from the arbitrary figures used by monax tool for now
 	amount := uint64(9999999999)
 	return GenesisSpec{
 		Accounts: []TemplateAccount{{
-			Name:   fmt.Sprintf("Developer_%v", index),
+			Name:   name,
 			Amount: &amount,
 			Permissions: []string{permission.SendString, permission.CallString, permission.CreateContractString,
 				permission.CreateAccountString, permission.NameString, permission.HasRoleString,
@@ -66,13 +64,13 @@ func DeveloperAccount(index int) GenesisSpec {
 	}
 }
 
-func ValidatorAccount(index int) GenesisSpec {
+func ValidatorAccount(name string) GenesisSpec {
 	// Inheriting from the arbitrary figures used by monax tool for now
 	amount := uint64(9999999999)
 	amountBonded := amount - 1
 	return GenesisSpec{
 		Accounts: []TemplateAccount{{
-			Name:         fmt.Sprintf("Validator_%v", index),
+			Name:         name,
 			Amount:       &amount,
 			AmountBonded: &amountBonded,
 			Permissions:  []string{permission.BondString},
@@ -83,7 +81,7 @@ func ValidatorAccount(index int) GenesisSpec {
 func MergeGenesisSpecs(genesisSpecs ...GenesisSpec) GenesisSpec {
 	mergedGenesisSpec := GenesisSpec{}
 	// We will deduplicate and merge global permissions flags
-	permSet := make(map[string]bool)
+	permSet := make(map[string]struct{})
 
 	for _, genesisSpec := range genesisSpecs {
 		// We'll overwrite chain name for later specs
@@ -97,11 +95,11 @@ func MergeGenesisSpecs(genesisSpecs ...GenesisSpec) GenesisSpec {
 		}
 
 		for _, permString := range genesisSpec.GlobalPermissions {
-			permSet[permString] = true
+			permSet[permString] = struct{}{}
 		}
 
 		mergedGenesisSpec.Salt = append(mergedGenesisSpec.Salt, genesisSpec.Salt...)
-		mergedGenesisSpec.Accounts = append(mergedGenesisSpec.Accounts, genesisSpec.Accounts...)
+		mergedGenesisSpec.Accounts = mergeAccounts(mergedGenesisSpec.Accounts, genesisSpec.Accounts)
 	}
 
 	mergedGenesisSpec.GlobalPermissions = make([]string, 0, len(permSet))
@@ -115,3 +113,73 @@ func MergeGenesisSpecs(genesisSpecs ...GenesisSpec) GenesisSpec {
 
 	return mergedGenesisSpec
 }
+
+// Merge accounts by adding to base list or updating previously named account
+func mergeAccounts(bases, overrides []TemplateAccount) []TemplateAccount {
+	indexOfBase := make(map[string]int, len(bases))
+	for i, ta := range bases {
+		if ta.Name != "" {
+			indexOfBase[ta.Name] = i
+		}
+	}
+
+	for _, override := range overrides {
+		if override.Name != "" {
+			if i, ok := indexOfBase[override.Name]; ok {
+				bases[i] = mergeAccount(bases[i], override)
+				continue
+			}
+		}
+		bases = append(bases, override)
+	}
+	return bases
+}
+
+func mergeAccount(base, override TemplateAccount) TemplateAccount {
+	if override.Address != nil {
+		base.Address = override.Address
+	}
+	if override.PublicKey != nil {
+		base.PublicKey = override.PublicKey
+	}
+	if override.Name != "" {
+		base.Name = override.Name
+	}
+
+	base.Amount = addUint64Pointers(base.Amount, override.Amount)
+	base.AmountBonded = addUint64Pointers(base.AmountBonded, override.AmountBonded)
+
+	base.Permissions = mergeStrings(base.Permissions, override.Permissions)
+	base.Roles = mergeStrings(base.Roles, override.Roles)
+	return base
+}
+
+func mergeStrings(as, bs []string) []string {
+	var strs []string
+	strSet := make(map[string]struct{})
+	for _, a := range as {
+		strSet[a] = struct{}{}
+	}
+	for _, b := range bs {
+		strSet[b] = struct{}{}
+	}
+	for str := range strSet {
+		strs = append(strs, str)
+	}
+	sort.Strings(strs)
+	return strs
+}
+
+func addUint64Pointers(a, b *uint64) *uint64 {
+	if a == nil && b == nil {
+		return nil
+	}
+	amt := uint64(0)
+	if a != nil {
+		amt += *a
+	}
+	if b != nil {
+		amt += *b
+	}
+	return &amt
+}
diff --git a/genesis/spec/presets_test.go b/genesis/spec/presets_test.go
index dc65fd9e92c34671439f0d5e21744ee2b83db7dc..b8a40cd00be4c59e487302f48daf1db3053fc674 100644
--- a/genesis/spec/presets_test.go
+++ b/genesis/spec/presets_test.go
@@ -11,14 +11,11 @@ import (
 
 func TestMergeGenesisSpecAccounts(t *testing.T) {
 	keyClient := mock.NewMockKeyClient()
-	gs := MergeGenesisSpecs(FullAccount(0), ParticipantAccount(1), ParticipantAccount(2))
+	gs := MergeGenesisSpecs(FullAccount("0"), ParticipantAccount("1"), ParticipantAccount("2"))
 	gd, err := gs.GenesisDoc(keyClient)
 	require.NoError(t, err)
 	assert.Len(t, gd.Validators, 1)
 	assert.Len(t, gd.Accounts, 3)
-	//bs, err := gd.JSONBytes()
-	//require.NoError(t, err)
-	//fmt.Println(string(bs))
 }
 
 func TestMergeGenesisSpecGlobalPermissions(t *testing.T) {
@@ -33,3 +30,63 @@ func TestMergeGenesisSpecGlobalPermissions(t *testing.T) {
 	assert.Equal(t, []string{permission.CreateAccountString, permission.HasRoleString, permission.SendString},
 		gsMerged.GlobalPermissions)
 }
+
+func TestMergeGenesisSpecsRepeatedAccounts(t *testing.T) {
+	name1 := "Party!"
+	name3 := "Counter!"
+
+	amt1 := uint64(5)
+	amt2 := uint64(2)
+	amt3 := uint64(9)
+
+	gs1 := GenesisSpec{
+		Accounts: []TemplateAccount{
+			{
+				Name:        name1,
+				Amount:      &amt1,
+				Permissions: []string{permission.SendString, permission.CreateAccountString, permission.HasRoleString},
+				Roles:       []string{"fooer"},
+			},
+		},
+	}
+	gs2 := GenesisSpec{
+		Accounts: []TemplateAccount{
+			{
+				Name:        name1,
+				Amount:      &amt2,
+				Permissions: []string{permission.SendString, permission.CreateAccountString},
+				Roles:       []string{"barer"},
+			},
+		},
+	}
+	gs3 := GenesisSpec{
+		Accounts: []TemplateAccount{
+			{
+				Name:   name3,
+				Amount: &amt3,
+			},
+		},
+	}
+
+	gsMerged := MergeGenesisSpecs(gs1, gs2, gs3)
+	bsMerged, err := gsMerged.JSONBytes()
+	require.NoError(t, err)
+
+	amtExpected := amt1 + amt2
+	gsExpected := GenesisSpec{
+		Accounts: []TemplateAccount{
+			{
+				Name:        name1,
+				Amount:      &amtExpected,
+				Permissions: []string{permission.CreateAccountString, permission.HasRoleString, permission.SendString},
+				Roles:       []string{"barer", "fooer"},
+			},
+			gs3.Accounts[0],
+		},
+	}
+	bsExpected, err := gsExpected.JSONBytes()
+	require.NoError(t, err)
+	if !assert.Equal(t, string(bsExpected), string(bsMerged)) {
+		t.Logf("Expected:\n%s\n\nActual:\n%s", string(bsExpected), string(bsMerged))
+	}
+}
diff --git a/keys/mock/key_client_mock.go b/keys/mock/key_client_mock.go
index d2c78231f945cdf3d7db952dc21933f016492298..47bdca9aeb9dba5840d2da98ba6c692db89c9005 100644
--- a/keys/mock/key_client_mock.go
+++ b/keys/mock/key_client_mock.go
@@ -15,20 +15,23 @@
 package mock
 
 import (
+	"bytes"
 	"crypto/rand"
+	"encoding/base64"
 	"fmt"
-
-	"bytes"
 	"text/template"
 
-	"encoding/base64"
+	"encoding/json"
+
+	"reflect"
 
 	acm "github.com/hyperledger/burrow/account"
 	. "github.com/hyperledger/burrow/keys"
 	"github.com/pkg/errors"
 	"github.com/tendermint/ed25519"
-	crypto "github.com/tendermint/go-crypto"
+	"github.com/tendermint/go-crypto"
 	"github.com/tmthrgd/go-hex"
+	"github.com/wayn3h0/go-uuid"
 	"golang.org/x/crypto/ripemd160"
 )
 
@@ -48,16 +51,33 @@ const DefaultDumpKeysFormat = `{
     {
       "Name": "<< $key.Name >>",
       "Address": "<< $key.Address >>",
-      "PublicKey": "<< $key.PublicKeyBase64 >>",
-      "PrivateKey": "<< $key.PrivateKeyBase64 >>"
+      "PublicKey": "<< base64 $key.PublicKey >>",
+      "PrivateKey": "<< base64 $key.PrivateKey >>"
     }<< end >>
   ]
 }`
 
+const KubernetesKeyDumpFormat = `keysFiles:<< range $index, $key := . >>
+  key-<< printf "%03d" $index >>: << base64 $key.MonaxKeyJSON >><< end >>
+keysAddresses:<< range $index, $key := . >>
+  key-<< printf "%03d" $index >>: << $key.Address >><< end >>
+validatorAddresses:<< range $index, $key := . >>
+  - << $key.Address >><< end >>
+`
+
 const LeftTemplateDelim = "<<"
 const RightTemplateDelim = ">>"
 
-var DefaultDumpKeysTemplate = template.Must(template.New("MockKeyClient_DumpKeys").
+var templateFuncs template.FuncMap = map[string]interface{}{
+	"base64": func(rv reflect.Value) string {
+		return encode(rv, base64.StdEncoding.EncodeToString)
+	},
+	"hex": func(rv reflect.Value) string {
+		return encode(rv, hex.EncodeUpperToString)
+	},
+}
+
+var DefaultDumpKeysTemplate = template.Must(template.New("MockKeyClient_DumpKeys").Funcs(templateFuncs).
 	Delims(LeftTemplateDelim, RightTemplateDelim).
 	Parse(DefaultDumpKeysFormat))
 
@@ -110,20 +130,31 @@ func (mockKey *MockKey) Sign(message []byte) (acm.Signature, error) {
 	return acm.SignatureFromBytes(ed25519.Sign(&privateKey, message)[:])
 }
 
-func (mockKey *MockKey) PrivateKeyBase64() string {
-	return base64.StdEncoding.EncodeToString(mockKey.PrivateKey[:])
-}
-
-func (mockKey *MockKey) PrivateKeyHex() string {
-	return hex.EncodeUpperToString(mockKey.PrivateKey[:])
-}
-
-func (mockKey *MockKey) PublicKeyBase64() string {
-	return base64.StdEncoding.EncodeToString(mockKey.PublicKey)
+// TODO: remove after merging keys taken from there to match serialisation
+type plainKeyJSON struct {
+	Id         []byte
+	Type       string
+	Address    string
+	PrivateKey []byte
 }
 
-func (mockKey *MockKey) PublicKeyHex() string {
-	return hex.EncodeUpperToString(mockKey.PublicKey)
+// Returns JSON string compatible with that stored by monax-keys
+func (mockKey *MockKey) MonaxKeyJSON() string {
+	id, err := uuid.NewRandom()
+	if err != nil {
+		return errors.Wrap(err, "could not create monax key json").Error()
+	}
+	jsonKey := plainKeyJSON{
+		Id:         []byte(id.String()),
+		Address:    mockKey.Address.String(),
+		Type:       string(KeyTypeEd25519Ripemd160),
+		PrivateKey: mockKey.PrivateKey,
+	}
+	bs, err := json.Marshal(jsonKey)
+	if err != nil {
+		return errors.Wrap(err, "could not create monax key json").Error()
+	}
+	return string(bs)
 }
 
 //---------------------------------------------------------------------
@@ -183,9 +214,10 @@ func (mkc *MockKeyClient) HealthCheck() error {
 }
 
 func (mkc *MockKeyClient) DumpKeys(templateString string) (string, error) {
-	tmpl, err := template.New("DumpKeys").Delims(LeftTemplateDelim, RightTemplateDelim).Parse(templateString)
+	tmpl, err := template.New("DumpKeys").Delims(LeftTemplateDelim, RightTemplateDelim).Funcs(templateFuncs).
+		Parse(templateString)
 	if err != nil {
-		errors.Wrap(err, "could not dump keys to template")
+		return "", errors.Wrap(err, "could not dump keys to template")
 	}
 	buf := new(bytes.Buffer)
 	keys := make([]*MockKey, 0, len(mkc.knownKeys))
@@ -198,3 +230,14 @@ func (mkc *MockKeyClient) DumpKeys(templateString string) (string, error) {
 	}
 	return buf.String(), nil
 }
+
+func encode(rv reflect.Value, encoder func([]byte) string) string {
+	switch rv.Kind() {
+	case reflect.Slice:
+		return encoder(rv.Bytes())
+	case reflect.String:
+		return encoder([]byte(rv.String()))
+	default:
+		panic(fmt.Errorf("could not convert %#v to bytes to encode", rv))
+	}
+}
diff --git a/keys/mock/key_client_mock_test.go b/keys/mock/key_client_mock_test.go
index ffcec0019c000eecfa116ec1be3e2b275cb6c992..9a5cbef3f520c7c17d16592f51dfb7aae12f7227 100644
--- a/keys/mock/key_client_mock_test.go
+++ b/keys/mock/key_client_mock_test.go
@@ -5,6 +5,8 @@ import (
 
 	"encoding/json"
 
+	"fmt"
+
 	"github.com/hyperledger/burrow/keys"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -27,3 +29,29 @@ func TestMockKeyClient_DumpKeys(t *testing.T) {
 	require.NoError(t, err)
 	assert.Equal(t, string(bs), dump)
 }
+
+func TestMockKeyClient_DumpKeysKubernetes(t *testing.T) {
+	keyClient := NewMockKeyClient()
+	_, err := keyClient.Generate("foo", keys.KeyTypeEd25519Ripemd160)
+	require.NoError(t, err)
+	_, err = keyClient.Generate("foobar", keys.KeyTypeEd25519Ripemd160)
+	require.NoError(t, err)
+	dump, err := keyClient.DumpKeys(KubernetesKeyDumpFormat)
+	require.NoError(t, err)
+	fmt.Println(dump)
+}
+
+func TestMockKey_MonaxKeyJSON(t *testing.T) {
+	key, err := newMockKey("monax-key-test")
+	require.NoError(t, err)
+	monaxKey := key.MonaxKeyJSON()
+	t.Logf("key is: %v", monaxKey)
+	keyJSON := &plainKeyJSON{}
+	err = json.Unmarshal([]byte(monaxKey), keyJSON)
+	require.NoError(t, err)
+	// byte length of UUID string = 16 * 2 + 4 = 36
+	assert.Len(t, keyJSON.Id, 36)
+	assert.Equal(t, key.Address.String(), keyJSON.Address)
+	assert.Equal(t, key.PrivateKey, keyJSON.PrivateKey)
+	assert.Equal(t, string(keys.KeyTypeEd25519Ripemd160), keyJSON.Type)
+}
diff --git a/vendor/github.com/wayn3h0/go-uuid/LICENSE b/vendor/github.com/wayn3h0/go-uuid/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..fad0aa6350da088c679b81e73dee76ef873491da
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2015 Wayne Ho.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/vendor/github.com/wayn3h0/go-uuid/domain.go b/vendor/github.com/wayn3h0/go-uuid/domain.go
new file mode 100644
index 0000000000000000000000000000000000000000..ce09399bcae3b33d13fd12648d20594b9952530a
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/domain.go
@@ -0,0 +1,14 @@
+package uuid
+
+import (
+	"github.com/wayn3h0/go-uuid/internal/dcesecurity"
+)
+
+// Domain represents the identifier for a local domain
+type Domain byte
+
+// Domains.
+const (
+	DomainUser  = Domain(dcesecurity.User)  // POSIX UID domain
+	DomainGroup = Domain(dcesecurity.Group) // POSIX GID domain
+)
diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/dcesecurity/dcesecurity.go b/vendor/github.com/wayn3h0/go-uuid/internal/dcesecurity/dcesecurity.go
new file mode 100644
index 0000000000000000000000000000000000000000..9de07243d9483e479daa7cf6be52b36496d90cf7
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/internal/dcesecurity/dcesecurity.go
@@ -0,0 +1,37 @@
+package dcesecurity
+
+import (
+	"encoding/binary"
+	"errors"
+	"os"
+
+	"github.com/wayn3h0/go-uuid/internal/layout"
+	"github.com/wayn3h0/go-uuid/internal/timebased"
+	"github.com/wayn3h0/go-uuid/internal/version"
+)
+
+// Generate returns a new DCE security uuid.
+func New(domain Domain) ([]byte, error) {
+	uuid, err := timebased.New()
+	if err != nil {
+		return nil, err
+	}
+
+	switch domain {
+	case User:
+		uid := os.Getuid()
+		binary.BigEndian.PutUint32(uuid[0:], uint32(uid)) // network byte order
+	case Group:
+		gid := os.Getgid()
+		binary.BigEndian.PutUint32(uuid[0:], uint32(gid)) // network byte order
+	default:
+		return nil, errors.New("uuid: domain is invalid")
+	}
+
+	// set version(v2)
+	version.Set(uuid, version.DCESecurity)
+	// set layout(RFC4122)
+	layout.Set(uuid, layout.RFC4122)
+
+	return uuid, nil
+}
diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/dcesecurity/domain.go b/vendor/github.com/wayn3h0/go-uuid/internal/dcesecurity/domain.go
new file mode 100644
index 0000000000000000000000000000000000000000..38eae8301fdb4a49fe10e50b74ef64a666919a51
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/internal/dcesecurity/domain.go
@@ -0,0 +1,9 @@
+package dcesecurity
+
+// Domain represents the identifier for a local domain
+type Domain byte
+
+const (
+	User  Domain = iota + 1 // POSIX UID domain
+	Group                   // POSIX GID domain
+)
diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/layout/layout.go b/vendor/github.com/wayn3h0/go-uuid/internal/layout/layout.go
new file mode 100644
index 0000000000000000000000000000000000000000..65a84cc463e4e429bbcea37024c1cffc56859daa
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/internal/layout/layout.go
@@ -0,0 +1,12 @@
+package layout
+
+// Layout represents the layout of UUID. See page 5 in RFC 4122.
+type Layout byte
+
+const (
+	Invalid   Layout = iota // Invalid
+	NCS                     // Reserved, NCS backward compatibility. (Values: 0x00-0x07)
+	RFC4122                 // The variant specified in RFC 4122. (Values: 0x08-0x0b)
+	Microsoft               // Reserved, Microsoft Corporation backward compatibility. (Values: 0x0c-0x0d)
+	Future                  // Reserved for future definition. (Values: 0x0e-0x0f)
+)
diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/layout/utility.go b/vendor/github.com/wayn3h0/go-uuid/internal/layout/utility.go
new file mode 100644
index 0000000000000000000000000000000000000000..7c5fde7beb4acd4386126cbbe93e5cf8040e8b67
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/internal/layout/utility.go
@@ -0,0 +1,33 @@
+package layout
+
+// Set sets the layout for uuid.
+func Set(uuid []byte, layout Layout) {
+	switch layout {
+	case NCS:
+		uuid[8] = (uuid[8] | 0x00) & 0x0f // Msb0=0
+	case RFC4122:
+		uuid[8] = (uuid[8] | 0x80) & 0x8f // Msb0=1, Msb1=0
+	case Microsoft:
+		uuid[8] = (uuid[8] | 0xc0) & 0xcf // Msb0=1, Msb1=1, Msb2=0
+	case Future:
+		uuid[8] = (uuid[8] | 0xe0) & 0xef // Msb0=1, Msb1=1, Msb2=1
+	default:
+		panic("uuid: layout is invalid")
+	}
+}
+
+// Get returns layout of uuid.
+func Get(uuid []byte) Layout {
+	switch {
+	case (uuid[8] & 0x80) == 0x00:
+		return NCS
+	case (uuid[8] & 0xc0) == 0x80:
+		return RFC4122
+	case (uuid[8] & 0xe0) == 0xc0:
+		return Microsoft
+	case (uuid[8] & 0xe0) == 0xe0:
+		return Future
+	}
+
+	return Invalid
+}
diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/namebased/md5/md5.go b/vendor/github.com/wayn3h0/go-uuid/internal/namebased/md5/md5.go
new file mode 100644
index 0000000000000000000000000000000000000000..a03e6c71d68cd9f10cbe6591af938122ed803e71
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/internal/namebased/md5/md5.go
@@ -0,0 +1,33 @@
+package md5
+
+import (
+	"crypto/md5"
+
+	"github.com/wayn3h0/go-uuid/internal/layout"
+	"github.com/wayn3h0/go-uuid/internal/version"
+)
+
+// New returns a new name-based uses SHA-1 hashing uuid.
+func New(namespace, name string) ([]byte, error) {
+	hash := md5.New()
+	_, err := hash.Write([]byte(namespace))
+	if err != nil {
+		return nil, err
+	}
+	_, err = hash.Write([]byte(name))
+	if err != nil {
+		return nil, err
+	}
+
+	sum := hash.Sum(nil)
+
+	uuid := make([]byte, 16)
+	copy(uuid, sum)
+
+	// set version(v3)
+	version.Set(uuid, version.NameBasedMD5)
+	// set layout(RFC4122)
+	layout.Set(uuid, layout.RFC4122)
+
+	return uuid, nil
+}
diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/namebased/namebased.go b/vendor/github.com/wayn3h0/go-uuid/internal/namebased/namebased.go
new file mode 100644
index 0000000000000000000000000000000000000000..f4202676ad56349cf0dee76ef3adae53805f2b66
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/internal/namebased/namebased.go
@@ -0,0 +1,9 @@
+package namebased
+
+// Standard Namespaces
+const (
+	NamespaceDNS  = "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
+	NamespaceURL  = "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
+	NamespaceOID  = "6ba7b812-9dad-11d1-80b4-00c04fd430c8"
+	NamespaceX500 = "6ba7b814-9dad-11d1-80b4-00c04fd430c8"
+)
diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/namebased/sha1/sha1.go b/vendor/github.com/wayn3h0/go-uuid/internal/namebased/sha1/sha1.go
new file mode 100644
index 0000000000000000000000000000000000000000..14a8eeb01c607720d0b2acfb230f3dc431e2d629
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/internal/namebased/sha1/sha1.go
@@ -0,0 +1,33 @@
+package sha1
+
+import (
+	"crypto/sha1"
+
+	"github.com/wayn3h0/go-uuid/internal/layout"
+	"github.com/wayn3h0/go-uuid/internal/version"
+)
+
+// New returns a new name-based uses SHA-1 hashing uuid.
+func New(namespace, name string) ([]byte, error) {
+	hash := sha1.New()
+	_, err := hash.Write([]byte(namespace))
+	if err != nil {
+		return nil, err
+	}
+	_, err = hash.Write([]byte(name))
+	if err != nil {
+		return nil, err
+	}
+
+	sum := hash.Sum(nil)
+
+	uuid := make([]byte, 16)
+	copy(uuid, sum)
+
+	// set version(v5)
+	version.Set(uuid, version.NameBasedSHA1)
+	// set layout(RFC4122)
+	layout.Set(uuid, layout.RFC4122)
+
+	return uuid, nil
+}
diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/random/random.go b/vendor/github.com/wayn3h0/go-uuid/internal/random/random.go
new file mode 100644
index 0000000000000000000000000000000000000000..59354280800f18125241c15b9e941198ff2cc0e8
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/internal/random/random.go
@@ -0,0 +1,27 @@
+package random
+
+import (
+	"crypto/rand"
+
+	"github.com/wayn3h0/go-uuid/internal/layout"
+	"github.com/wayn3h0/go-uuid/internal/version"
+)
+
+// New returns a new randomly uuid.
+func New() ([]byte, error) {
+	uuid := make([]byte, 16)
+	n, err := rand.Read(uuid[:])
+	if err != nil {
+		return nil, err
+	}
+	if n != len(uuid) {
+		return nil, err
+	}
+
+	// set version(v4)
+	version.Set(uuid, version.Random)
+	// set layout(RFC4122)
+	layout.Set(uuid, layout.RFC4122)
+
+	return uuid, nil
+}
diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/timebased/timebased.go b/vendor/github.com/wayn3h0/go-uuid/internal/timebased/timebased.go
new file mode 100644
index 0000000000000000000000000000000000000000..43541567cf1eaf24fdc4df388608f316e51da635
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/internal/timebased/timebased.go
@@ -0,0 +1,92 @@
+package timebased
+
+import (
+	"crypto/rand"
+	"encoding/binary"
+	"net"
+	"sync"
+	"time"
+
+	"github.com/wayn3h0/go-uuid/internal/layout"
+	"github.com/wayn3h0/go-uuid/internal/version"
+)
+
+const (
+	// Intervals bewteen 1/1/1970 and 15/10/1582 (Julain days of 1 Jan 1970 - Julain days of 15 Oct 1582) * 100-Nanoseconds Per Day
+	intervals = (2440587 - 2299160) * 86400 * 10000000
+)
+
+var (
+	lastGenerated time.Time  // last generated time
+	clockSequence uint16     // clock sequence for same tick
+	nodeID        []byte     // node id (MAC Address)
+	locker        sync.Mutex // global lock
+)
+
+// New returns a new time-based uuid.
+func New() ([]byte, error) {
+	// Get and release a global lock
+	locker.Lock()
+	defer locker.Unlock()
+
+	uuid := make([]byte, 16)
+
+	// get timestamp
+	now := time.Now().UTC()
+	timestamp := uint64(now.UnixNano()/100) + intervals // get timestamp
+	if !now.After(lastGenerated) {
+		clockSequence++ // last generated time known, then just increment clock sequence
+	} else {
+		b := make([]byte, 2)
+		_, err := rand.Read(b)
+		if err != nil {
+			return nil, err
+		}
+		clockSequence = uint16(int(b[0])<<8 | int(b[1])) // set to a random value (network byte order)
+	}
+
+	lastGenerated = now // remember the last generated time
+
+	timeLow := uint32(timestamp & 0xffffffff)
+	timeMiddle := uint16((timestamp >> 32) & 0xffff)
+	timeHigh := uint16((timestamp >> 48) & 0xfff)
+
+	// network byte order(BigEndian)
+	binary.BigEndian.PutUint32(uuid[0:], timeLow)
+	binary.BigEndian.PutUint16(uuid[4:], timeMiddle)
+	binary.BigEndian.PutUint16(uuid[6:], timeHigh)
+	binary.BigEndian.PutUint16(uuid[8:], clockSequence)
+
+	// get node id(mac address)
+	if nodeID == nil {
+		interfaces, err := net.Interfaces()
+		if err != nil {
+			return nil, err
+		}
+
+		for _, i := range interfaces {
+			if len(i.HardwareAddr) >= 6 {
+				nodeID = make([]byte, 6)
+				copy(nodeID, i.HardwareAddr)
+				break
+			}
+		}
+
+		if nodeID == nil {
+			nodeID = make([]byte, 6)
+			_, err := rand.Read(nodeID)
+			if err != nil {
+				return nil, err
+			}
+		}
+	}
+
+	copy(uuid[10:], nodeID)
+
+	// set version(v1)
+	version.Set(uuid, version.TimeBased)
+	// set layout(RFC4122)
+	layout.Set(uuid, layout.RFC4122)
+
+	return uuid, nil
+}
diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/version/utility.go b/vendor/github.com/wayn3h0/go-uuid/internal/version/utility.go
new file mode 100644
index 0000000000000000000000000000000000000000..2862711a022e9f06fa763ea6546700e7765f2aed
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/internal/version/utility.go
@@ -0,0 +1,29 @@
+package version
+
+// Set sets the version for uuid.
+func Set(uuid []byte, version Version) {
+	switch version {
+	case TimeBased:
+		uuid[6] = (uuid[6] | 0x10) & 0x1f
+	case DCESecurity:
+		uuid[6] = (uuid[6] | 0x20) & 0x2f
+	case NameBasedMD5:
+		uuid[6] = (uuid[6] | 0x30) & 0x3f
+	case Random:
+		uuid[6] = (uuid[6] | 0x40) & 0x4f
+	case NameBasedSHA1:
+		uuid[6] = (uuid[6] | 0x50) & 0x5f
+	default:
+		panic("uuid: version is unknown")
+	}
+}
+
+// Get gets the version of uuid.
+func Get(uuid []byte) Version {
+	version := uuid[6] >> 4
+	if version > 0 && version < 6 {
+		return Version(version)
+	}
+
+	return Unknown
+}
diff --git a/vendor/github.com/wayn3h0/go-uuid/internal/version/version.go b/vendor/github.com/wayn3h0/go-uuid/internal/version/version.go
new file mode 100644
index 0000000000000000000000000000000000000000..10ab52cfa15cde59be546f2c21dc49e24b79103c
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/internal/version/version.go
@@ -0,0 +1,14 @@
+package version
+
+// Version represents the version of UUID. See page 7 in RFC 4122.
+type Version byte
+
+// Version List
+const (
+	Unknown       Version = iota // Unknwon
+	TimeBased                    // V1: The time-based version
+	DCESecurity                  // V2: The DCE security version, with embedded POSIX UIDs
+	NameBasedMD5                 // V3: The name-based version that uses MD5 hashing
+	Random                       // V4: The randomly or pseudo-randomly generated version
+	NameBasedSHA1                // V5: The name-based version that uses SHA-1 hashing
+)
diff --git a/vendor/github.com/wayn3h0/go-uuid/layout.go b/vendor/github.com/wayn3h0/go-uuid/layout.go
new file mode 100644
index 0000000000000000000000000000000000000000..7656df5d163e6e15a85aeaef1d7ec9605d3885f0
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/layout.go
@@ -0,0 +1,33 @@
+package uuid
+
+import (
+	"github.com/wayn3h0/go-uuid/internal/layout"
+)
+
+// Layout represents the layout of UUID. See page 5 in RFC 4122.
+type Layout byte
+
+// Layouts.
+const (
+	LayoutInvalid   = Layout(layout.Invalid)   // Invalid
+	LayoutNCS       = Layout(layout.NCS)       // Reserved, NCS backward compatibility. (Values: 0x00-0x07)
+	LayoutRFC4122   = Layout(layout.RFC4122)   // The variant specified in RFC 4122. (Values: 0x08-0x0b)
+	LayoutMicrosoft = Layout(layout.Microsoft) // Reserved, Microsoft Corporation backward compatibility. (Values: 0x0c-0x0d)
+	LayoutFuture    = Layout(layout.Future)    // Reserved for future definition. (Values: 0x0e-0x0f)
+)
+
+// String returns English description of layout.
+func (this Layout) String() string {
+	switch this {
+	case LayoutNCS:
+		return "Layout: Reserved For NCS"
+	case LayoutRFC4122:
+		return "Layout: RFC 4122"
+	case LayoutMicrosoft:
+		return "Layout: Reserved For Microsoft"
+	case LayoutFuture:
+		return "Layout: Reserved For Future"
+	default:
+		return "Layout: Invalid"
+	}
+}
diff --git a/vendor/github.com/wayn3h0/go-uuid/namespace.go b/vendor/github.com/wayn3h0/go-uuid/namespace.go
new file mode 100644
index 0000000000000000000000000000000000000000..23b314a56f84c63c6c9788a226d261d5b5545316
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/namespace.go
@@ -0,0 +1,13 @@
+package uuid
+
+import (
+	"github.com/wayn3h0/go-uuid/internal/namebased"
+)
+
+// Imports namespaces.
+const (
+	NamespaceDNS  = namebased.NamespaceDNS  // "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
+	NamespaceURL  = namebased.NamespaceURL  // "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
+	NamespaceOID  = namebased.NamespaceOID  // "6ba7b812-9dad-11d1-80b4-00c04fd430c8"
+	NamespaceX500 = namebased.NamespaceX500 // "6ba7b814-9dad-11d1-80b4-00c04fd430c8"
+)
diff --git a/vendor/github.com/wayn3h0/go-uuid/parse.go b/vendor/github.com/wayn3h0/go-uuid/parse.go
new file mode 100644
index 0000000000000000000000000000000000000000..0847299c3cede87a69dea74f44860003d34b2722
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/parse.go
@@ -0,0 +1,47 @@
+package uuid
+
+import (
+	"encoding/hex"
+	"errors"
+	"fmt"
+)
+
+// Parse parses the UUID string.
+func Parse(str string) (UUID, error) {
+	length := len(str)
+	buffer := make([]byte, 16)
+	charIndexes := []int{}
+	switch length {
+	case 36:
+		if str[8] != '-' || str[13] != '-' || str[18] != '-' || str[23] != '-' {
+			return Nil, fmt.Errorf("uuid: format of UUID string \"%s\" is invalid, it should be xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)", str)
+		}
+		charIndexes = []int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34}
+	case 32:
+		charIndexes = []int{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30}
+	default:
+		return Nil, fmt.Errorf("uuid: length of UUID string \"%s\" is invalid, it should be 36 (standard) or 32 (without dash)", str)
+	}
+	for i, v := range charIndexes {
+		if c, e := hex.DecodeString(str[v : v+2]); e != nil {
+			return Nil, fmt.Errorf("uuid: UUID string \"%s\" is invalid: %s", str, e.Error())
+		} else {
+			buffer[i] = c[0]
+		}
+	}
+
+	uuid := UUID{}
+	copy(uuid[:], buffer)
+
+	if !uuid.Equal(Nil) {
+		if uuid.Layout() == LayoutInvalid {
+			return Nil, errors.New("uuid: layout is invalid")
+		}
+
+		if uuid.Version() == VersionUnknown {
+			return Nil, errors.New("uuid: version is unknown")
+		}
+	}
+
+	return uuid, nil
+}
diff --git a/vendor/github.com/wayn3h0/go-uuid/style.go b/vendor/github.com/wayn3h0/go-uuid/style.go
new file mode 100644
index 0000000000000000000000000000000000000000..17b816a75c1d1b9ce8c7142b15a7fbbc527d6597
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/style.go
@@ -0,0 +1,22 @@
+package uuid
+
+// Style represents the style of UUID string.
+type Style byte
+
+// Styles.
+const (
+	StyleStandard    Style = iota + 1 // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12, length: 36)
+	StyleWithoutDash                  // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (length: 32)
+)
+
+// String returns English description of style.
+func (this Style) String() string {
+	switch this {
+	case StyleStandard:
+		return "Style: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)"
+	case StyleWithoutDash:
+		return "Style: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+	default:
+		return "Style: Unknown"
+	}
+}
diff --git a/vendor/github.com/wayn3h0/go-uuid/uuid.go b/vendor/github.com/wayn3h0/go-uuid/uuid.go
new file mode 100644
index 0000000000000000000000000000000000000000..ea17ec284621868df10e5b288a28ccb1b7a8476b
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/uuid.go
@@ -0,0 +1,147 @@
+package uuid
+
+import (
+	"bytes"
+	"fmt"
+
+	"github.com/wayn3h0/go-uuid/internal/dcesecurity"
+	"github.com/wayn3h0/go-uuid/internal/layout"
+	"github.com/wayn3h0/go-uuid/internal/namebased/md5"
+	"github.com/wayn3h0/go-uuid/internal/namebased/sha1"
+	"github.com/wayn3h0/go-uuid/internal/random"
+	"github.com/wayn3h0/go-uuid/internal/timebased"
+	"github.com/wayn3h0/go-uuid/internal/version"
+)
+
+var (
+	Nil = UUID{} // Nil UUID
+)
+
+// NewTimeBased returns a new time based UUID (version 1).
+func NewTimeBased() (UUID, error) {
+	u, err := timebased.New()
+	if err != nil {
+		return Nil, err
+	}
+
+	uuid := UUID{}
+	copy(uuid[:], u)
+
+	return uuid, nil
+}
+
+// NewV1 same as NewTimeBased.
+func NewV1() (UUID, error) {
+	return NewTimeBased()
+}
+
+// NewDCESecurity returns a new DCE security UUID (version 2).
+func NewDCESecurity(domain Domain) (UUID, error) {
+	u, err := dcesecurity.New(dcesecurity.Domain(domain))
+	if err != nil {
+		return Nil, err
+	}
+
+	uuid := UUID{}
+	copy(uuid[:], u)
+
+	return uuid, nil
+}
+
+// NewV2 same as NewDCESecurity.
+func NewV2(domain Domain) (UUID, error) {
+	return NewDCESecurity(domain)
+}
+
+// NewNameBasedMD5 returns a new name based UUID with MD5 hash (version 3).
+func NewNameBasedMD5(namespace, name string) (UUID, error) {
+	u, err := md5.New(namespace, name)
+	if err != nil {
+		return Nil, err
+	}
+
+	uuid := UUID{}
+	copy(uuid[:], u)
+
+	return uuid, nil
+}
+
+// NewV3 same as NewNameBasedMD5.
+func NewV3(namespace, name string) (UUID, error) {
+	return NewNameBasedMD5(namespace, name)
+}
+
+// NewRandom returns a new random UUID (version 4).
+func NewRandom() (UUID, error) {
+	u, err := random.New()
+	if err != nil {
+		return Nil, err
+	}
+
+	uuid := UUID{}
+	copy(uuid[:], u)
+
+	return uuid, nil
+}
+
+// NewV4 same as NewRandom.
+func NewV4() (UUID, error) {
+	return NewRandom()
+}
+
+// New same as NewRandom.
+func New() (UUID, error) {
+	return NewRandom()
+}
+
+// NewNameBasedSHA1 returns a new name based UUID with SHA1 hash (version 5).
+func NewNameBasedSHA1(namespace, name string) (UUID, error) {
+	u, err := sha1.New(namespace, name)
+	if err != nil {
+		return Nil, err
+	}
+
+	uuid := UUID{}
+	copy(uuid[:], u)
+
+	return uuid, nil
+}
+
+// NewV5 same as NewNameBasedSHA1.
+func NewV5(namespace, name string) (UUID, error) {
+	return NewNameBasedSHA1(namespace, name)
+}
+
+// UUID respresents an UUID type compliant with specification in RFC 4122.
+type UUID [16]byte
+
+// Layout returns layout of UUID.
+func (this UUID) Layout() Layout {
+	return Layout(layout.Get(this[:]))
+}
+
+// Version returns version of UUID.
+func (this UUID) Version() Version {
+	return Version(version.Get(this[:]))
+}
+
+// Equal returns true if current uuid equal to passed uuid.
+func (this UUID) Equal(another UUID) bool {
+	return bytes.EqualFold(this[:], another[:])
+}
+
+// Format returns the formatted string of UUID.
+func (this UUID) Format(style Style) string {
+	switch style {
+	case StyleWithoutDash:
+		return fmt.Sprintf("%x", this[:])
+	//case StyleStandard:
+	default:
+		return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x", this[:4], this[4:6], this[6:8], this[8:10], this[10:])
+	}
+}
+
+// String returns the string of UUID with standard style(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | 8-4-4-4-12).
+func (this UUID) String() string {
+	return this.Format(StyleStandard)
+}
diff --git a/vendor/github.com/wayn3h0/go-uuid/version.go b/vendor/github.com/wayn3h0/go-uuid/version.go
new file mode 100644
index 0000000000000000000000000000000000000000..af509e3a3bb170dc45576c415d38f3c6f0444ffe
--- /dev/null
+++ b/vendor/github.com/wayn3h0/go-uuid/version.go
@@ -0,0 +1,45 @@
+package uuid
+
+import (
+	"github.com/wayn3h0/go-uuid/internal/version"
+)
+
+// Version represents the version of UUID. See page 7 in RFC 4122.
+type Version byte
+
+// Versions.
+const (
+	VersionUnknown       = Version(version.Unknown)       // Unknown
+	VersionTimeBased     = Version(version.TimeBased)     // V1: The time-based version
+	VersionDCESecurity   = Version(version.DCESecurity)   // V2: The DCE security version, with embedded POSIX UIDs
+	VersionNameBasedMD5  = Version(version.NameBasedMD5)  // V3: The name-based version that uses MD5 hashing
+	VersionRandom        = Version(version.Random)        // V4: The randomly or pseudo-randomly generated version
+	VersionNameBasedSHA1 = Version(version.NameBasedSHA1) // V5: The name-based version that uses SHA-1 hashing
+)
+
+// Short names.
+const (
+	V1 = VersionTimeBased
+	V2 = VersionDCESecurity
+	V3 = VersionNameBasedMD5
+	V4 = VersionRandom
+	V5 = VersionNameBasedSHA1
+)
+
+// String returns English description of version.
+func (this Version) String() string {
+	switch this {
+	case VersionTimeBased:
+		return "Version 1: Time-Based"
+	case VersionDCESecurity:
+		return "Version 2: DCE Security With Embedded POSIX UIDs"
+	case VersionNameBasedMD5:
+		return "Version 3: Name-Based (MD5)"
+	case VersionRandom:
+		return "Version 4: Randomly OR Pseudo-Randomly Generated"
+	case VersionNameBasedSHA1:
+		return "Version 5: Name-Based (SHA-1)"
+	default:
+		return "Version: Unknown"
+	}
+}