diff --git a/genesis/deterministic_genesis.go b/genesis/deterministic_genesis.go
new file mode 100644
index 0000000000000000000000000000000000000000..7eebd858da93ea33b22e816b6bf2f5ae63337a6c
--- /dev/null
+++ b/genesis/deterministic_genesis.go
@@ -0,0 +1,98 @@
+package genesis
+
+import (
+	"math/rand"
+	"time"
+
+	"fmt"
+
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/permission"
+	"github.com/tendermint/ed25519"
+	"github.com/tendermint/go-crypto"
+)
+
+type deterministicGenesis struct {
+	random *rand.Rand
+}
+
+// Generates deterministic pseudo-random genesis state
+func NewDeterministicGenesis(seed int64) *deterministicGenesis {
+	return &deterministicGenesis{
+		random: rand.New(rand.NewSource(seed)),
+	}
+}
+
+func (dg *deterministicGenesis) GenesisDoc(numAccounts int, randBalance bool, minBalance uint64, numValidators int,
+	randBonded bool, minBonded int64) (*GenesisDoc, []acm.PrivateAccount) {
+
+	accounts := make([]Account, numAccounts)
+	privAccounts := make([]acm.PrivateAccount, numAccounts)
+	defaultPerms := permission.DefaultAccountPermissions
+	for i := 0; i < numAccounts; i++ {
+		account, privAccount := dg.Account(randBalance, minBalance)
+		accounts[i] = Account{
+			BasicAccount: BasicAccount{
+				Address: account.Address(),
+				Amount:  account.Balance(),
+			},
+			Permissions: defaultPerms.Clone(), // This will get copied into each state.Account.
+		}
+		privAccounts[i] = privAccount
+	}
+	validators := make([]Validator, numValidators)
+	for i := 0; i < numValidators; i++ {
+		validator := acm.GeneratePrivateAccountFromSecret(fmt.Sprintf("val_%v", i))
+		validators[i] = Validator{
+			BasicAccount: BasicAccount{
+				Address:   validator.Address(),
+				PublicKey: validator.PublicKey(),
+				Amount:    uint64(dg.random.Int63()),
+			},
+			UnbondTo: []BasicAccount{
+				{
+					Address: validator.Address(),
+					Amount:  uint64(dg.random.Int63()),
+				},
+			},
+		}
+	}
+	return &GenesisDoc{
+		ChainName:   "TestChain",
+		GenesisTime: time.Unix(1506172037, 0),
+		Accounts:    accounts,
+		Validators:  validators,
+	}, privAccounts
+
+}
+
+func (dg *deterministicGenesis) Account(randBalance bool, minBalance uint64) (acm.Account, acm.PrivateAccount) {
+	privKey := dg.PrivateKey()
+	pubKey := acm.PublicKeyFromPubKey(privKey.PubKey())
+	privAccount := &acm.ConcretePrivateAccount{
+		PublicKey:  pubKey,
+		PrivateKey: privKey,
+		Address:    pubKey.Address(),
+	}
+	perms := permission.DefaultAccountPermissions
+	acc := &acm.ConcreteAccount{
+		Address:     privAccount.Address,
+		PublicKey:   privAccount.PublicKey,
+		Sequence:    uint64(dg.random.Int()),
+		Balance:     minBalance,
+		Permissions: perms,
+	}
+	if randBalance {
+		acc.Balance += uint64(dg.random.Int())
+	}
+	return acc.Account(), privAccount.PrivateAccount()
+}
+
+func (dg *deterministicGenesis) PrivateKey() acm.PrivateKey {
+	privKeyBytes := new([64]byte)
+	for i := 0; i < 32; i++ {
+		privKeyBytes[i] = byte(dg.random.Int() % 256)
+	}
+	ed25519.MakePublicKey(privKeyBytes)
+	return acm.PrivateKeyFromPrivKey(crypto.PrivKeyEd25519(*privKeyBytes).Wrap())
+}
diff --git a/genesis/gen_test.go b/genesis/gen_test.go
deleted file mode 100644
index 3a55eea7df1cbbba038cfb07be248dc383f5c889..0000000000000000000000000000000000000000
--- a/genesis/gen_test.go
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 2017 Monax Industries Limited
-//
-// 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.
-
-package genesis
-
-import (
-	"bytes"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"testing"
-	"time"
-)
-
-// 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
-	// NOTE: [ben] set time to zero time, "genesis_time": "0001-01-01T00:00:00.000Z"
-	genesisFileWritten, err := generateKnownWithTime(chainID, accountsCSVpath, validatorsCSVpath, time.Time{})
-	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/genesis.go b/genesis/genesis.go
index 4eb99cbbc64c498b45b816386c0295601c905001..7d62594e5f60e35b7ec3055d41f9629874ec6075 100644
--- a/genesis/genesis.go
+++ b/genesis/genesis.go
@@ -15,70 +15,214 @@
 package genesis
 
 import (
-	"bytes"
+	"crypto/sha256"
 	"encoding/json"
+	"fmt"
+	"sort"
 	"time"
 
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/permission"
 	ptypes "github.com/hyperledger/burrow/permission/types"
-	wire "github.com/tendermint/go-wire"
 )
 
-// MakeGenesisDocFromAccounts takes a chainName and a slice of pointers to GenesisAccount,
-// and a slice of pointers to GenesisValidator to construct a GenesisDoc, or returns an error on
-// failure.  In particular MakeGenesisDocFromAccount uses the local time as a
-// timestamp for the GenesisDoc.
-func MakeGenesisDocFromAccounts(chainName string, accounts []*GenesisAccount,
-	validators []*GenesisValidator) (GenesisDoc, error) {
-
-	// TODO: assert valid accounts and validators
-	// TODO: [ben] expose setting global permissions
-	globalPermissions := ptypes.DefaultAccountPermissions.Clone()
-	genesisParameters := &GenesisParams{
-		GlobalPermissions: &globalPermissions,
+// How many bytes to take from the front of the GenesisDoc hash to append
+// to the ChainName to form the ChainID. The idea is to avoid some classes
+// of replay attack between chains with the same name.
+const ShortHashSuffixBytes = 3
+
+// we store the GenesisDoc in the db under this key
+
+var GenDocKey = []byte("GenDocKey")
+
+//------------------------------------------------------------
+// core types for a genesis definition
+
+type BasicAccount struct {
+	// Address  is convenient to have in file for reference, but otherwise ignored since derived from PublicKey
+	Address   acm.Address
+	PublicKey acm.PublicKey
+	Amount    uint64
+}
+
+type Account struct {
+	BasicAccount
+	Name        string
+	Permissions ptypes.AccountPermissions
+}
+
+type Validator struct {
+	BasicAccount
+	Name     string
+	UnbondTo []BasicAccount
+}
+
+//------------------------------------------------------------
+// GenesisDoc is stored in the state database
+
+type GenesisDoc struct {
+	GenesisTime       time.Time
+	ChainName         string
+	Salt              []byte `json:",omitempty"`
+	GlobalPermissions ptypes.AccountPermissions
+	Accounts          []Account
+	Validators        []Validator
+}
+
+// JSONBytes returns the JSON (not-yet) canonical bytes for a given
+// GenesisDoc or an error.
+func (genesisDoc *GenesisDoc) JSONBytes() ([]byte, error) {
+	// TODO: write JSON in canonical order
+	return json.MarshalIndent(genesisDoc, "", "\t")
+}
+
+func (genesisDoc *GenesisDoc) Hash() []byte {
+	genesisDocBytes, err := genesisDoc.JSONBytes()
+	if err != nil {
+		panic(fmt.Errorf("could not create hash of GenesisDoc: %v", err))
 	}
-	// copy slice of pointers to accounts into slice of accounts
-	accountsCopy := make([]GenesisAccount, len(accounts))
-	for i, genesisAccount := range accounts {
-		accountsCopy[i] = genesisAccount.Clone()
+	hasher := sha256.New()
+	hasher.Write(genesisDocBytes)
+	return hasher.Sum(nil)
+}
+
+func (genesisDoc *GenesisDoc) ShortHash() []byte {
+	return genesisDoc.Hash()[:ShortHashSuffixBytes]
+}
+
+func (genesisDoc *GenesisDoc) ChainID() string {
+	return fmt.Sprintf("%s-%X", genesisDoc.ChainName, genesisDoc.ShortHash())
+}
+
+//------------------------------------------------------------
+// Make genesis state from file
+
+func GenesisDocFromJSON(jsonBlob []byte) (*GenesisDoc, error) {
+	genDoc := new(GenesisDoc)
+	err := json.Unmarshal(jsonBlob, genDoc)
+	if err != nil {
+		return nil, fmt.Errorf("couldn't read GenesisDoc: %v", err)
 	}
-	// copy slice of pointers to validators into slice of validators
-	validatorsCopy := make([]GenesisValidator, len(validators))
-	for i, genesisValidator := range validators {
-		genesisValidatorCopy, err := genesisValidator.Clone()
-		if err != nil {
-			return GenesisDoc{}, err
-		}
-		validatorsCopy[i] = genesisValidatorCopy
+	return genDoc, nil
+}
+
+//------------------------------------------------------------
+// Account methods
+
+func GenesisAccountFromAccount(name string, account acm.Account) Account {
+	return Account{
+		Name:        name,
+		Permissions: account.Permissions(),
+		BasicAccount: BasicAccount{
+			Address: account.Address(),
+			Amount:  account.Balance(),
+		},
 	}
-	genesisDoc := GenesisDoc{
-		GenesisTime: time.Now(),
-		// TODO: this needs to be corrected for ChainName, and ChainId
-		// is the derived hash from the GenesisDoc serialised bytes
-		ChainID:    chainName,
-		Params:     genesisParameters,
-		Accounts:   accountsCopy,
-		Validators: validatorsCopy,
+}
+
+// Clone clones the genesis account
+func (genesisAccount *Account) Clone() Account {
+	// clone the account permissions
+	return Account{
+		BasicAccount: BasicAccount{
+			Address: genesisAccount.Address,
+			Amount:  genesisAccount.Amount,
+		},
+		Name:        genesisAccount.Name,
+		Permissions: genesisAccount.Permissions.Clone(),
 	}
-	return genesisDoc, nil
 }
 
-// GetGenesisFileBytes returns the JSON (not-yet) canonical bytes for a given
-// GenesisDoc or an error.  In a first rewrite, rely on go-wire
-// for the JSON serialisation with type-bytes.
-func GetGenesisFileBytes(genesisDoc *GenesisDoc) ([]byte, error) {
+//------------------------------------------------------------
+// Validator methods
 
-	// TODO: write JSON in canonical order
-	var err error
-	buffer, n := new(bytes.Buffer), new(int)
-	// write JSON with go-wire type-bytes (for public keys)
-	wire.WriteJSON(genesisDoc, buffer, n, &err)
-	if err != nil {
-		return nil, err
+func (gv *Validator) Validator() acm.Validator {
+	return acm.ConcreteValidator{
+		Address:   gv.PublicKey.Address(),
+		PublicKey: gv.PublicKey,
+		Power:     uint64(gv.Amount),
+	}.Validator()
+}
+
+// Clone clones the genesis validator
+func (gv *Validator) Clone() Validator {
+	// clone the addresses to unbond to
+	unbondToClone := make([]BasicAccount, len(gv.UnbondTo))
+	for i, basicAccount := range gv.UnbondTo {
+		unbondToClone[i] = basicAccount.Clone()
+	}
+	return Validator{
+		BasicAccount: BasicAccount{
+			PublicKey: gv.PublicKey,
+			Amount:    gv.Amount,
+		},
+		Name:     gv.Name,
+		UnbondTo: unbondToClone,
+	}
+}
+
+//------------------------------------------------------------
+// BasicAccount methods
+
+// Clone clones the basic account
+func (basicAccount *BasicAccount) Clone() BasicAccount {
+	return BasicAccount{
+		Address: basicAccount.Address,
+		Amount:  basicAccount.Amount,
+	}
+}
+
+// MakeGenesisDocFromAccounts takes a chainName and a slice of pointers to Account,
+// and a slice of pointers to Validator to construct a GenesisDoc, or returns an error on
+// failure.  In particular MakeGenesisDocFromAccount uses the local time as a
+// timestamp for the GenesisDoc.
+func MakeGenesisDocFromAccounts(chainName string, salt []byte, genesisTime time.Time, accounts map[string]acm.Account,
+	validators map[string]acm.Validator) *GenesisDoc {
+
+	// Establish deterministic order of accounts by name so we obtain identical GenesisDoc
+	// from identical input
+	names := make([]string, 0, len(accounts))
+	for name := range accounts {
+		names = append(names, name)
+	}
+	sort.Strings(names)
+	// copy slice of pointers to accounts into slice of accounts
+	genesisAccounts := make([]Account, 0, len(accounts))
+	for _, name := range names {
+		genesisAccounts = append(genesisAccounts, GenesisAccountFromAccount(name, accounts[name]))
+	}
+	// Sigh...
+	names = names[:0]
+	for name := range validators {
+		names = append(names, name)
+	}
+	sort.Strings(names)
+	// copy slice of pointers to validators into slice of validators
+	genesisValidators := make([]Validator, 0, len(validators))
+	for _, name := range names {
+		val := validators[name]
+		genesisValidators = append(genesisValidators, Validator{
+			Name: name,
+			BasicAccount: BasicAccount{
+				Address:   val.Address(),
+				PublicKey: val.PublicKey(),
+				Amount:    val.Power(),
+			},
+			// Simpler to just do this by convention
+			UnbondTo: []BasicAccount{
+				{
+					Amount:  val.Power(),
+					Address: val.Address(),
+				},
+			},
+		})
 	}
-	// rewrite buffer with indentation
-	indentedBuffer := new(bytes.Buffer)
-	if err := json.Indent(indentedBuffer, buffer.Bytes(), "", "\t"); err != nil {
-		return nil, err
+	return &GenesisDoc{
+		ChainName:         chainName,
+		Salt:              salt,
+		GenesisTime:       genesisTime,
+		GlobalPermissions: permission.DefaultAccountPermissions.Clone(),
+		Accounts:          genesisAccounts,
+		Validators:        genesisValidators,
 	}
-	return indentedBuffer.Bytes(), nil
 }
diff --git a/genesis/genesis_test.go b/genesis/genesis_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..0c5021bb0a0b07f3ad6d10a96e003d807cbbf305
--- /dev/null
+++ b/genesis/genesis_test.go
@@ -0,0 +1,59 @@
+package genesis
+
+import (
+	"fmt"
+	"testing"
+	"time"
+
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/permission"
+	"github.com/stretchr/testify/assert"
+)
+
+var genesisTime, _ = time.Parse("02-01-2006", "27-10-2017")
+
+func TestMakeGenesisDocFromAccounts(t *testing.T) {
+	genDoc := MakeGenesisDocFromAccounts("test-chain", nil, genesisTime,
+		accountMap("Tinkie-winkie", "Lala", "Po", "Dipsy"),
+		validatorMap("Foo", "Bar", "Baz"),
+	)
+
+	// Check we have matching serialisation after a round trip
+	bs, err := genDoc.JSONBytes()
+	assert.NoError(t, err)
+
+	genDocOut, err := GenesisDocFromJSON(bs)
+	assert.NoError(t, err)
+
+	bsOut, err := genDocOut.JSONBytes()
+	assert.NoError(t, err)
+
+	assert.Equal(t, bs, bsOut)
+	assert.Equal(t, genDoc.Hash(), genDocOut.Hash())
+	fmt.Println(string(bs))
+}
+
+func accountMap(names ...string) map[string]acm.Account {
+	accounts := make(map[string]acm.Account, len(names))
+	for _, name := range names {
+		accounts[name] = accountFromName(name)
+	}
+	return accounts
+}
+
+func validatorMap(names ...string) map[string]acm.Validator {
+	validators := make(map[string]acm.Validator, len(names))
+	for _, name := range names {
+		validators[name] = acm.AsValidator(accountFromName(name))
+	}
+	return validators
+}
+
+func accountFromName(name string) acm.Account {
+	ca := acm.NewConcreteAccountFromSecret(name)
+	for _, c := range name {
+		ca.Balance += uint64(c)
+	}
+	ca.Permissions = permission.AllAccountPermissions.Clone()
+	return ca.Account()
+}
diff --git a/genesis/make_genesis_file.go b/genesis/make_genesis_file.go
deleted file mode 100644
index 624b73d23f351ad1649aecd44287e792d4ebe054..0000000000000000000000000000000000000000
--- a/genesis/make_genesis_file.go
+++ /dev/null
@@ -1,249 +0,0 @@
-// Copyright 2017 Monax Industries Limited
-//
-// 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.
-
-package genesis
-
-import (
-	"bytes"
-	"encoding/csv"
-	"encoding/hex"
-	"encoding/json"
-	"fmt"
-	"os"
-	"strconv"
-	"time"
-
-	ptypes "github.com/hyperledger/burrow/permission/types"
-	"github.com/hyperledger/burrow/util"
-
-	"github.com/tendermint/go-crypto"
-	wire "github.com/tendermint/go-wire"
-)
-
-//------------------------------------------------------------------------------------
-// interface functions that are consumed by monax tooling
-// TODO: [ben] these interfaces will be deprecated from v0.17
-
-func GenerateKnown(chainID, accountsPathCSV, validatorsPathCSV string) (string, error) {
-	return generateKnownWithTime(chainID, accountsPathCSV, validatorsPathCSV,
-		// set the timestamp for the genesis
-		time.Now())
-}
-
-//------------------------------------------------------------------------------------
-// interface functions that are consumed by monax tooling
-
-func GenerateGenesisFileBytes(chainName string, genesisAccounts []*GenesisAccount,
-	genesisValidators []*GenesisValidator) ([]byte, error) {
-	genesisDoc, err := MakeGenesisDocFromAccounts(chainName, genesisAccounts, genesisValidators)
-
-	buf, buf2, n := new(bytes.Buffer), new(bytes.Buffer), new(int)
-	wire.WriteJSON(genesisDoc, buf, n, &err)
-	if err != nil {
-		return nil, err
-	}
-	if err := json.Indent(buf2, buf.Bytes(), "", "\t"); err != nil {
-		return nil, err
-	}
-
-	return buf2.Bytes(), nil
-}
-
-//------------------------------------------------------------------------------------
-// core functions that provide functionality for monax tooling in v0.16
-
-// GenerateKnownWithTime takes chainId, an accounts and validators CSV filepath
-// and a timestamp to generate the string of `genesis.json`
-// NOTE: [ben] is introduced as technical debt to preserve the signature
-// of GenerateKnown but in order to introduce the timestamp gradually
-// This will be deprecated in v0.17
-func generateKnownWithTime(chainID, accountsPathCSV, validatorsPathCSV string,
-	genesisTime time.Time) (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, genesisTime, 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, genesisTime time.Time, nVal, nAcc int) *GenesisDoc {
-	genDoc := GenesisDoc{
-		ChainID:     chainID,
-		GenesisTime: genesisTime,
-	}
-	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/maker.go b/genesis/maker.go
index 48f09c563e0cd49ad786be7e235091d82946e436..7b722b46062e93ce4be13987c14d939d4df284a2 100644
--- a/genesis/maker.go
+++ b/genesis/maker.go
@@ -15,11 +15,21 @@
 package genesis
 
 import (
+	"bytes"
+	"encoding/csv"
+	"encoding/hex"
+	"encoding/json"
 	"fmt"
+	"os"
+	"strconv"
+	"time"
 
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/permission"
 	ptypes "github.com/hyperledger/burrow/permission/types"
-
+	"github.com/hyperledger/burrow/util"
 	"github.com/tendermint/go-crypto"
+	wire "github.com/tendermint/go-wire"
 )
 
 const (
@@ -27,19 +37,21 @@ const (
 	PublicKeySecp256k1ByteLength int = 64
 )
 
-// NewGenesisAccount returns a new GenesisAccount
-func NewGenesisAccount(address []byte, amount int64, name string,
-	permissions *ptypes.AccountPermissions) *GenesisAccount {
-	return &GenesisAccount{
-		Address:     address,
-		Amount:      amount,
+// NewGenesisAccount returns a new Account
+func NewGenesisAccount(address acm.Address, amount uint64, name string,
+	permissions *ptypes.AccountPermissions) *Account {
+	return &Account{
+		BasicAccount: BasicAccount{
+			Address: address,
+			Amount:  amount,
+		},
 		Name:        name,
-		Permissions: permissions,
+		Permissions: permissions.Clone(),
 	}
 }
 
-func NewGenesisValidator(amount int64, name string, unbondToAddress []byte,
-	unbondAmount int64, keyType string, publicKeyBytes []byte) (*GenesisValidator, error) {
+func NewGenesisValidator(amount uint64, name string, unbondToAddress acm.Address,
+	unbondAmount uint64, keyType string, publicKeyBytes []byte) (*Validator, error) {
 	// convert the key bytes into a typed fixed size byte array
 	var typedPublicKeyBytes []byte
 	switch keyType {
@@ -61,20 +73,252 @@ func NewGenesisValidator(amount int64, name string, unbondToAddress []byte,
 	default:
 		return nil, fmt.Errorf("Unsupported key type (%s)", keyType)
 	}
-	newPublicKey, err := crypto.PubKeyFromBytes(typedPublicKeyBytes)
+	newPubKey, err := crypto.PubKeyFromBytes(typedPublicKeyBytes)
 	if err != nil {
 		return nil, err
 	}
 	// ability to unbond to multiple accounts currently unused
 	var unbondTo []BasicAccount
 
-	return &GenesisValidator{
-		PubKey: newPublicKey,
-		Amount: unbondAmount,
-		Name:   name,
+	address, err := acm.AddressFromBytes(newPubKey.Address())
+	if err != nil {
+		return nil, err
+	}
+	return &Validator{
+		BasicAccount: BasicAccount{
+			Address:   address,
+			PublicKey: acm.PublicKeyFromPubKey(newPubKey),
+			Amount:    amount,
+		},
+		Name: name,
 		UnbondTo: append(unbondTo, BasicAccount{
 			Address: unbondToAddress,
 			Amount:  unbondAmount,
 		}),
 	}, nil
 }
+
+//------------------------------------------------------------------------------------
+// interface functions that are consumed by monax tooling
+
+func GenerateKnown(chainName, accountsPathCSV, validatorsPathCSV string) (string, error) {
+	return generateKnownWithTime(chainName, accountsPathCSV, validatorsPathCSV,
+		// set the timestamp for the genesis
+		time.Now())
+}
+
+//------------------------------------------------------------------------------------
+// interface functions that are consumed by monax tooling
+
+func GenerateGenesisFileBytes(chainName string, salt []byte, genesisTime time.Time, genesisAccounts map[string]acm.Account,
+	genesisValidators map[string]acm.Validator) ([]byte, error) {
+
+	genesisDoc := MakeGenesisDocFromAccounts(chainName, salt, genesisTime, genesisAccounts, genesisValidators)
+
+	var err error
+	buf, buf2, n := new(bytes.Buffer), new(bytes.Buffer), new(int)
+	wire.WriteJSON(genesisDoc, buf, n, &err)
+	if err != nil {
+		return nil, err
+	}
+	if err := json.Indent(buf2, buf.Bytes(), "", "\t"); err != nil {
+		return nil, err
+	}
+
+	return buf2.Bytes(), nil
+}
+
+//------------------------------------------------------------------------------------
+// core functions that provide functionality for monax tooling in v0.16
+
+// GenerateKnownWithTime takes chainName, an accounts and validators CSV filepath
+// and a timestamp to generate the string of `genesis.json`
+// NOTE: [ben] is introduced as technical debt to preserve the signature
+// of GenerateKnown but in order to introduce the timestamp gradually
+// This will be deprecated in v0.17
+func generateKnownWithTime(chainName, accountsPathCSV, validatorsPathCSV string,
+	genesisTime time.Time) (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(chainName, genesisTime, 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(chainName string, genesisTime time.Time, nVal, nAcc int) *GenesisDoc {
+	genDoc := GenesisDoc{
+		ChainName:   chainName,
+		GenesisTime: genesisTime,
+	}
+	genDoc.Accounts = make([]Account, nAcc)
+	genDoc.Validators = make([]Validator, nVal)
+	return &genDoc
+}
+
+func genDocAddAccount(genDoc *GenesisDoc, pubKey crypto.PubKeyEd25519, amt uint64, name string,
+	perm, setbit ptypes.PermFlag, index int) {
+	addr, _ := acm.AddressFromBytes(pubKey.Address())
+	acc := Account{
+		BasicAccount: BasicAccount{
+			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 uint64, name string,
+	perm, setbit ptypes.PermFlag, index int) {
+	addr, _ := acm.AddressFromBytes(pubKey.Address())
+	genDoc.Validators[index] = Validator{
+		BasicAccount: BasicAccount{
+			Address:   acm.MustAddressFromBytes(pubKey.Address()),
+			PublicKey: acm.PublicKeyFromPubKey(pubKey.Wrap()),
+			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 []uint64, 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", permission.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([]uint64, len(amtS))
+	for i, a := range amtS {
+		if amts[i], err = strconv.ParseUint(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/spec/genesis_spec.go b/genesis/spec/genesis_spec.go
new file mode 100644
index 0000000000000000000000000000000000000000..be653182b652a63522e27cb158ed3f7462a48265
--- /dev/null
+++ b/genesis/spec/genesis_spec.go
@@ -0,0 +1,233 @@
+package spec
+
+import (
+	"bytes"
+	"crypto/sha256"
+	"encoding/json"
+	"fmt"
+	"time"
+
+	acm "github.com/hyperledger/burrow/account"
+	"github.com/hyperledger/burrow/genesis"
+	"github.com/hyperledger/burrow/keys"
+	"github.com/hyperledger/burrow/permission"
+	ptypes "github.com/hyperledger/burrow/permission/types"
+)
+
+const DefaultAmount uint64 = 1000000
+const DefaultAmountBonded uint64 = 10000
+
+// A GenesisSpec is schematic representation of a genesis state, that is it is a template
+// for a GenesisDoc excluding that which needs to be instantiated at the point of genesis
+// so it describes the type and number of accounts, the genesis salt, but not the
+// account keys or addresses, or the GenesisTime. It is responsible for generating keys
+// 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"`
+}
+
+type TemplateAccount struct {
+	// Address  is convenient to have in file for reference, but otherwise ignored since derived from PublicKey
+	Address *acm.Address   `json:",omitempty"`
+	PubKey  *acm.PublicKey `json:",omitempty"`
+	Amount  *uint64        `json:",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"`
+}
+
+func (ta TemplateAccount) Validator(keyClient keys.KeyClient, index int) (*genesis.Validator, error) {
+	var err error
+	gv := new(genesis.Validator)
+	gv.PublicKey, gv.Address, err = ta.RealisePubKeyAndAddress(keyClient)
+	if err != nil {
+		return nil, err
+	}
+	if ta.AmountBonded == nil {
+		gv.Amount = DefaultAmountBonded
+	} else {
+		gv.Amount = *ta.AmountBonded
+	}
+	if ta.Name == "" {
+		gv.Name = accountNameFromIndex(index)
+	} else {
+		gv.Name = ta.Name
+	}
+
+	gv.UnbondTo = []genesis.BasicAccount{{
+		Address:   gv.Address,
+		PublicKey: gv.PublicKey,
+		Amount:    gv.Amount,
+	}}
+	return gv, nil
+}
+
+func (ta TemplateAccount) AccountPermissions() (ptypes.AccountPermissions, error) {
+	basePerms, err := permission.BasePermissionsFromStringList(ta.Permissions)
+	if err != nil {
+		return permission.ZeroAccountPermissions, nil
+	}
+	return ptypes.AccountPermissions{
+		Base:  basePerms,
+		Roles: ta.Roles,
+	}, nil
+}
+
+func (ta TemplateAccount) Account(keyClient keys.KeyClient, index int) (*genesis.Account, error) {
+	var err error
+	ga := new(genesis.Account)
+	ga.PublicKey, ga.Address, err = ta.RealisePubKeyAndAddress(keyClient)
+	if err != nil {
+		return nil, err
+	}
+	if ta.Amount == nil {
+		ga.Amount = DefaultAmount
+	} else {
+		ga.Amount = *ta.Amount
+	}
+	if ta.Name == "" {
+		ga.Name = accountNameFromIndex(index)
+	} else {
+		ga.Name = ta.Name
+	}
+	if ta.Permissions == nil {
+		ga.Permissions = permission.DefaultAccountPermissions.Clone()
+	} else {
+		ga.Permissions, err = ta.AccountPermissions()
+		if err != nil {
+			return nil, err
+		}
+	}
+	return ga, nil
+}
+
+// Adds a public key and address to the template. If PublicKey will try to fetch it by Address.
+// If both PublicKey and Address are not set will use the keyClient to generate a new keypair
+func (ta TemplateAccount) RealisePubKeyAndAddress(keyClient keys.KeyClient) (pubKey acm.PublicKey, address acm.Address, err error) {
+	if ta.PubKey == nil {
+		if ta.Address == nil {
+			// If neither PublicKey or Address set then generate a new one
+			address, err = keyClient.Generate(ta.Name, keys.KeyTypeEd25519Ripemd160)
+			if err != nil {
+				return
+			}
+		} else {
+			address = *ta.Address
+		}
+		// Get the (possibly existing) key
+		pubKey, err = keyClient.PublicKey(address)
+		if err != nil {
+			return
+		}
+	} else {
+		address := ta.PubKey.Address()
+		if ta.Address != nil && *ta.Address != address {
+			err = fmt.Errorf("template address %s does not match public key derived address %s", ta.Address,
+				ta.PubKey)
+		}
+	}
+	return
+}
+
+// Produce a fully realised GenesisDoc from a template GenesisDoc that may omit values
+func (gs *GenesisSpec) GenesisDoc(keyClient keys.KeyClient) (*genesis.GenesisDoc, error) {
+	genesisDoc := new(genesis.GenesisDoc)
+	if gs.GenesisTime == nil {
+		genesisDoc.GenesisTime = time.Now()
+	} else {
+		genesisDoc.GenesisTime = *gs.GenesisTime
+	}
+
+	if gs.ChainName == "" {
+		genesisDoc.ChainName = fmt.Sprintf("BurrowChain_%X", gs.ShortHash())
+	} else {
+		genesisDoc.ChainName = gs.ChainName
+	}
+
+	if len(gs.GlobalPermissions) == 0 {
+		genesisDoc.GlobalPermissions = permission.DefaultAccountPermissions.Clone()
+	} else {
+		basePerms, err := permission.BasePermissionsFromStringList(gs.GlobalPermissions)
+		if err != nil {
+			return nil, err
+		}
+		genesisDoc.GlobalPermissions = ptypes.AccountPermissions{
+			Base: basePerms,
+		}
+	}
+
+	templateAccounts := gs.Accounts
+	if len(gs.Accounts) == 0 {
+		amountBonded := DefaultAmountBonded
+		templateAccounts = append(templateAccounts, TemplateAccount{
+			AmountBonded: &amountBonded,
+		})
+	}
+
+	for i, templateAccount := range templateAccounts {
+		account, err := templateAccount.Account(keyClient, i)
+		if err != nil {
+			return nil, fmt.Errorf("could not create Account from template: %v", err)
+		}
+		genesisDoc.Accounts = append(genesisDoc.Accounts, *account)
+		// Create a corresponding validator
+		if templateAccount.AmountBonded != nil {
+			// Note this does not modify the input template
+			templateAccount.Address = &account.Address
+			validator, err := templateAccount.Validator(keyClient, i)
+			if err != nil {
+				return nil, fmt.Errorf("could not create Validator from template: %v", err)
+			}
+			genesisDoc.Validators = append(genesisDoc.Validators, *validator)
+		}
+	}
+
+	return genesisDoc, nil
+}
+
+func (gs *GenesisSpec) JSONBytes() ([]byte, error) {
+	bs, err := json.Marshal(gs)
+	if err != nil {
+		return nil, err
+	}
+	// rewrite buffer with indentation
+	indentedBuffer := new(bytes.Buffer)
+	if err := json.Indent(indentedBuffer, bs, "", "\t"); err != nil {
+		return nil, err
+	}
+	return indentedBuffer.Bytes(), nil
+}
+
+func (gs *GenesisSpec) Hash() []byte {
+	gsBytes, err := gs.JSONBytes()
+	if err != nil {
+		panic(fmt.Errorf("could not create hash of GenesisDoc: %v", err))
+	}
+	hasher := sha256.New()
+	hasher.Write(gsBytes)
+	return hasher.Sum(nil)
+}
+
+func (gs *GenesisSpec) ShortHash() []byte {
+	return gs.Hash()[:genesis.ShortHashSuffixBytes]
+}
+
+func GenesisSpecFromJSON(jsonBlob []byte) (*GenesisSpec, error) {
+	genDoc := new(GenesisSpec)
+	err := json.Unmarshal(jsonBlob, genDoc)
+	if err != nil {
+		return nil, fmt.Errorf("couldn't read GenesisSpec: %v", err)
+	}
+	return genDoc, nil
+}
+
+func accountNameFromIndex(index int) string {
+	return fmt.Sprintf("Account_%v", index)
+}
diff --git a/genesis/spec/genesis_spec_test.go b/genesis/spec/genesis_spec_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..19dae19ffe1a6b1bf03ca69c8c5e3c16600545d9
--- /dev/null
+++ b/genesis/spec/genesis_spec_test.go
@@ -0,0 +1,91 @@
+package spec
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/hyperledger/burrow/keys"
+	"github.com/hyperledger/burrow/keys/mock"
+	"github.com/hyperledger/burrow/permission"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestGenesisSpec_GenesisDoc(t *testing.T) {
+	keyClient := mock.NewMockKeyClient()
+
+	// Try a spec with a single account/validator
+	amtBonded := uint64(100)
+	genesisSpec := GenesisSpec{
+		Accounts: []TemplateAccount{{
+			AmountBonded: &amtBonded,
+		}},
+	}
+
+	genesisDoc, err := genesisSpec.GenesisDoc(keyClient)
+	require.NoError(t, err)
+	require.Len(t, genesisDoc.Accounts, 1)
+	// Should create validator
+	require.Len(t, genesisDoc.Validators, 1)
+	assert.NotZero(t, genesisDoc.Accounts[0].Address)
+	assert.NotZero(t, genesisDoc.Accounts[0].PublicKey)
+	assert.Equal(t, genesisDoc.Accounts[0].Address, genesisDoc.Validators[0].Address)
+	assert.Equal(t, genesisDoc.Accounts[0].PublicKey, genesisDoc.Validators[0].PublicKey)
+	assert.Equal(t, amtBonded, genesisDoc.Validators[0].Amount)
+	assert.NotEmpty(t, genesisDoc.ChainName, "Chain name should not be empty")
+
+	address, err := keyClient.Generate("test-lookup-of-key", keys.KeyTypeEd25519Ripemd160)
+	require.NoError(t, err)
+	pubKey, err := keyClient.PublicKey(address)
+	require.NoError(t, err)
+
+	// Try a spec with two accounts and no validators
+	amt := uint64(99299299)
+	genesisSpec = GenesisSpec{
+		Accounts: []TemplateAccount{
+			{
+				Address: &address,
+			},
+			{
+				Amount:      &amt,
+				Permissions: []string{permission.CreateAccountString, permission.CallString},
+			}},
+	}
+
+	genesisDoc, err = genesisSpec.GenesisDoc(keyClient)
+	require.NoError(t, err)
+
+	require.Len(t, genesisDoc.Accounts, 2)
+	// Nothing bonded so no validators
+	require.Len(t, genesisDoc.Validators, 0)
+	assert.Equal(t, pubKey, genesisDoc.Accounts[0].PublicKey)
+	assert.Equal(t, amt, genesisDoc.Accounts[1].Amount)
+	permFlag := permission.CreateAccount | permission.Call
+	assert.Equal(t, permFlag, genesisDoc.Accounts[1].Permissions.Base.Perms)
+	assert.Equal(t, permFlag, genesisDoc.Accounts[1].Permissions.Base.SetBit)
+
+	// Try an empty spec
+	genesisSpec = GenesisSpec{}
+
+	genesisDoc, err = genesisSpec.GenesisDoc(keyClient)
+	require.NoError(t, err)
+
+	// Similar assersions to first case - should generate our default single identity chain
+	require.Len(t, genesisDoc.Accounts, 1)
+	// Should create validator
+	require.Len(t, genesisDoc.Validators, 1)
+	assert.NotZero(t, genesisDoc.Accounts[0].Address)
+	assert.NotZero(t, genesisDoc.Accounts[0].PublicKey)
+	assert.Equal(t, genesisDoc.Accounts[0].Address, genesisDoc.Validators[0].Address)
+	assert.Equal(t, genesisDoc.Accounts[0].PublicKey, genesisDoc.Validators[0].PublicKey)
+}
+
+func TestJSONRoundTrip(t *testing.T) {
+
+	var a []byte
+
+	b := []byte{1, 2, 3}
+
+	c := append(b, a...)
+	fmt.Println(c)
+}
diff --git a/genesis/spec/presets.go b/genesis/spec/presets.go
new file mode 100644
index 0000000000000000000000000000000000000000..727b9236e869b5ac1ede7374226155f17dbc9c81
--- /dev/null
+++ b/genesis/spec/presets.go
@@ -0,0 +1,74 @@
+package spec
+
+import (
+	"fmt"
+
+	"sort"
+
+	"github.com/hyperledger/burrow/permission"
+)
+
+// 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 {
+	// 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),
+			Amount:       &amount,
+			AmountBonded: &amountBonded,
+			Permissions:  []string{permission.AllString},
+		},
+		},
+	}
+}
+
+func ParticipantAccount(index int) GenesisSpec {
+	amount := uint64(9999999999)
+	return GenesisSpec{
+		Accounts: []TemplateAccount{{
+			Name:        fmt.Sprintf("Participant_%v", index),
+			Amount:      &amount,
+			Permissions: []string{permission.SendString, permission.CallString, permission.NameString, permission.HasRoleString},
+		}},
+	}
+}
+
+func MergeGenesisSpecs(genesisSpecs ...GenesisSpec) GenesisSpec {
+	mergedGenesisSpec := GenesisSpec{}
+	// We will deduplicate and merge global permissions flags
+	permSet := make(map[string]bool)
+
+	for _, genesisSpec := range genesisSpecs {
+		// We'll overwrite chain name for later specs
+		if genesisSpec.ChainName != "" {
+			mergedGenesisSpec.ChainName = genesisSpec.ChainName
+		}
+		// Take the max genesis time
+		if mergedGenesisSpec.GenesisTime == nil ||
+			(genesisSpec.GenesisTime != nil && genesisSpec.GenesisTime.After(*mergedGenesisSpec.GenesisTime)) {
+			mergedGenesisSpec.GenesisTime = genesisSpec.GenesisTime
+		}
+
+		for _, permString := range genesisSpec.GlobalPermissions {
+			permSet[permString] = true
+		}
+
+		mergedGenesisSpec.Salt = append(mergedGenesisSpec.Salt, genesisSpec.Salt...)
+		mergedGenesisSpec.Accounts = append(mergedGenesisSpec.Accounts, genesisSpec.Accounts...)
+	}
+
+	mergedGenesisSpec.GlobalPermissions = make([]string, 0, len(permSet))
+
+	for permString := range permSet {
+		mergedGenesisSpec.GlobalPermissions = append(mergedGenesisSpec.GlobalPermissions, permString)
+	}
+
+	// Make sure merged GenesisSpec is deterministic on inputs
+	sort.Strings(mergedGenesisSpec.GlobalPermissions)
+
+	return mergedGenesisSpec
+}
diff --git a/genesis/spec/presets_test.go b/genesis/spec/presets_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..dc65fd9e92c34671439f0d5e21744ee2b83db7dc
--- /dev/null
+++ b/genesis/spec/presets_test.go
@@ -0,0 +1,35 @@
+package spec
+
+import (
+	"testing"
+
+	"github.com/hyperledger/burrow/keys/mock"
+	"github.com/hyperledger/burrow/permission"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestMergeGenesisSpecAccounts(t *testing.T) {
+	keyClient := mock.NewMockKeyClient()
+	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) {
+	gs1 := GenesisSpec{
+		GlobalPermissions: []string{permission.CreateAccountString, permission.CreateAccountString},
+	}
+	gs2 := GenesisSpec{
+		GlobalPermissions: []string{permission.SendString, permission.CreateAccountString, permission.HasRoleString},
+	}
+
+	gsMerged := MergeGenesisSpecs(gs1, gs2)
+	assert.Equal(t, []string{permission.CreateAccountString, permission.HasRoleString, permission.SendString},
+		gsMerged.GlobalPermissions)
+}
diff --git a/genesis/types.go b/genesis/types.go
deleted file mode 100644
index 5f1d4ada090dc8465e3b43f76e8fa8414de35390..0000000000000000000000000000000000000000
--- a/genesis/types.go
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright 2017 Monax Industries Limited
-//
-// 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.
-
-package genesis
-
-import (
-	"fmt"
-	"os"
-	"time"
-
-	ptypes "github.com/hyperledger/burrow/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"`
-}
-
-// GenesisPrivateValidator marshals the state of the private
-// validator for the purpose of Genesis creation; and hence
-// is defined in genesis and not under consensus, where
-// PrivateValidator (currently inherited from Tendermint) is.
-type GenesisPrivateValidator struct {
-	Address    string        `json:"address"`
-	PubKey     []interface{} `json:"pub_key"`
-	PrivKey    []interface{} `json:"priv_key"`
-	LastHeight int64         `json:"last_height"`
-	LastRound  int64         `json:"last_round"`
-	LastStep   int64         `json:"last_step"`
-}
-
-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
-}
-
-//------------------------------------------------------------
-// Methods for genesis types
-// NOTE: breaks formatting convention
-// TODO: split each genesis type in its own file definition
-
-//------------------------------------------------------------
-// GenesisAccount methods
-
-// Clone clones the genesis account
-func (genesisAccount *GenesisAccount) Clone() GenesisAccount {
-	// clone the address
-	addressClone := make([]byte, len(genesisAccount.Address))
-	copy(addressClone, genesisAccount.Address)
-	// clone the account permissions
-	accountPermissionsClone := genesisAccount.Permissions.Clone()
-	return GenesisAccount{
-		Address:     addressClone,
-		Amount:      genesisAccount.Amount,
-		Name:        genesisAccount.Name,
-		Permissions: &accountPermissionsClone,
-	}
-}
-
-//------------------------------------------------------------
-// GenesisValidator methods
-
-// Clone clones the genesis validator
-func (genesisValidator *GenesisValidator) Clone() (GenesisValidator, error) {
-	if genesisValidator == nil {
-		return GenesisValidator{}, fmt.Errorf("Cannot clone nil GenesisValidator.")
-	}
-	if genesisValidator.PubKey == nil {
-		return GenesisValidator{}, fmt.Errorf("Invalid GenesisValidator %s with nil public key.",
-			genesisValidator.Name)
-	}
-	// clone the addresses to unbond to
-	unbondToClone := make([]BasicAccount, len(genesisValidator.UnbondTo))
-	for i, basicAccount := range genesisValidator.UnbondTo {
-		unbondToClone[i] = basicAccount.Clone()
-	}
-	return GenesisValidator{
-		PubKey:   genesisValidator.PubKey,
-		Amount:   genesisValidator.Amount,
-		Name:     genesisValidator.Name,
-		UnbondTo: unbondToClone,
-	}, nil
-}
-
-//------------------------------------------------------------
-// BasicAccount methods
-
-// Clone clones the basic account
-func (basicAccount *BasicAccount) Clone() BasicAccount {
-	// clone the address
-	addressClone := make([]byte, len(basicAccount.Address))
-	copy(addressClone, basicAccount.Address)
-	return BasicAccount{
-		Address: addressClone,
-		Amount:  basicAccount.Amount,
-	}
-}