Repository: hyperledger/firefly
Pull request #947 (2022-08-10 00:10:33)

Replace namespace.remotename with namespace.multiparty.networkname
This is a proposed rename for discussion.

Currently a namespace can have a `name` and a `remoteName` - `name` is how it's known locally (on APIs and most database objects), while `remoteName` is how it's written into objects that transmit the namespace in a multi-party system (specifically messages, batches, and groups on dataexchange, and the BatchPin method of the V1 blockchain contract).

Since the thing we've called `remoteName` is entirely specific to multiparty networks, it could be renamed to `multiparty.networkNamespace`, which seems more accurate. It would still default to the value of the local namespace `name` if unset.

Counterpoints:
- These fields will continue to show as `localNamespace` and `namespace` on messages and groups when queried (and the latter can't easily be modified due to the way hashes are computed).
- ~~Tokens plugins have a similar `name` and `remoteName` (one is stored locally and one is sent in messages), so this would be a departure from that terminology.~~ Update: decided to rename the tokens plugin `remoteName` to `broadcastName` based on comments below.


Patch:
diff --git a/docs/reference/config.md b/docs/reference/config.md
index ac017bb52..740408c87 100644
--- a/docs/reference/config.md
+++ b/docs/reference/config.md
@@ -633,7 +633,6 @@ nav_order: 2
 |description|A description for the namespace|`string`|`<nil>`
 |name|The name of the namespace (must be unique)|`string`|`<nil>`
 |plugins|The list of plugins for this namespace|`string`|`<nil>`
-|remotename|The namespace name to be sent in plugin calls, if it differs from namespace name|`string`|`<nil>`
 
 ## namespaces.predefined[].asset.manager
 
@@ -646,6 +645,7 @@ nav_order: 2
 |Key|Description|Type|Default Value|
 |---|-----------|----|-------------|
 |enabled|Enables multi-party mode for this namespace (defaults to true if an org name or key is configured, either here or at the root level)|`boolean`|`<nil>`
+|networknamespace|The shared namespace name to be sent in multiparty messages, if it differs from the local namespace name|`string`|`<nil>`
 
 ## namespaces.predefined[].multiparty.contract[]
 
@@ -717,7 +717,7 @@ nav_order: 2
 |dataexchange|The array of configured Data Exchange plugins |`string`|`<nil>`
 |identity|The list of available Identity plugins|`string`|`<nil>`
 |sharedstorage|The list of configured Shared Storage plugins|`string`|`<nil>`
-|tokens|The tokens plugin configurations. This will be used to configure tokens connectors|`string`|`<nil>`
+|tokens|The token plugin configurations|`string`|`<nil>`
 
 ## plugins.auth[]
 
@@ -1109,9 +1109,9 @@ nav_order: 2
 
 |Key|Description|Type|Default Value|
 |---|-----------|----|-------------|
-|name|The name of the Tokens Connector. This will be used in the FireFly API path to refer to this specific Token Connector|`string`|`<nil>`
-|remotename|The remote name of the Tokens Connector. This will be used in messages and events to refer to this specific Token Connector, if it differs from the name|`string`|`<nil>`
-|type|The type of the Tokens Connector plugin to use|`string`|`<nil>`
+|broadcastName|The name to be used in broadcast messages related to this token plugin, if it differs from the local plugin name|`string`|`<nil>`
+|name|A name to identify this token plugin|`string`|`<nil>`
+|type|The type of the token plugin to use|`string`|`<nil>`
 
 ## plugins.tokens[].fftokens
 
@@ -1124,7 +1124,7 @@ nav_order: 2
 |maxIdleConns|The max number of idle connections to hold pooled|`int`|`100`
 |requestTimeout|The maximum amount of time that a request is allowed to remain open|[`time.Duration`](https://pkg.go.dev/time#Duration)|`30s`
 |tlsHandshakeTimeout|The maximum amount of time to wait for a successful TLS handshake|[`time.Duration`](https://pkg.go.dev/time#Duration)|`10s`
-|url|The URL of the Token Connector|URL `string`|`<nil>`
+|url|The URL of the token connector|URL `string`|`<nil>`
 
 ## plugins.tokens[].fftokens.auth
 
@@ -1137,7 +1137,7 @@ nav_order: 2
 
 |Key|Description|Type|Default Value|
 |---|-----------|----|-------------|
-|url|Optional HTTP proxy server to use when connecting to the Token Connector|URL `string`|`<nil>`
+|url|Optional HTTP proxy server to use when connecting to the token connector|URL `string`|`<nil>`
 
 ## plugins.tokens[].fftokens.retry
 
@@ -1323,11 +1323,11 @@ nav_order: 2
 |headers|Adds custom headers to HTTP requests|`map[string]string`|`<nil>`
 |idleTimeout|The max duration to hold a HTTP keepalive connection between calls|[`time.Duration`](https://pkg.go.dev/time#Duration)|`<nil>`
 |maxIdleConns|The max number of idle connections to hold pooled|`int`|`<nil>`
-|name|The name of the Tokens Connector. This will be used in the FireFly API path to refer to this specific Token Connector|`string`|`<nil>`
-|plugin|The name of the Tokens Connector plugin to use|`string`|`<nil>`
+|name|A name to identify this token plugin|`string`|`<nil>`
+|plugin|The type of the token plugin to use|`string`|`<nil>`
 |requestTimeout|The maximum amount of time that a request is allowed to remain open|[`time.Duration`](https://pkg.go.dev/time#Duration)|`<nil>`
 |tlsHandshakeTimeout|The maximum amount of time to wait for a successful TLS handshake|[`time.Duration`](https://pkg.go.dev/time#Duration)|`<nil>`
-|url|The URL of the Token Connector|URL `string`|`<nil>`
+|url|The URL of the token connector|URL `string`|`<nil>`
 
 ## tokens[].auth
 
@@ -1340,7 +1340,7 @@ nav_order: 2
 
 |Key|Description|Type|Default Value|
 |---|-----------|----|-------------|
-|url|Optional HTTP proxy server to use when connecting to the Token Connector|URL `string`|`<nil>`
+|url|Optional HTTP proxy server to use when connecting to the token connector|URL `string`|`<nil>`
 
 ## tokens[].retry
 
diff --git a/docs/reference/types/namespace.md b/docs/reference/types/namespace.md
index 9297ccb9f..6f77b4589 100644
--- a/docs/reference/types/namespace.md
+++ b/docs/reference/types/namespace.md
@@ -25,7 +25,7 @@ nav_order: 20
 ```json
 {
     "name": "default",
-    "remoteName": "default",
+    "networkName": "default",
     "description": "Default predefined namespace",
     "created": "2022-05-16T01:23:16Z"
 }
@@ -36,7 +36,7 @@ nav_order: 20
 | Field Name | Description | Type |
 |------------|-------------|------|
 | `name` | The local namespace name | `string` |
-| `remoteName` | The namespace name within the multiparty network | `string` |
+| `networkName` | The shared namespace name within the multiparty network | `string` |
 | `description` | A description of the namespace | `string` |
 | `created` | The time the namespace was created | [`FFTime`](simpletypes#fftime) |
 
diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml
index da5f799f6..0671cc9c4 100644
--- a/docs/swagger/swagger.yaml
+++ b/docs/swagger/swagger.yaml
@@ -8790,8 +8790,9 @@ paths:
                     name:
                       description: The local namespace name
                       type: string
-                    remoteName:
-                      description: The namespace name within the multiparty network
+                    networkName:
+                      description: The shared namespace name within the multiparty
+                        network
                       type: string
                   type: object
                 type: array
@@ -8835,8 +8836,8 @@ paths:
                   name:
                     description: The local namespace name
                     type: string
-                  remoteName:
-                    description: The namespace name within the multiparty network
+                  networkName:
+                    description: The shared namespace name within the multiparty network
                     type: string
                 type: object
           description: Success
@@ -20533,8 +20534,9 @@ paths:
                       name:
                         description: The local namespace name
                         type: string
-                      remoteName:
-                        description: The namespace name within the multiparty network
+                      networkName:
+                        description: The shared namespace name within the multiparty
+                          network
                         type: string
                     type: object
                   node:
@@ -28094,8 +28096,9 @@ paths:
                       name:
                         description: The local namespace name
                         type: string
-                      remoteName:
-                        description: The namespace name within the multiparty network
+                      networkName:
+                        description: The shared namespace name within the multiparty
+                          network
                         type: string
                     type: object
                   node:
diff --git a/internal/blockchain/common/common.go b/internal/blockchain/common/common.go
index 9786f6a0a..3c63d5a3f 100644
--- a/internal/blockchain/common/common.go
+++ b/internal/blockchain/common/common.go
@@ -39,7 +39,7 @@ type BlockchainCallbacks interface {
 }
 
 type FireflySubscriptions interface {
-	AddSubscription(ctx context.Context, namespace core.NamespaceRef, version int, subID string, extra interface{})
+	AddSubscription(ctx context.Context, namespace *core.Namespace, version int, subID string, extra interface{})
 	RemoveSubscription(ctx context.Context, subID string)
 	GetSubscription(subID string) *SubscriptionInfo
 }
@@ -239,7 +239,7 @@ func GetNamespaceFromSubName(subName string) string {
 	return parts[2]
 }
 
-func (s *subscriptions) AddSubscription(ctx context.Context, namespace core.NamespaceRef, version int, subID string, extra interface{}) {
+func (s *subscriptions) AddSubscription(ctx context.Context, namespace *core.Namespace, version int, subID string, extra interface{}) {
 	if version == 1 {
 		// The V1 contract shares a single subscription per contract, and the remote namespace name is passed on chain.
 		// Therefore, it requires a map of remote->local in order to farm out notifications to one or more local handlers.
@@ -252,13 +252,13 @@ func (s *subscriptions) AddSubscription(ctx context.Context, namespace core.Name
 			}
 			s.subs[subID] = existing
 		}
-		existing.V1Namespace[namespace.RemoteName] = append(existing.V1Namespace[namespace.RemoteName], namespace.LocalName)
+		existing.V1Namespace[namespace.NetworkName] = append(existing.V1Namespace[namespace.NetworkName], namespace.Name)
 	} else {
 		// The V2 contract does not pass the namespace on chain, and requires a separate contract instance (and subscription) per namespace.
 		// Therefore, the local namespace name can simply be cached alongside each subscription.
 		s.subs[subID] = &SubscriptionInfo{
 			Version:     version,
-			V2Namespace: namespace.LocalName,
+			V2Namespace: namespace.Name,
 			Extra:       extra,
 		}
 	}
diff --git a/internal/blockchain/ethereum/ethereum.go b/internal/blockchain/ethereum/ethereum.go
index c9d09e030..73efd1638 100644
--- a/internal/blockchain/ethereum/ethereum.go
+++ b/internal/blockchain/ethereum/ethereum.go
@@ -195,7 +195,7 @@ func (e *Ethereum) Capabilities() *blockchain.Capabilities {
 	return e.capabilities
 }
 
-func (e *Ethereum) AddFireflySubscription(ctx context.Context, namespace core.NamespaceRef, location *fftypes.JSONAny, firstEvent string) (string, error) {
+func (e *Ethereum) AddFireflySubscription(ctx context.Context, namespace *core.Namespace, location *fftypes.JSONAny, firstEvent string) (string, error) {
 	ethLocation, err := parseContractLocation(ctx, location)
 	if err != nil {
 		return "", err
@@ -206,7 +206,7 @@ func (e *Ethereum) AddFireflySubscription(ctx context.Context, namespace core.Na
 		return "", err
 	}
 
-	sub, err := e.streams.ensureFireFlySubscription(ctx, namespace.LocalName, version, ethLocation.Address, firstEvent, e.streamID, batchPinEventABI)
+	sub, err := e.streams.ensureFireFlySubscription(ctx, namespace.Name, version, ethLocation.Address, firstEvent, e.streamID, batchPinEventABI)
 	if err != nil {
 		return "", err
 	}
@@ -558,7 +558,7 @@ func (e *Ethereum) queryContractMethod(ctx context.Context, address string, abi
 	return res, nil
 }
 
-func (e *Ethereum) SubmitBatchPin(ctx context.Context, nsOpID, remoteNamespace, signingKey string, batch *blockchain.BatchPin, location *fftypes.JSONAny) error {
+func (e *Ethereum) SubmitBatchPin(ctx context.Context, nsOpID, networkNamespace, signingKey string, batch *blockchain.BatchPin, location *fftypes.JSONAny) error {
 	ethLocation, err := parseContractLocation(ctx, location)
 	if err != nil {
 		return err
@@ -583,7 +583,7 @@ func (e *Ethereum) SubmitBatchPin(ctx context.Context, nsOpID, remoteNamespace,
 	if version == 1 {
 		method = batchPinMethodABIV1
 		input = []interface{}{
-			remoteNamespace,
+			networkNamespace,
 			ethHexFormatB32(&uuids),
 			ethHexFormatB32(batch.BatchHash),
 			batch.BatchPayloadRef,
diff --git a/internal/blockchain/fabric/fabric.go b/internal/blockchain/fabric/fabric.go
index 0e7abf7ec..500a47afd 100644
--- a/internal/blockchain/fabric/fabric.go
+++ b/internal/blockchain/fabric/fabric.go
@@ -380,7 +380,7 @@ func (f *Fabric) handleReceipt(ctx context.Context, reply fftypes.JSONObject) {
 	f.callbacks.OperationUpdate(ctx, f, requestID, updateType, txHash, message, reply)
 }
 
-func (f *Fabric) AddFireflySubscription(ctx context.Context, namespace core.NamespaceRef, location *fftypes.JSONAny, firstEvent string) (string, error) {
+func (f *Fabric) AddFireflySubscription(ctx context.Context, namespace *core.Namespace, location *fftypes.JSONAny, firstEvent string) (string, error) {
 	fabricOnChainLocation, err := parseContractLocation(ctx, location)
 	if err != nil {
 		return "", err
@@ -391,7 +391,7 @@ func (f *Fabric) AddFireflySubscription(ctx context.Context, namespace core.Name
 		return "", err
 	}
 
-	sub, err := f.streams.ensureFireFlySubscription(ctx, namespace.LocalName, version, fabricOnChainLocation, firstEvent, f.streamID, batchPinEvent)
+	sub, err := f.streams.ensureFireFlySubscription(ctx, namespace.Name, version, fabricOnChainLocation, firstEvent, f.streamID, batchPinEvent)
 	if err != nil {
 		return "", err
 	}
@@ -594,7 +594,7 @@ func hexFormatB32(b *fftypes.Bytes32) string {
 	return "0x" + hex.EncodeToString(b[0:32])
 }
 
-func (f *Fabric) SubmitBatchPin(ctx context.Context, nsOpID, remoteNamespace, signingKey string, batch *blockchain.BatchPin, location *fftypes.JSONAny) error {
+func (f *Fabric) SubmitBatchPin(ctx context.Context, nsOpID, networkNamespace, signingKey string, batch *blockchain.BatchPin, location *fftypes.JSONAny) error {
 	fabricOnChainLocation, err := parseContractLocation(ctx, location)
 	if err != nil {
 		return err
@@ -619,7 +619,7 @@ func (f *Fabric) SubmitBatchPin(ctx context.Context, nsOpID, remoteNamespace, si
 	if version == 1 {
 		prefixItems = batchPinPrefixItemsV1
 		pinInput = map[string]interface{}{
-			"namespace":  remoteNamespace,
+			"namespace":  networkNamespace,
 			"uuids":      hexFormatB32(&uuids),
 			"batchHash":  hexFormatB32(batch.BatchHash),
 			"payloadRef": batch.BatchPayloadRef,
diff --git a/internal/broadcast/manager.go b/internal/broadcast/manager.go
index dbb17330d..e6add739d 100644
--- a/internal/broadcast/manager.go
+++ b/internal/broadcast/manager.go
@@ -56,7 +56,7 @@ type Manager interface {
 
 type broadcastManager struct {
 	ctx                   context.Context
-	namespace             core.NamespaceRef
+	namespace             *core.Namespace
 	database              database.Plugin
 	identity              identity.Manager
 	data                  data.Manager
@@ -70,7 +70,7 @@ type broadcastManager struct {
 	operations            operations.Manager
 }
 
-func NewBroadcastManager(ctx context.Context, ns core.NamespaceRef, di database.Plugin, bi blockchain.Plugin, dx dataexchange.Plugin, si sharedstorage.Plugin, im identity.Manager, dm data.Manager, ba batch.Manager, sa syncasync.Bridge, mult multiparty.Manager, mm metrics.Manager, om operations.Manager) (Manager, error) {
+func NewBroadcastManager(ctx context.Context, ns *core.Namespace, di database.Plugin, bi blockchain.Plugin, dx dataexchange.Plugin, si sharedstorage.Plugin, im identity.Manager, dm data.Manager, ba batch.Manager, sa syncasync.Bridge, mult multiparty.Manager, mm metrics.Manager, om operations.Manager) (Manager, error) {
 	if di == nil || im == nil || dm == nil || bi == nil || dx == nil || si == nil || ba == nil || mm == nil || om == nil || mult == nil {
 		return nil, i18n.NewError(ctx, coremsgs.MsgInitializationNilDepError, "BroadcastManager")
 	}
@@ -128,7 +128,7 @@ func (bm *broadcastManager) dispatchBatch(ctx context.Context, state *batch.Disp
 	// Upload the batch itself
 	op := core.NewOperation(
 		bm.sharedstorage,
-		bm.namespace.LocalName,
+		bm.namespace.Name,
 		state.Persisted.TX.ID,
 		core.OpTypeSharedStorageUploadBatch)
 	addUploadBatchInputs(op, state.Persisted.ID)
@@ -156,7 +156,7 @@ func (bm *broadcastManager) uploadBlobs(ctx context.Context, tx *fftypes.UUID, d
 
 			op := core.NewOperation(
 				bm.sharedstorage,
-				bm.namespace.LocalName,
+				bm.namespace.Name,
 				tx,
 				core.OpTypeSharedStorageUploadBlob)
 			addUploadBlobInputs(op, d.ID)
diff --git a/internal/broadcast/message.go b/internal/broadcast/message.go
index b993b6164..cb83cc242 100644
--- a/internal/broadcast/message.go
+++ b/internal/broadcast/message.go
@@ -87,8 +87,8 @@ func (s *broadcastSender) SendAndWait(ctx context.Context) error {
 func (s *broadcastSender) setDefaults() {
 	msg := s.msg.Message
 	msg.Header.ID = fftypes.NewUUID()
-	msg.Header.Namespace = s.mgr.namespace.RemoteName
-	msg.LocalNamespace = s.mgr.namespace.LocalName
+	msg.Header.Namespace = s.mgr.namespace.NetworkName
+	msg.LocalNamespace = s.mgr.namespace.Name
 	msg.State = core.MessageStateReady
 	if msg.Header.Type == "" {
 		msg.Header.Type = core.MessageTypeBroadcast
diff --git a/internal/broadcast/operations.go b/internal/broadcast/operations.go
index 24e389142..16c10ea24 100644
--- a/internal/broadcast/operations.go
+++ b/internal/broadcast/operations.go
@@ -77,7 +77,7 @@ func (bm *broadcastManager) PrepareOperation(ctx context.Context, op *core.Opera
 		if err != nil {
 			return nil, err
 		}
-		bp, err := bm.database.GetBatchByID(ctx, bm.namespace.LocalName, id)
+		bp, err := bm.database.GetBatchByID(ctx, bm.namespace.Name, id)
 		if err != nil {
 			return nil, err
 		} else if bp == nil {
@@ -94,7 +94,7 @@ func (bm *broadcastManager) PrepareOperation(ctx context.Context, op *core.Opera
 		if err != nil {
 			return nil, err
 		}
-		d, err := bm.database.GetDataByID(ctx, bm.namespace.LocalName, dataID, false)
+		d, err := bm.database.GetDataByID(ctx, bm.namespace.Name, dataID, false)
 		if err != nil {
 			return nil, err
 		} else if d == nil || d.Blob == nil {
@@ -127,7 +127,7 @@ func (bm *broadcastManager) RunOperation(ctx context.Context, op *core.PreparedO
 // uploadBatch uploads the serialized batch to public storage
 func (bm *broadcastManager) uploadBatch(ctx context.Context, data uploadBatchData) (outputs fftypes.JSONObject, complete bool, err error) {
 	// Serialize the full payload, which has already been sealed for us by the BatchManager
-	data.Batch.Namespace = bm.namespace.RemoteName
+	data.Batch.Namespace = bm.namespace.NetworkName
 	payload, err := json.Marshal(data.Batch)
 	if err != nil {
 		return nil, false, i18n.WrapError(ctx, err, coremsgs.MsgSerializationFailed)
@@ -159,7 +159,7 @@ func (bm *broadcastManager) uploadBlob(ctx context.Context, data uploadBlobData)
 	}
 
 	// Update the data in the DB
-	err = bm.database.UpdateData(ctx, bm.namespace.LocalName, data.Data.ID, database.DataQueryFactory.NewUpdate(ctx).Set("blob.public", data.Data.Blob.Public))
+	err = bm.database.UpdateData(ctx, bm.namespace.Name, data.Data.ID, database.DataQueryFactory.NewUpdate(ctx).Set("blob.public", data.Data.Blob.Public))
 	if err != nil {
 		return nil, false, err
 	}
diff --git a/internal/coreconfig/coreconfig.go b/internal/coreconfig/coreconfig.go
index 8eaa6f7bc..19d619bb1 100644
--- a/internal/coreconfig/coreconfig.go
+++ b/internal/coreconfig/coreconfig.go
@@ -29,14 +29,12 @@ const (
 	PluginConfigName = "name"
 	// PluginConfigType is the type of the plugin to be loaded
 	PluginConfigType = "type"
-	// PluginRemoteName is the plugin name to be sent in plugin calls
-	PluginRemoteName = "remotename"
+	// PluginBroadcastName is the plugin name to be sent in multi-party broadcasts, if it differs from the local plugin name
+	PluginBroadcastName = "broadcastName"
 	// NamespaceName is the short name for a pre-defined namespace
 	NamespaceName = "name"
 	// NamespaceName is the long description for a pre-defined namespace
 	NamespaceDescription = "description"
-	// NamespaceRemoteName is the namespace name to be sent in plugin calls
-	NamespaceRemoteName = "remotename"
 	// NamespacePlugins is the list of namespace plugins
 	NamespacePlugins = "plugins"
 	// NamespaceDefaultKey is the default signing key for blockchain transactions within this namespace
@@ -47,6 +45,8 @@ const (
 	NamespaceMultiparty = "multiparty"
 	// NamespaceMultipartyEnabled specifies if multi-party mode is enabled for a namespace
 	NamespaceMultipartyEnabled = "enabled"
+	// NamespaceMultipartyNetworkNamespace is the shared namespace name to be used in off-chain messaging
+	NamespaceMultipartyNetworkNamespace = "networknamespace"
 	// NamespaceMultipartyOrgName is a short name for the local root org within a namespace
 	NamespaceMultipartyOrgName = "org.name"
 	// NamespaceMultipartyOrgDescription is a description for the local root org within a namespace
diff --git a/internal/coremsgs/en_config_descriptions.go b/internal/coremsgs/en_config_descriptions.go
index b975ab5ba..b5495523f 100644
--- a/internal/coremsgs/en_config_descriptions.go
+++ b/internal/coremsgs/en_config_descriptions.go
@@ -264,10 +264,10 @@ var (
 	ConfigNamespacesPredefinedName               = ffc("config.namespaces.predefined[].name", "The name of the namespace (must be unique)", i18n.StringType)
 	ConfigNamespacesPredefinedDescription        = ffc("config.namespaces.predefined[].description", "A description for the namespace", i18n.StringType)
 	ConfigNamespacesPredefinedPlugins            = ffc("config.namespaces.predefined[].plugins", "The list of plugins for this namespace", i18n.StringType)
-	ConfigNamespacesPredefinedRemoteName         = ffc("config.namespaces.predefined[].remotename", "The namespace name to be sent in plugin calls, if it differs from namespace name", i18n.StringType)
 	ConfigNamespacesPredefinedDefaultKey         = ffc("config.namespaces.predefined[].defaultKey", "A default signing key for blockchain transactions within this namespace", i18n.StringType)
 	ConfigNamespacesPredefinedKeyNormalization   = ffc("config.namespaces.predefined[].asset.manager.keyNormalization", "Mechanism to normalize keys before using them. Valid options are `blockchain_plugin` - use blockchain plugin (default) or `none` - do not attempt normalization", i18n.StringType)
 	ConfigNamespacesMultipartyEnabled            = ffc("config.namespaces.predefined[].multiparty.enabled", "Enables multi-party mode for this namespace (defaults to true if an org name or key is configured, either here or at the root level)", i18n.BooleanType)
+	ConfigNamespacesMultipartyNetworkNamespace   = ffc("config.namespaces.predefined[].multiparty.networknamespace", "The shared namespace name to be sent in multiparty messages, if it differs from the local namespace name", i18n.StringType)
 	ConfigNamespacesMultipartyOrgName            = ffc("config.namespaces.predefined[].multiparty.org.name", "A short name for the local root organization within this namespace", i18n.StringType)
 	ConfigNamespacesMultipartyOrgDesc            = ffc("config.namespaces.predefined[].multiparty.org.description", "A description for the local root organization within this namespace", i18n.StringType)
 	ConfigNamespacesMultipartyOrgKey             = ffc("config.namespaces.predefined[].multiparty.org.key", "The signing key allocated to the root organization within this namespace", i18n.StringType)
@@ -313,19 +313,17 @@ var (
 	ConfigSubscriptionMax               = ffc("config.subscription.max", "The maximum number of pre-defined subscriptions that can exist (note for high fan-out consider connecting a dedicated pub/sub broker to the dispatcher)", i18n.IntType)
 	ConfigSubscriptionDefaultsBatchSize = ffc("config.subscription.defaults.batchSize", "Default read ahead to enable for subscriptions that do not explicitly configure readahead", i18n.IntType)
 
-	ConfigTokensConnector = ffc("config.tokens[].connector", "The name of the Tokens Connector. This will be used in the FireFly API path to refer to this specific Token Connector", i18n.StringType)
-	ConfigTokensName      = ffc("config.tokens[].name", "The name of the Tokens Connector. This will be used in the FireFly API path to refer to this specific Token Connector", i18n.StringType)
-	ConfigTokensPlugin    = ffc("config.tokens[].plugin", "The name of the Tokens Connector plugin to use", i18n.StringType)
-	ConfigTokensURL       = ffc("config.tokens[].url", "The URL of the Token Connector", "URL "+i18n.StringType)
-
-	ConfigPluginTokens           = ffc("config.plugins.tokens", "The tokens plugin configurations. This will be used to configure tokens connectors", i18n.StringType)
-	ConfigPluginTokensName       = ffc("config.plugins.tokens[].name", "The name of the Tokens Connector. This will be used in the FireFly API path to refer to this specific Token Connector", i18n.StringType)
-	ConfigPluginTokensRemoteName = ffc("config.plugins.tokens[].remotename", "The remote name of the Tokens Connector. This will be used in messages and events to refer to this specific Token Connector, if it differs from the name", i18n.StringType)
-	ConfigPluginTokensType       = ffc("config.plugins.tokens[].type", "The type of the Tokens Connector plugin to use", i18n.StringType)
-	ConfigPluginTokensURL        = ffc("config.plugins.tokens[].fftokens.url", "The URL of the Token Connector", "URL "+i18n.StringType)
-	ConfigPluginTokensProxyURL   = ffc("config.plugins.tokens[].fftokens.proxy.url", "Optional HTTP proxy server to use when connecting to the Token Connector", "URL "+i18n.StringType)
-
-	ConfigTokensProxyURL = ffc("config.tokens[].proxy.url", "Optional HTTP proxy server to use when connecting to the Token Connector", "URL "+i18n.StringType)
+	ConfigTokensName     = ffc("config.tokens[].name", "A name to identify this token plugin", i18n.StringType)
+	ConfigTokensPlugin   = ffc("config.tokens[].plugin", "The type of the token plugin to use", i18n.StringType)
+	ConfigTokensURL      = ffc("config.tokens[].url", "The URL of the token connector", "URL "+i18n.StringType)
+	ConfigTokensProxyURL = ffc("config.tokens[].proxy.url", "Optional HTTP proxy server to use when connecting to the token connector", "URL "+i18n.StringType)
+
+	ConfigPluginTokens              = ffc("config.plugins.tokens", "The token plugin configurations", i18n.StringType)
+	ConfigPluginTokensName          = ffc("config.plugins.tokens[].name", "A name to identify this token plugin", i18n.StringType)
+	ConfigPluginTokensBroadcastName = ffc("config.plugins.tokens[].broadcastName", "The name to be used in broadcast messages related to this token plugin, if it differs from the local plugin name", i18n.StringType)
+	ConfigPluginTokensType          = ffc("config.plugins.tokens[].type", "The type of the token plugin to use", i18n.StringType)
+	ConfigPluginTokensURL           = ffc("config.plugins.tokens[].fftokens.url", "The URL of the token connector", "URL "+i18n.StringType)
+	ConfigPluginTokensProxyURL      = ffc("config.plugins.tokens[].fftokens.proxy.url", "Optional HTTP proxy server to use when connecting to the token connector", "URL "+i18n.StringType)
 
 	ConfigUIEnabled = ffc("config.ui.enabled", "Enables the web user interface", i18n.BooleanType)
 	ConfigUIPath    = ffc("config.ui.path", "The file system path which contains the static HTML, CSS, and JavaScript files for the user interface", i18n.StringType)
diff --git a/internal/coremsgs/en_error_messages.go b/internal/coremsgs/en_error_messages.go
index 02bfa6842..18e52ad54 100644
--- a/internal/coremsgs/en_error_messages.go
+++ b/internal/coremsgs/en_error_messages.go
@@ -258,7 +258,7 @@ var (
 	MsgInvalidSubscriptionForNetwork      = ffe("FF10416", "Subscription name '%s' is invalid according to multiparty network rules in effect (network version=%d)")
 	MsgBlockchainNotConfigured            = ffe("FF10417", "No blockchain plugin configured")
 	MsgInvalidBatchPinEvent               = ffe("FF10418", "BatchPin event is not valid - %s (%s): %s")
-	MsgDuplicatePluginRemoteName          = ffe("FF10419", "Invalid %s plugin remote name: %s - remote names must be unique")
+	MsgDuplicatePluginBroadcastName       = ffe("FF10419", "Invalid %s plugin broadcast name: %s - broadcast names must be unique")
 	MsgInvalidConnectorName               = ffe("FF10420", "Could not find name %s for %s connector")
 	MsgCannotInitLegacyNS                 = ffe("FF10421", "could not initialize legacy '%s' namespace - found conflicting V1 multi-party config in %s and %s")
 )
diff --git a/internal/coremsgs/en_struct_descriptions.go b/internal/coremsgs/en_struct_descriptions.go
index 1921479a5..607eecdf5 100644
--- a/internal/coremsgs/en_struct_descriptions.go
+++ b/internal/coremsgs/en_struct_descriptions.go
@@ -377,7 +377,7 @@ var (
 
 	// Namespace field descriptions
 	NamespaceName                  = ffm("Namespace.name", "The local namespace name")
-	NamespaceRemoteName            = ffm("Namespace.remoteName", "The namespace name within the multiparty network")
+	NamespaceNetworkName           = ffm("Namespace.networkName", "The shared namespace name within the multiparty network")
 	NamespaceDescription           = ffm("Namespace.description", "A description of the namespace")
 	NamespaceCreated               = ffm("Namespace.created", "The time the namespace was created")
 	MultipartyContractsActive      = ffm("MultipartyContracts.active", "The currently active FireFly smart contract")
diff --git a/internal/data/blobstore.go b/internal/data/blobstore.go
index 4d36740f5..ab585ceb9 100644
--- a/internal/data/blobstore.go
+++ b/internal/data/blobstore.go
@@ -53,7 +53,7 @@ func (bs *blobStore) uploadVerifyBlob(ctx context.Context, id *fftypes.UUID, rea
 		copyDone <- err
 	}()
 
-	payloadRef, uploadHash, uploadSize, dxErr := bs.exchange.UploadBlob(ctx, bs.dm.namespace.RemoteName, *id, dxReader)
+	payloadRef, uploadHash, uploadSize, dxErr := bs.exchange.UploadBlob(ctx, bs.dm.namespace.NetworkName, *id, dxReader)
 	dxReader.Close()
 	copyErr := <-copyDone
 	if dxErr != nil {
@@ -85,7 +85,7 @@ func (bs *blobStore) UploadBlob(ctx context.Context, inData *core.DataRefOrValue
 
 	data := &core.Data{
 		ID:        fftypes.NewUUID(),
-		Namespace: bs.dm.namespace.LocalName,
+		Namespace: bs.dm.namespace.Name,
 		Created:   fftypes.Now(),
 		Validator: inData.Validator,
 		Datatype:  inData.Datatype,
@@ -151,7 +151,7 @@ func (bs *blobStore) DownloadBlob(ctx context.Context, dataID string) (*core.Blo
 		return nil, nil, err
 	}
 
-	data, err := bs.database.GetDataByID(ctx, bs.dm.namespace.LocalName, id, false)
+	data, err := bs.database.GetDataByID(ctx, bs.dm.namespace.Name, id, false)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/internal/data/data_manager.go b/internal/data/data_manager.go
index d8c2f6f38..b696013b6 100644
--- a/internal/data/data_manager.go
+++ b/internal/data/data_manager.go
@@ -57,7 +57,7 @@ type Manager interface {
 
 type dataManager struct {
 	blobStore
-	namespace         core.NamespaceRef
+	namespace         *core.Namespace
 	database          database.Plugin
 	validatorCache    *ccache.Cache
 	validatorCacheTTL time.Duration
@@ -94,7 +94,7 @@ const (
 	CRORequireBatchID
 )
 
-func NewDataManager(ctx context.Context, ns core.NamespaceRef, di database.Plugin, dx dataexchange.Plugin) (Manager, error) {
+func NewDataManager(ctx context.Context, ns *core.Namespace, di database.Plugin, dx dataexchange.Plugin) (Manager, error) {
 	if di == nil {
 		return nil, i18n.NewError(ctx, coremsgs.MsgInitializationNilDepError, "DataManager")
 	}
@@ -132,7 +132,7 @@ func (dm *dataManager) Start() {
 }
 
 func (dm *dataManager) CheckDatatype(ctx context.Context, datatype *core.Datatype) error {
-	_, err := newJSONValidator(ctx, dm.namespace.LocalName, datatype)
+	_, err := newJSONValidator(ctx, dm.namespace.Name, datatype)
 	return err
 }
 
@@ -143,26 +143,26 @@ func (dm *dataManager) getValidatorForDatatype(ctx context.Context, validator co
 	}
 
 	if datatypeRef == nil || datatypeRef.Name == "" || datatypeRef.Version == "" {
-		log.L(ctx).Warnf("Invalid datatype reference '%s:%s:%s'", validator, dm.namespace, datatypeRef)
+		log.L(ctx).Warnf("Invalid datatype reference '%s:%s:%s'", validator, dm.namespace.Name, datatypeRef)
 		return nil, nil
 	}
 
-	key := fmt.Sprintf("%s:%s:%s", validator, dm.namespace, datatypeRef)
+	key := fmt.Sprintf("%s:%s:%s", validator, dm.namespace.Name, datatypeRef)
 	if cached := dm.validatorCache.Get(key); cached != nil {
 		cached.Extend(dm.validatorCacheTTL)
 		return cached.Value().(Validator), nil
 	}
 
-	datatype, err := dm.database.GetDatatypeByName(ctx, dm.namespace.LocalName, datatypeRef.Name, datatypeRef.Version)
+	datatype, err := dm.database.GetDatatypeByName(ctx, dm.namespace.Name, datatypeRef.Name, datatypeRef.Version)
 	if err != nil {
 		return nil, err
 	}
 	if datatype == nil {
 		return nil, nil
 	}
-	v, err := newJSONValidator(ctx, dm.namespace.LocalName, datatype)
+	v, err := newJSONValidator(ctx, dm.namespace.Name, datatype)
 	if err != nil {
-		log.L(ctx).Errorf("Invalid validator stored for '%s:%s:%s': %s", validator, dm.namespace, datatypeRef, err)
+		log.L(ctx).Errorf("Invalid validator stored for '%s:%s:%s': %s", validator, dm.namespace.Name, datatypeRef, err)
 		return nil, nil
 	}
 
@@ -177,7 +177,7 @@ func (dm *dataManager) GetMessageWithDataCached(ctx context.Context, msgID *ffty
 	if mce := dm.queryMessageCache(ctx, msgID, options...); mce != nil {
 		return mce.msg, mce.data, true, nil
 	}
-	msg, err = dm.database.GetMessageByID(ctx, dm.namespace.LocalName, msgID)
+	msg, err = dm.database.GetMessageByID(ctx, dm.namespace.Name, msgID)
 	if err != nil || msg == nil {
 		return nil, nil, false, err
 	}
@@ -246,7 +246,7 @@ func (dm *dataManager) queryMessageCache(ctx context.Context, id *fftypes.UUID,
 	}
 	log.L(ctx).Debugf("Cache hit for message %s", id)
 	cached.Extend(dm.messageCacheTTL)
-	mce.msg.LocalNamespace = dm.namespace.LocalName // always populate LocalNamespace on the way out of the cache
+	mce.msg.LocalNamespace = dm.namespace.Name // always populate LocalNamespace on the way out of the cache
 	return mce
 }
 
@@ -324,7 +324,7 @@ func (dm *dataManager) resolveRef(ctx context.Context, dataRef *core.DataRef) (*
 		log.L(ctx).Warnf("data is nil")
 		return nil, nil
 	}
-	d, err := dm.database.GetDataByID(ctx, dm.namespace.LocalName, dataRef.ID, true)
+	d, err := dm.database.GetDataByID(ctx, dm.namespace.Name, dataRef.ID, true)
 	if err != nil {
 		return nil, err
 	}
@@ -403,7 +403,7 @@ func (dm *dataManager) validateInputData(ctx context.Context, inData *core.DataR
 	data = &core.Data{
 		Validator: validator,
 		Datatype:  datatype,
-		Namespace: dm.namespace.LocalName,
+		Namespace: dm.namespace.Name,
 		Value:     value,
 		Blob:      blobRef,
 	}
@@ -481,7 +481,7 @@ func (dm *dataManager) HydrateBatch(ctx context.Context, persistedBatch *core.Ba
 	batch := persistedBatch.GenInflight(make([]*core.Message, len(manifest.Messages)), make(core.DataArray, len(manifest.Data)))
 
 	for i, mr := range manifest.Messages {
-		m, err := dm.database.GetMessageByID(ctx, dm.namespace.LocalName, mr.ID)
+		m, err := dm.database.GetMessageByID(ctx, dm.namespace.Name, mr.ID)
 		if err != nil || m == nil {
 			return nil, i18n.WrapError(ctx, err, coremsgs.MsgFailedToRetrieve, "message", mr.ID)
 		}
@@ -489,7 +489,7 @@ func (dm *dataManager) HydrateBatch(ctx context.Context, persistedBatch *core.Ba
 		batch.Payload.Messages[i] = m.BatchMessage()
 	}
 	for i, dr := range manifest.Data {
-		d, err := dm.database.GetDataByID(ctx, dm.namespace.LocalName, dr.ID, true)
+		d, err := dm.database.GetDataByID(ctx, dm.namespace.Name, dr.ID, true)
 		if err != nil || d == nil {
 			return nil, i18n.WrapError(ctx, err, coremsgs.MsgFailedToRetrieve, "data", dr.ID)
 		}
diff --git a/internal/database/sqlcommon/namespace_sql.go b/internal/database/sqlcommon/namespace_sql.go
index 7a228a3b9..2e7a7b73e 100644
--- a/internal/database/sqlcommon/namespace_sql.go
+++ b/internal/database/sqlcommon/namespace_sql.go
@@ -52,7 +52,7 @@ func (s *SQLCommon) UpsertNamespace(ctx context.Context, namespace *core.Namespa
 		namespaceRows, _, err := s.queryTx(ctx, namespacesTable, tx,
 			sq.Select("seq").
 				From(namespacesTable).
-				Where(sq.Eq{"name": namespace.LocalName}),
+				Where(sq.Eq{"name": namespace.Name}),
 		)
 		if err != nil {
 			return err
@@ -65,11 +65,11 @@ func (s *SQLCommon) UpsertNamespace(ctx context.Context, namespace *core.Namespa
 		// Update the namespace
 		if _, err = s.updateTx(ctx, namespacesTable, tx,
 			sq.Update(namespacesTable).
-				Set("remote_name", namespace.RemoteName).
+				Set("remote_name", namespace.NetworkName).
 				Set("description", namespace.Description).
 				Set("created", namespace.Created).
 				Set("firefly_contracts", namespace.Contracts).
-				Where(sq.Eq{"name": namespace.LocalName}),
+				Where(sq.Eq{"name": namespace.Name}),
 			nil,
 		); err != nil {
 			return err
@@ -79,8 +79,8 @@ func (s *SQLCommon) UpsertNamespace(ctx context.Context, namespace *core.Namespa
 			sq.Insert(namespacesTable).
 				Columns(namespaceColumns...).
 				Values(
-					namespace.LocalName,
-					namespace.RemoteName,
+					namespace.Name,
+					namespace.NetworkName,
 					namespace.Description,
 					namespace.Created,
 					namespace.Contracts,
@@ -97,8 +97,8 @@ func (s *SQLCommon) UpsertNamespace(ctx context.Context, namespace *core.Namespa
 func (s *SQLCommon) namespaceResult(ctx context.Context, row *sql.Rows) (*core.Namespace, error) {
 	namespace := core.Namespace{}
 	err := row.Scan(
-		&namespace.LocalName,
-		&namespace.RemoteName,
+		&namespace.Name,
+		&namespace.NetworkName,
 		&namespace.Description,
 		&namespace.Created,
 		&namespace.Contracts,
diff --git a/internal/dataexchange/ffdx/ffdx.go b/internal/dataexchange/ffdx/ffdx.go
index 6c71fd1ae..e7e135d50 100644
--- a/internal/dataexchange/ffdx/ffdx.go
+++ b/internal/dataexchange/ffdx/ffdx.go
@@ -197,8 +197,8 @@ func (h *FFDX) Init(ctx context.Context, config config.Section) (err error) {
 	return nil
 }
 
-func (h *FFDX) SetHandler(remoteNamespace, nodeName string, handler dataexchange.Callbacks) {
-	key := remoteNamespace + ":" + nodeName
+func (h *FFDX) SetHandler(networkNamespace, nodeName string, handler dataexchange.Callbacks) {
+	key := networkNamespace + ":" + nodeName
 	h.callbacks.handlers[key] = handler
 }
 
@@ -270,11 +270,11 @@ func (h *FFDX) GetEndpointInfo(ctx context.Context, nodeName string) (peer fftyp
 	return peer, nil
 }
 
-func (h *FFDX) AddNode(ctx context.Context, remoteNamespace, nodeName string, peer fftypes.JSONObject) (err error) {
+func (h *FFDX) AddNode(ctx context.Context, networkNamespace, nodeName string, peer fftypes.JSONObject) (err error) {
 	h.initMutex.Lock()
 	defer h.initMutex.Unlock()
 
-	key := remoteNamespace + ":" + h.GetPeerID(peer)
+	key := networkNamespace + ":" + h.GetPeerID(peer)
 	h.nodes[key] = &dxNode{
 		Peer: peer,
 		Name: nodeName,
diff --git a/internal/definitions/handler.go b/internal/definitions/handler.go
index 5515495a8..8e3c4a761 100644
--- a/internal/definitions/handler.go
+++ b/internal/definitions/handler.go
@@ -77,35 +77,33 @@ func (dma DefinitionMessageAction) String() string {
 }
 
 type definitionHandler struct {
-	namespace       string
-	remoteNamespace string
-	multiparty      bool
-	database        database.Plugin
-	blockchain      blockchain.Plugin   // optional
-	exchange        dataexchange.Plugin // optional
-	data            data.Manager
-	identity        identity.Manager
-	assets          assets.Manager
-	contracts       contracts.Manager // optional
-	tokenNames      map[string]string // mapping of token connector remote name => name
+	namespace  *core.Namespace
+	multiparty bool
+	database   database.Plugin
+	blockchain blockchain.Plugin   // optional
+	exchange   dataexchange.Plugin // optional
+	data       data.Manager
+	identity   identity.Manager
+	assets     assets.Manager
+	contracts  contracts.Manager // optional
+	tokenNames map[string]string // mapping of token connector remote name => name
 }
 
-func newDefinitionHandler(ctx context.Context, ns, remoteNS string, multiparty bool, di database.Plugin, bi blockchain.Plugin, dx dataexchange.Plugin, dm data.Manager, im identity.Manager, am assets.Manager, cm contracts.Manager, tokenNames map[string]string) (*definitionHandler, error) {
+func newDefinitionHandler(ctx context.Context, ns *core.Namespace, multiparty bool, di database.Plugin, bi blockchain.Plugin, dx dataexchange.Plugin, dm data.Manager, im identity.Manager, am assets.Manager, cm contracts.Manager, tokenNames map[string]string) (*definitionHandler, error) {
 	if di == nil || dm == nil || im == nil || am == nil {
 		return nil, i18n.NewError(ctx, coremsgs.MsgInitializationNilDepError, "DefinitionHandler")
 	}
 	return &definitionHandler{
-		namespace:       ns,
-		remoteNamespace: remoteNS,
-		multiparty:      multiparty,
-		database:        di,
-		blockchain:      bi,
-		exchange:        dx,
-		data:            dm,
-		identity:        im,
-		assets:          am,
-		contracts:       cm,
-		tokenNames:      tokenNames,
+		namespace:  ns,
+		multiparty: multiparty,
+		database:   di,
+		blockchain: bi,
+		exchange:   dx,
+		data:       dm,
+		identity:   im,
+		assets:     am,
+		contracts:  cm,
+		tokenNames: tokenNames,
 	}, nil
 }
 
diff --git a/internal/definitions/handler_contracts.go b/internal/definitions/handler_contracts.go
index 9e68f2940..257f7826c 100644
--- a/internal/definitions/handler_contracts.go
+++ b/internal/definitions/handler_contracts.go
@@ -77,7 +77,7 @@ func (dh *definitionHandler) handleFFIBroadcast(ctx context.Context, state *core
 
 func (dh *definitionHandler) handleFFIDefinition(ctx context.Context, state *core.BatchState, ffi *fftypes.FFI, tx *fftypes.UUID) (HandlerResult, error) {
 	l := log.L(ctx)
-	ffi.Namespace = dh.namespace
+	ffi.Namespace = dh.namespace.Name
 	if err := ffi.Validate(ctx, true); err != nil {
 		return HandlerResult{Action: ActionReject}, i18n.NewError(ctx, coremsgs.MsgDefRejectedValidateFail, "contract interface", ffi.ID, err)
 	}
@@ -108,7 +108,7 @@ func (dh *definitionHandler) handleContractAPIBroadcast(ctx context.Context, sta
 
 func (dh *definitionHandler) handleContractAPIDefinition(ctx context.Context, state *core.BatchState, httpServerURL string, api *core.ContractAPI, tx *fftypes.UUID) (HandlerResult, error) {
 	l := log.L(ctx)
-	api.Namespace = dh.namespace
+	api.Namespace = dh.namespace.Name
 	if err := api.Validate(ctx, true); err != nil {
 		return HandlerResult{Action: ActionReject}, i18n.NewError(ctx, coremsgs.MsgDefRejectedValidateFail, "contract API", api.ID, err)
 	}
diff --git a/internal/definitions/handler_datatype.go b/internal/definitions/handler_datatype.go
index ed5983d4c..f74e54d1f 100644
--- a/internal/definitions/handler_datatype.go
+++ b/internal/definitions/handler_datatype.go
@@ -31,7 +31,7 @@ func (dh *definitionHandler) handleDatatypeBroadcast(ctx context.Context, state
 	if !valid {
 		return HandlerResult{Action: ActionReject}, i18n.NewError(ctx, coremsgs.MsgDefRejectedBadPayload, "datatype", msg.Header.ID)
 	}
-	dt.Namespace = dh.namespace
+	dt.Namespace = dh.namespace.Name
 	if err := dt.Validate(ctx, true); err != nil {
 		return HandlerResult{Action: ActionReject}, i18n.NewError(ctx, coremsgs.MsgDefRejectedValidateFail, "datatype", dt.ID, err)
 	}
diff --git a/internal/definitions/handler_identity_claim.go b/internal/definitions/handler_identity_claim.go
index 784d6bea8..95ca37bb4 100644
--- a/internal/definitions/handler_identity_claim.go
+++ b/internal/definitions/handler_identity_claim.go
@@ -109,7 +109,7 @@ func (dh *definitionHandler) confirmVerificationForClaim(ctx context.Context, st
 		fb.Eq("state", core.MessageStateConfirmed),
 		fb.Eq("tag", core.SystemTagIdentityVerification),
 	)
-	candidates, _, err := dh.database.GetMessages(ctx, dh.namespace, filter)
+	candidates, _, err := dh.database.GetMessages(ctx, dh.namespace.Name, filter)
 	if err != nil {
 		return nil, err
 	}
@@ -135,7 +135,7 @@ func (dh *definitionHandler) confirmVerificationForClaim(ctx context.Context, st
 			if !dh.getSystemBroadcastPayload(ctx, candidate, data, &verification) {
 				return nil, nil
 			}
-			verification.Identity.Namespace = dh.namespace
+			verification.Identity.Namespace = dh.namespace.Name
 			identityMatches = verification.Identity.Equals(ctx, &identity.IdentityBase)
 			verificationID = verification.Claim.ID
 			verificationHash = verification.Claim.Hash
@@ -152,7 +152,7 @@ func (dh *definitionHandler) handleIdentityClaim(ctx context.Context, state *cor
 	l := log.L(ctx)
 
 	identity := identityClaim.Identity
-	identity.Namespace = dh.namespace
+	identity.Namespace = dh.namespace.Name
 	parent, retryable, err := dh.identity.VerifyIdentityChain(ctx, identity)
 	if err != nil {
 		if retryable {
@@ -175,7 +175,7 @@ func (dh *definitionHandler) handleIdentityClaim(ctx context.Context, state *cor
 
 	existingIdentity, err := dh.database.GetIdentityByName(ctx, identity.Type, identity.Namespace, identity.Name)
 	if err == nil && existingIdentity == nil {
-		existingIdentity, err = dh.database.GetIdentityByID(ctx, dh.namespace, identity.ID)
+		existingIdentity, err = dh.database.GetIdentityByID(ctx, dh.namespace.Name, identity.ID)
 	}
 	if err != nil {
 		return HandlerResult{Action: ActionRetry}, err // retry database errors
@@ -234,7 +234,7 @@ func (dh *definitionHandler) handleIdentityClaim(ctx context.Context, state *cor
 		state.AddPreFinalize(
 			func(ctx context.Context) error {
 				// Tell the data exchange about this node. Treat these errors like database errors - and return for retry processing
-				return dh.exchange.AddNode(ctx, dh.remoteNamespace, identity.Name, identity.Profile)
+				return dh.exchange.AddNode(ctx, dh.namespace.NetworkName, identity.Name, identity.Profile)
 			})
 	}
 
diff --git a/internal/definitions/handler_identity_verification.go b/internal/definitions/handler_identity_verification.go
index cc59cb823..834108fe6 100644
--- a/internal/definitions/handler_identity_verification.go
+++ b/internal/definitions/handler_identity_verification.go
@@ -30,7 +30,7 @@ func (dh *definitionHandler) handleIdentityVerificationBroadcast(ctx context.Con
 	if !valid {
 		return HandlerResult{Action: ActionReject}, i18n.NewError(ctx, coremsgs.MsgDefRejectedBadPayload, "identity verification", verifyMsg.Header.ID)
 	}
-	verification.Identity.Namespace = dh.namespace
+	verification.Identity.Namespace = dh.namespace.Name
 	err := verification.Identity.Validate(ctx)
 	if err != nil || verification.Identity.Parent == nil || verification.Claim.ID == nil || verification.Claim.Hash == nil {
 		return HandlerResult{Action: ActionReject}, i18n.NewError(ctx, coremsgs.MsgDefRejectedValidateFail, "identity verification", verifyMsg.Header.ID, err)
@@ -50,7 +50,7 @@ func (dh *definitionHandler) handleIdentityVerificationBroadcast(ctx context.Con
 
 	// At this point, this is a valid verification, but we don't know if the claim has arrived.
 	// See if the message has already arrived, if so we need to queue a rewind to it
-	claimMsg, err := dh.database.GetMessageByID(ctx, dh.namespace, verification.Claim.ID)
+	claimMsg, err := dh.database.GetMessageByID(ctx, dh.namespace.Name, verification.Claim.ID)
 	if err != nil {
 		return HandlerResult{Action: ActionRetry}, err
 	}
diff --git a/internal/definitions/handler_tokenpool.go b/internal/definitions/handler_tokenpool.go
index 13e24a7a8..0fa5f3e1e 100644
--- a/internal/definitions/handler_tokenpool.go
+++ b/internal/definitions/handler_tokenpool.go
@@ -50,13 +50,13 @@ func (dh *definitionHandler) handleTokenPoolDefinition(ctx context.Context, stat
 	// from the event (without downloading and parsing the msg)
 	correlator := pool.ID
 
-	pool.Namespace = dh.namespace
+	pool.Namespace = dh.namespace.Name
 	if err := pool.Validate(ctx); err != nil {
 		return HandlerResult{Action: ActionReject, CustomCorrelator: correlator}, i18n.NewError(ctx, coremsgs.MsgDefRejectedValidateFail, "token pool", pool.ID, err)
 	}
 
 	// Check if pool has already been confirmed on chain (and confirm the message if so)
-	if existingPool, err := dh.database.GetTokenPoolByID(ctx, dh.namespace, pool.ID); err != nil {
+	if existingPool, err := dh.database.GetTokenPoolByID(ctx, dh.namespace.Name, pool.ID); err != nil {
 		return HandlerResult{Action: ActionRetry}, err
 	} else if existingPool != nil && existingPool.State == core.TokenPoolStateConfirmed {
 		return HandlerResult{Action: ActionConfirm, CustomCorrelator: correlator}, nil
diff --git a/internal/definitions/sender.go b/internal/definitions/sender.go
index aad58aed9..364c1097b 100644
--- a/internal/definitions/sender.go
+++ b/internal/definitions/sender.go
@@ -46,16 +46,16 @@ type Sender interface {
 }
 
 type definitionSender struct {
-	ctx              context.Context
-	namespace        string
-	multiparty       bool
-	database         database.Plugin
-	broadcast        broadcast.Manager // optional
-	identity         identity.Manager
-	data             data.Manager
-	contracts        contracts.Manager // optional
-	handler          *definitionHandler
-	tokenRemoteNames map[string]string // mapping of token connector name => remote name
+	ctx                 context.Context
+	namespace           string
+	multiparty          bool
+	database            database.Plugin
+	broadcast           broadcast.Manager // optional
+	identity            identity.Manager
+	data                data.Manager
+	contracts           contracts.Manager // optional
+	handler             *definitionHandler
+	tokenBroadcastNames map[string]string // mapping of token connector name => remote name
 }
 
 // Definitions that get processed immediately will create a temporary batch state and then finalize it inline
@@ -71,22 +71,22 @@ func fakeBatch(ctx context.Context, handler func(context.Context, *core.BatchSta
 	return err
 }
 
-func NewDefinitionSender(ctx context.Context, ns, remoteNS string, multiparty bool, di database.Plugin, bi blockchain.Plugin, dx dataexchange.Plugin, bm broadcast.Manager, im identity.Manager, dm data.Manager, am assets.Manager, cm contracts.Manager, tokenRemoteNames map[string]string) (Sender, Handler, error) {
+func NewDefinitionSender(ctx context.Context, ns *core.Namespace, multiparty bool, di database.Plugin, bi blockchain.Plugin, dx dataexchange.Plugin, bm broadcast.Manager, im identity.Manager, dm data.Manager, am assets.Manager, cm contracts.Manager, tokenBroadcastNames map[string]string) (Sender, Handler, error) {
 	if di == nil || im == nil || dm == nil {
 		return nil, nil, i18n.NewError(ctx, coremsgs.MsgInitializationNilDepError, "DefinitionSender")
 	}
 	ds := &definitionSender{
-		ctx:              ctx,
-		namespace:        ns,
-		multiparty:       multiparty,
-		database:         di,
-		broadcast:        bm,
-		identity:         im,
-		data:             dm,
-		contracts:        cm,
-		tokenRemoteNames: tokenRemoteNames,
+		ctx:                 ctx,
+		namespace:           ns.Name,
+		multiparty:          multiparty,
+		database:            di,
+		broadcast:           bm,
+		identity:            im,
+		data:                dm,
+		contracts:           cm,
+		tokenBroadcastNames: tokenBroadcastNames,
 	}
-	dh, err := newDefinitionHandler(ctx, ns, remoteNS, multiparty, di, bi, dx, dm, im, am, cm, reverseMap(tokenRemoteNames))
+	dh, err := newDefinitionHandler(ctx, ns, multiparty, di, bi, dx, dm, im, am, cm, reverseMap(tokenBroadcastNames))
 	ds.handler = dh
 	return ds, dh, err
 }
diff --git a/internal/definitions/sender_tokenpool.go b/internal/definitions/sender_tokenpool.go
index 3b3632d72..a5172419d 100644
--- a/internal/definitions/sender_tokenpool.go
+++ b/internal/definitions/sender_tokenpool.go
@@ -26,12 +26,12 @@ import (
 )
 
 func (bm *definitionSender) DefineTokenPool(ctx context.Context, pool *core.TokenPoolAnnouncement, waitConfirm bool) error {
-	// Map token connector name -> remote name
-	if remoteName, exists := bm.tokenRemoteNames[pool.Pool.Connector]; exists {
-		pool.Pool.Connector = remoteName
+	// Map token connector name -> broadcast name
+	if broadcastName, exists := bm.tokenBroadcastNames[pool.Pool.Connector]; exists {
+		pool.Pool.Connector = broadcastName
 	} else {
-		log.L(ctx).Infof("Could not find remote name for token connector: %s", pool.Pool.Connector)
-		return i18n.NewError(ctx, coremsgs.MsgInvalidConnectorName, remoteName, "token")
+		log.L(ctx).Infof("Could not find broadcast name for token connector: %s", pool.Pool.Connector)
+		return i18n.NewError(ctx, coremsgs.MsgInvalidConnectorName, broadcastName, "token")
 	}
 
 	if bm.multiparty {
diff --git a/internal/events/batch_pin_complete.go b/internal/events/batch_pin_complete.go
index 966d9fa4e..88e5e4efd 100644
--- a/internal/events/batch_pin_complete.go
+++ b/internal/events/batch_pin_complete.go
@@ -40,7 +40,7 @@ func (em *eventManager) BatchPinComplete(namespace string, batchPin *blockchain.
 		log.L(em.ctx).Errorf("Invalid BatchPin transaction - ID is nil")
 		return nil // move on
 	}
-	if namespace != em.namespace.LocalName {
+	if namespace != em.namespace.Name {
 		log.L(em.ctx).Debugf("Ignoring batch pin from different namespace '%s'", namespace)
 		return nil // move on
 	}
@@ -60,7 +60,7 @@ func (em *eventManager) BatchPinComplete(namespace string, batchPin *blockchain.
 			if err := em.persistBatchTransaction(ctx, batchPin); err != nil {
 				return err
 			}
-			chainEvent := buildBlockchainEvent(em.namespace.LocalName, nil, &batchPin.Event, &core.BlockchainTransactionRef{
+			chainEvent := buildBlockchainEvent(em.namespace.Name, nil, &batchPin.Event, &core.BlockchainTransactionRef{
 				Type:         core.TransactionTypeBatchPin,
 				ID:           batchPin.TransactionID,
 				BlockchainID: batchPin.Event.BlockchainTXID,
@@ -102,7 +102,7 @@ func (em *eventManager) persistContexts(ctx context.Context, batchPin *blockchai
 	pins := make([]*core.Pin, len(batchPin.Contexts))
 	for idx, hash := range batchPin.Contexts {
 		pins[idx] = &core.Pin{
-			Namespace: em.namespace.LocalName,
+			Namespace: em.namespace.Name,
 			Masked:    private,
 			Hash:      hash,
 			Batch:     batchPin.BatchID,
diff --git a/internal/events/blockchain_event.go b/internal/events/blockchain_event.go
index b408a4667..193e021d3 100644
--- a/internal/events/blockchain_event.go
+++ b/internal/events/blockchain_event.go
@@ -46,7 +46,7 @@ func buildBlockchainEvent(ns string, subID *fftypes.UUID, event *blockchain.Even
 
 func (em *eventManager) getChainListenerByProtocolIDCached(ctx context.Context, protocolID string) (*core.ContractListener, error) {
 	return em.getChainListenerCached(fmt.Sprintf("pid:%s", protocolID), func() (*core.ContractListener, error) {
-		return em.database.GetContractListenerByBackendID(ctx, em.namespace.LocalName, protocolID)
+		return em.database.GetContractListenerByBackendID(ctx, em.namespace.Name, protocolID)
 	})
 }
 
@@ -111,11 +111,11 @@ func (em *eventManager) BlockchainEvent(event *blockchain.EventWithSubscription)
 				log.L(ctx).Warnf("Event received from unknown subscription %s", event.Subscription)
 				return nil // no retry
 			}
-			if listener.Namespace != em.namespace.LocalName {
+			if listener.Namespace != em.namespace.Name {
 				log.L(em.ctx).Debugf("Ignoring blockchain event from different namespace '%s'", listener.Namespace)
 				return nil
 			}
-			listener.Namespace = em.namespace.LocalName
+			listener.Namespace = em.namespace.Name
 
 			chainEvent := buildBlockchainEvent(listener.Namespace, listener.ID, &event.Event, &core.BlockchainTransactionRef{
 				BlockchainID: event.BlockchainTXID,
diff --git a/internal/events/dx_callbacks.go b/internal/events/dx_callbacks.go
index 65bdd477d..c58a1fd80 100644
--- a/internal/events/dx_callbacks.go
+++ b/internal/events/dx_callbacks.go
@@ -81,11 +81,11 @@ func (em *eventManager) privateBatchReceived(peerID string, batch *core.Batch, w
 		log.L(em.ctx).Errorf("Ignoring private batch from non-multiparty network!")
 		return "", nil
 	}
-	if batch.Namespace != em.namespace.RemoteName {
+	if batch.Namespace != em.namespace.NetworkName {
 		log.L(em.ctx).Debugf("Ignoring private batch from different namespace '%s'", batch.Namespace)
 		return "", nil
 	}
-	batch.Namespace = em.namespace.LocalName
+	batch.Namespace = em.namespace.Name
 
 	// Retry for persistence errors (not validation errors)
 	err = em.retry.Do(em.ctx, "private batch received", func(attempt int) (bool, error) {
@@ -157,7 +157,7 @@ func (em *eventManager) markUnpinnedMessagesConfirmed(ctx context.Context, batch
 		Set("state", core.MessageStateConfirmed).
 		Set("confirmed", fftypes.Now())
 
-	if err := em.database.UpdateMessages(ctx, em.namespace.LocalName, filter, update); err != nil {
+	if err := em.database.UpdateMessages(ctx, em.namespace.Name, filter, update); err != nil {
 		return err
 	}
 
@@ -212,7 +212,7 @@ func (em *eventManager) privateBlobReceived(dx dataexchange.Plugin, event dataex
 		event.Ack() // Still confirm the event
 		return
 	}
-	if br.Namespace != em.namespace.RemoteName {
+	if br.Namespace != em.namespace.NetworkName {
 		log.L(em.ctx).Debugf("Ignoring blob from different namespace '%s'", br.Namespace)
 		event.Ack() // Still confirm the event
 		return
diff --git a/internal/events/event_manager.go b/internal/events/event_manager.go
index 6d3b84fa6..bc16523f8 100644
--- a/internal/events/event_manager.go
+++ b/internal/events/event_manager.go
@@ -90,7 +90,7 @@ type EventManager interface {
 
 type eventManager struct {
 	ctx                   context.Context
-	namespace             core.NamespaceRef
+	namespace             *core.Namespace
 	enricher              *eventEnricher
 	database              database.Plugin
 	txHelper              txcommon.Helper
@@ -116,7 +116,7 @@ type eventManager struct {
 	multiparty            multiparty.Manager // optional
 }
 
-func NewEventManager(ctx context.Context, ns core.NamespaceRef, di database.Plugin, bi blockchain.Plugin, im identity.Manager, dh definitions.Handler, dm data.Manager, ds definitions.Sender, bm broadcast.Manager, pm privatemessaging.Manager, am assets.Manager, sd shareddownload.Manager, mm metrics.Manager, om operations.Manager, txHelper txcommon.Helper, transports map[string]events.Plugin, mp multiparty.Manager) (EventManager, error) {
+func NewEventManager(ctx context.Context, ns *core.Namespace, di database.Plugin, bi blockchain.Plugin, im identity.Manager, dh definitions.Handler, dm data.Manager, ds definitions.Sender, bm broadcast.Manager, pm privatemessaging.Manager, am assets.Manager, sd shareddownload.Manager, mm metrics.Manager, om operations.Manager, txHelper txcommon.Helper, transports map[string]events.Plugin, mp multiparty.Manager) (EventManager, error) {
 	if di == nil || im == nil || dh == nil || dm == nil || om == nil || ds == nil || am == nil {
 		return nil, i18n.NewError(ctx, coremsgs.MsgInitializationNilDepError, "EventManager")
 	}
@@ -151,14 +151,14 @@ func NewEventManager(ctx context.Context, ns core.NamespaceRef, di database.Plug
 	ie, _ := eifactory.GetPlugin(ctx, system.SystemEventsTransport)
 	em.internalEvents = ie.(*system.Events)
 	if bi != nil {
-		em.aggregator = newAggregator(ctx, ns.LocalName, di, bi, pm, dh, im, dm, newPinNotifier, mm)
+		em.aggregator = newAggregator(ctx, ns.Name, di, bi, pm, dh, im, dm, newPinNotifier, mm)
 		em.blobReceiver = newBlobReceiver(ctx, em.aggregator)
 	}
 
-	em.enricher = newEventEnricher(ns.LocalName, di, dm, om, txHelper)
+	em.enricher = newEventEnricher(ns.Name, di, dm, om, txHelper)
 
 	var err error
-	if em.subManager, err = newSubscriptionManager(ctx, ns.LocalName, em.enricher, di, dm, newEventNotifier, bm, pm, txHelper, transports); err != nil {
+	if em.subManager, err = newSubscriptionManager(ctx, ns.Name, em.enricher, di, dm, newEventNotifier, bm, pm, txHelper, transports); err != nil {
 		return nil, err
 	}
 
@@ -242,7 +242,7 @@ func (em *eventManager) CreateUpdateDurableSubscription(ctx context.Context, sub
 	} else {
 		// We lock in the starting sequence at creation time, rather than when the first dispatcher
 		// starts, as that's a more obvious behavior for users
-		sequence, err := calcFirstOffset(ctx, em.namespace.LocalName, em.database, subDef.Options.FirstEvent)
+		sequence, err := calcFirstOffset(ctx, em.namespace.Name, em.database, subDef.Options.FirstEvent)
 		if err != nil {
 			return err
 		}
@@ -256,7 +256,7 @@ func (em *eventManager) CreateUpdateDurableSubscription(ctx context.Context, sub
 
 func (em *eventManager) DeleteDurableSubscription(ctx context.Context, subDef *core.Subscription) (err error) {
 	// The event in the database for the deletion of the susbscription, will asynchronously update the submanager
-	return em.database.DeleteSubscriptionByID(ctx, em.namespace.LocalName, subDef.ID)
+	return em.database.DeleteSubscriptionByID(ctx, em.namespace.Name, subDef.ID)
 }
 
 func (em *eventManager) AddSystemEventListener(ns string, el system.EventListener) error {
diff --git a/internal/events/network_action.go b/internal/events/network_action.go
index 296391c9a..8055692f8 100644
--- a/internal/events/network_action.go
+++ b/internal/events/network_action.go
@@ -56,7 +56,7 @@ func (em *eventManager) BlockchainNetworkAction(action string, location *fftypes
 		}
 
 		if err == nil {
-			chainEvent := buildBlockchainEvent(em.namespace.LocalName, nil, event, &core.BlockchainTransactionRef{
+			chainEvent := buildBlockchainEvent(em.namespace.Name, nil, event, &core.BlockchainTransactionRef{
 				BlockchainID: event.BlockchainTXID,
 			})
 			err = em.maybePersistBlockchainEvent(em.ctx, chainEvent, nil)
diff --git a/internal/events/persist_batch.go b/internal/events/persist_batch.go
index 8697fb869..4972cdf3d 100644
--- a/internal/events/persist_batch.go
+++ b/internal/events/persist_batch.go
@@ -99,7 +99,7 @@ func (em *eventManager) validateAndPersistBatchContent(ctx context.Context, batc
 		if valid, err = em.checkAndInitiateBlobDownloads(ctx, batch, i, data); !valid || err != nil {
 			return false, err
 		}
-		data.Namespace = em.namespace.LocalName
+		data.Namespace = em.namespace.Name
 		dataByID[*data.ID] = data
 	}
 
@@ -108,7 +108,7 @@ func (em *eventManager) validateAndPersistBatchContent(ctx context.Context, batc
 		if valid = em.validateBatchMessage(ctx, batch, i, msg); !valid {
 			return false, nil
 		}
-		msg.LocalNamespace = em.namespace.LocalName
+		msg.LocalNamespace = em.namespace.Name
 	}
 
 	// We require that the batch contains exactly the set of data that is in the messages - no more or less.
diff --git a/internal/events/ss_callbacks.go b/internal/events/ss_callbacks.go
index e1ee63f85..6f558a2eb 100644
--- a/internal/events/ss_callbacks.go
+++ b/internal/events/ss_callbacks.go
@@ -44,11 +44,11 @@ func (em *eventManager) SharedStorageBatchDownloaded(ss sharedstorage.Plugin, pa
 	}
 	l.Infof("Shared storage batch downloaded from %s '%s' id=%s (len=%d)", ss.Name(), payloadRef, batch.ID, len(data))
 
-	if batch.Namespace != em.namespace.RemoteName {
+	if batch.Namespace != em.namespace.NetworkName {
 		log.L(em.ctx).Debugf("Ignoring shared storage batch from different namespace '%s'", batch.Namespace)
 		return nil, nil // This is not retryable. skip this batch
 	}
-	batch.Namespace = em.namespace.LocalName
+	batch.Namespace = em.namespace.Name
 
 	err = em.retry.Do(em.ctx, "persist batch", func(attempt int) (bool, error) {
 		err := em.database.RunAsGroup(em.ctx, func(ctx context.Context) error {
diff --git a/internal/events/token_pool_created.go b/internal/events/token_pool_created.go
index 7ea1b31f2..6d08a71cb 100644
--- a/internal/events/token_pool_created.go
+++ b/internal/events/token_pool_created.go
@@ -83,7 +83,7 @@ func (em *eventManager) findTXOperation(ctx context.Context, tx *fftypes.UUID, o
 		fb.Eq("tx", tx),
 		fb.Eq("type", opType),
 	)
-	if operations, _, err := em.database.GetOperations(ctx, em.namespace.LocalName, filter); err != nil {
+	if operations, _, err := em.database.GetOperations(ctx, em.namespace.Name, filter); err != nil {
 		return nil, err
 	} else if len(operations) > 0 {
 		return operations[0], nil
@@ -92,7 +92,7 @@ func (em *eventManager) findTXOperation(ctx context.Context, tx *fftypes.UUID, o
 }
 
 func (em *eventManager) shouldConfirm(ctx context.Context, pool *tokens.TokenPool) (existingPool *core.TokenPool, err error) {
-	if existingPool, err = em.database.GetTokenPoolByLocator(ctx, em.namespace.LocalName, pool.Connector, pool.PoolLocator); err != nil || existingPool == nil {
+	if existingPool, err = em.database.GetTokenPoolByLocator(ctx, em.namespace.Name, pool.Connector, pool.PoolLocator); err != nil || existingPool == nil {
 		return existingPool, err
 	}
 	if err = addPoolDetailsFromPlugin(existingPool, pool); err != nil {
diff --git a/internal/events/tokens_approved.go b/internal/events/tokens_approved.go
index c1b99a490..9c46500af 100644
--- a/internal/events/tokens_approved.go
+++ b/internal/events/tokens_approved.go
@@ -43,7 +43,7 @@ func (em *eventManager) loadApprovalID(ctx context.Context, tx *fftypes.UUID, ap
 		fb.Eq("tx", tx),
 		fb.Eq("type", core.OpTypeTokenApproval),
 	)
-	operations, _, err := em.database.GetOperations(ctx, em.namespace.LocalName, filter)
+	operations, _, err := em.database.GetOperations(ctx, em.namespace.Name, filter)
 	if err != nil {
 		return nil, err
 	}
@@ -55,7 +55,7 @@ func (em *eventManager) loadApprovalID(ctx context.Context, tx *fftypes.UUID, ap
 			log.L(ctx).Warnf("Failed to read operation inputs for token approval '%s': %s", approval.Subject, err)
 		} else if input != nil && input.Connector == approval.Connector && input.Pool.Equals(approval.Pool) {
 			// Check if the LocalID has already been used
-			if existing, err := em.database.GetTokenApprovalByID(ctx, em.namespace.LocalName, input.LocalID); err != nil {
+			if existing, err := em.database.GetTokenApprovalByID(ctx, em.namespace.Name, input.LocalID); err != nil {
 				return nil, err
 			} else if existing == nil {
 				// Everything matches - use the LocalID that was assigned up-front when the operation was submitted
@@ -70,7 +70,7 @@ func (em *eventManager) loadApprovalID(ctx context.Context, tx *fftypes.UUID, ap
 func (em *eventManager) persistTokenApproval(ctx context.Context, approval *tokens.TokenApproval) (valid bool, err error) {
 	// Check that this is from a known pool
 	// TODO: should cache this lookup for efficiency
-	pool, err := em.database.GetTokenPoolByLocator(ctx, em.namespace.LocalName, approval.Connector, approval.PoolLocator)
+	pool, err := em.database.GetTokenPoolByLocator(ctx, em.namespace.Name, approval.Connector, approval.PoolLocator)
 	if err != nil {
 		return false, err
 	}
@@ -82,7 +82,7 @@ func (em *eventManager) persistTokenApproval(ctx context.Context, approval *toke
 	approval.Pool = pool.ID
 
 	// Check that approval has not already been recorded
-	if existing, err := em.database.GetTokenApprovalByProtocolID(ctx, em.namespace.LocalName, approval.Connector, approval.ProtocolID); err != nil {
+	if existing, err := em.database.GetTokenApprovalByProtocolID(ctx, em.namespace.Name, approval.Connector, approval.ProtocolID); err != nil {
 		return false, err
 	} else if existing != nil {
 		log.L(ctx).Warnf("Token approval '%s' has already been recorded - ignoring", approval.ProtocolID)
diff --git a/internal/events/tokens_transferred.go b/internal/events/tokens_transferred.go
index cdd833a23..2c8891a98 100644
--- a/internal/events/tokens_transferred.go
+++ b/internal/events/tokens_transferred.go
@@ -43,7 +43,7 @@ func (em *eventManager) loadTransferID(ctx context.Context, tx *fftypes.UUID, tr
 		fb.Eq("tx", tx),
 		fb.Eq("type", core.OpTypeTokenTransfer),
 	)
-	operations, _, err := em.database.GetOperations(ctx, em.namespace.LocalName, filter)
+	operations, _, err := em.database.GetOperations(ctx, em.namespace.Name, filter)
 	if err != nil {
 		return nil, err
 	}
@@ -55,7 +55,7 @@ func (em *eventManager) loadTransferID(ctx context.Context, tx *fftypes.UUID, tr
 			log.L(ctx).Warnf("Failed to read operation inputs for token transfer '%s': %s", transfer.ProtocolID, err)
 		} else if input != nil && input.Connector == transfer.Connector && input.Pool.Equals(transfer.Pool) {
 			// Check if the LocalID has already been used
-			if existing, err := em.database.GetTokenTransferByID(ctx, em.namespace.LocalName, input.LocalID); err != nil {
+			if existing, err := em.database.GetTokenTransferByID(ctx, em.namespace.Name, input.LocalID); err != nil {
 				return nil, err
 			} else if existing == nil {
 				// Everything matches - use the LocalID that was assigned up-front when the operation was submitted
@@ -70,7 +70,7 @@ func (em *eventManager) loadTransferID(ctx context.Context, tx *fftypes.UUID, tr
 func (em *eventManager) persistTokenTransfer(ctx context.Context, transfer *tokens.TokenTransfer) (valid bool, err error) {
 	// Check that this is from a known pool
 	// TODO: should cache this lookup for efficiency
-	pool, err := em.database.GetTokenPoolByLocator(ctx, em.namespace.LocalName, transfer.Connector, transfer.PoolLocator)
+	pool, err := em.database.GetTokenPoolByLocator(ctx, em.namespace.Name, transfer.Connector, transfer.PoolLocator)
 	if err != nil {
 		return false, err
 	}
@@ -82,7 +82,7 @@ func (em *eventManager) persistTokenTransfer(ctx context.Context, transfer *toke
 	transfer.Pool = pool.ID
 
 	// Check that transfer has not already been recorded
-	if existing, err := em.database.GetTokenTransferByProtocolID(ctx, em.namespace.LocalName, transfer.Connector, transfer.ProtocolID); err != nil {
+	if existing, err := em.database.GetTokenTransferByProtocolID(ctx, em.namespace.Name, transfer.Connector, transfer.ProtocolID); err != nil {
 		return false, err
 	} else if existing != nil {
 		log.L(ctx).Warnf("Token transfer '%s' has already been recorded - ignoring", transfer.ProtocolID)
@@ -138,7 +138,7 @@ func (em *eventManager) TokensTransferred(ti tokens.Plugin, transfer *tokens.Tok
 			}
 
 			if transfer.Message != nil {
-				msg, err := em.database.GetMessageByID(ctx, em.namespace.LocalName, transfer.Message)
+				msg, err := em.database.GetMessageByID(ctx, em.namespace.Name, transfer.Message)
 				switch {
 				case err != nil:
 					return err
diff --git a/internal/multiparty/manager.go b/internal/multiparty/manager.go
index 79ef86069..b2868e537 100644
--- a/internal/multiparty/manager.go
+++ b/internal/multiparty/manager.go
@@ -160,7 +160,7 @@ func (mm *multipartyManager) configureContractCommon(ctx context.Context, migrat
 		}
 	}
 
-	subID, err := mm.blockchain.AddFireflySubscription(ctx, mm.namespace.Ref(), location, firstEvent)
+	subID, err := mm.blockchain.AddFireflySubscription(ctx, mm.namespace, location, firstEvent)
 	if err == nil {
 		active.Location = location
 		active.FirstEvent = firstEvent
@@ -175,7 +175,7 @@ func (mm *multipartyManager) resolveFireFlyContract(ctx context.Context, contrac
 	if len(mm.config.Contracts) > 0 || contractIndex > 0 {
 		if contractIndex >= len(mm.config.Contracts) {
 			return nil, "", i18n.NewError(ctx, coremsgs.MsgInvalidFireFlyContractIndex,
-				fmt.Sprintf("%s.multiparty.contracts[%d]", mm.namespace.LocalName, contractIndex))
+				fmt.Sprintf("%s.multiparty.contracts[%d]", mm.namespace.Name, contractIndex))
 		}
 		active := mm.config.Contracts[contractIndex]
 		location = active.Location
@@ -221,7 +221,7 @@ func (mm *multipartyManager) SubmitNetworkAction(ctx context.Context, signingKey
 
 	op := core.NewOperation(
 		mm.blockchain,
-		mm.namespace.LocalName,
+		mm.namespace.Name,
 		txid,
 		core.OpTypeBlockchainNetworkAction)
 	addNetworkActionInputs(op, action.Type, signingKey)
@@ -236,7 +236,7 @@ func (mm *multipartyManager) SubmitNetworkAction(ctx context.Context, signingKey
 func (mm *multipartyManager) SubmitBatchPin(ctx context.Context, batch *core.BatchPersisted, contexts []*fftypes.Bytes32, payloadRef string) error {
 	op := core.NewOperation(
 		mm.blockchain,
-		mm.namespace.LocalName,
+		mm.namespace.Name,
 		batch.TX.ID,
 		core.OpTypeBlockchainPinBatch)
 	addBatchPinInputs(op, batch.ID, contexts, payloadRef)
diff --git a/internal/multiparty/operations.go b/internal/multiparty/operations.go
index 638c653d5..6f3dd5ca4 100644
--- a/internal/multiparty/operations.go
+++ b/internal/multiparty/operations.go
@@ -86,7 +86,7 @@ func (mm *multipartyManager) PrepareOperation(ctx context.Context, op *core.Oper
 		if err != nil {
 			return nil, err
 		}
-		batch, err := mm.database.GetBatchByID(ctx, mm.namespace.LocalName, batchID)
+		batch, err := mm.database.GetBatchByID(ctx, mm.namespace.Name, batchID)
 		if err != nil {
 			return nil, err
 		} else if batch == nil {
diff --git a/internal/namespace/config.go b/internal/namespace/config.go
index c8d57abcd..ddb272f75 100644
--- a/internal/namespace/config.go
+++ b/internal/namespace/config.go
@@ -36,13 +36,13 @@ var (
 func InitConfig(withDefaults bool) {
 	namespacePredefined.AddKnownKey(coreconfig.NamespaceName)
 	namespacePredefined.AddKnownKey(coreconfig.NamespaceDescription)
-	namespacePredefined.AddKnownKey(coreconfig.NamespaceRemoteName)
 	namespacePredefined.AddKnownKey(coreconfig.NamespacePlugins)
 	namespacePredefined.AddKnownKey(coreconfig.NamespaceDefaultKey)
 	namespacePredefined.AddKnownKey(coreconfig.NamespaceAssetKeyNormalization)
 
 	multipartyConf := namespacePredefined.SubSection(coreconfig.NamespaceMultiparty)
 	multipartyConf.AddKnownKey(coreconfig.NamespaceMultipartyEnabled)
+	multipartyConf.AddKnownKey(coreconfig.NamespaceMultipartyNetworkNamespace)
 	multipartyConf.AddKnownKey(coreconfig.NamespaceMultipartyOrgName)
 	multipartyConf.AddKnownKey(coreconfig.NamespaceMultipartyOrgDescription)
 	multipartyConf.AddKnownKey(coreconfig.NamespaceMultipartyOrgKey)
diff --git a/internal/namespace/manager.go b/internal/namespace/manager.go
index a995ff306..6fea9725e 100644
--- a/internal/namespace/manager.go
+++ b/internal/namespace/manager.go
@@ -107,11 +107,11 @@ type namespaceManager struct {
 		events        map[string]eventsPlugin
 		auth          map[string]authPlugin
 	}
-	metricsEnabled   bool
-	metrics          metrics.Manager
-	adminEvents      spievents.Manager
-	utOrchestrator   orchestrator.Orchestrator
-	tokenRemoteNames map[string]string
+	metricsEnabled      bool
+	metrics             metrics.Manager
+	adminEvents         spievents.Manager
+	utOrchestrator      orchestrator.Orchestrator
+	tokenBroadcastNames map[string]string
 }
 
 type blockchainPlugin struct {
@@ -168,9 +168,9 @@ func stringSlicesEqual(a, b []string) bool {
 
 func NewNamespaceManager(withDefaults bool) Manager {
 	nm := &namespaceManager{
-		namespaces:       make(map[string]*namespace),
-		metricsEnabled:   config.GetBool(coreconfig.MetricsEnabled),
-		tokenRemoteNames: make(map[string]string),
+		namespaces:          make(map[string]*namespace),
+		metricsEnabled:      config.GetBool(coreconfig.MetricsEnabled),
+		tokenBroadcastNames: make(map[string]string),
 	}
 
 	InitConfig(withDefaults)
@@ -228,7 +228,7 @@ func (nm *namespaceManager) Init(ctx context.Context, cancelCtx context.CancelFu
 		if multiparty {
 			version = fmt.Sprintf("%d", ns.Namespace.Contracts.Active.Info.Version)
 		}
-		log.L(ctx).Infof("Initialized namespace '%s' multiparty=%s version=%s", ns.LocalName, strconv.FormatBool(multiparty), version)
+		log.L(ctx).Infof("Initialized namespace '%s' multiparty=%s version=%s", ns.Name, strconv.FormatBool(multiparty), version)
 		if multiparty {
 			contract := nm.findV1Contract(ns)
 			if contract != nil {
@@ -238,7 +238,7 @@ func (nm *namespaceManager) Init(ctx context.Context, cancelCtx context.CancelFu
 				} else if !stringSlicesEqual(v1Namespace.plugins, ns.plugins) ||
 					v1Contract.Location.String() != contract.Location.String() ||
 					v1Contract.FirstEvent != contract.FirstEvent {
-					return i18n.NewError(ctx, coremsgs.MsgCannotInitLegacyNS, core.LegacySystemNamespace, v1Namespace.LocalName, ns.LocalName)
+					return i18n.NewError(ctx, coremsgs.MsgCannotInitLegacyNS, core.LegacySystemNamespace, v1Namespace.Name, ns.Name)
 				}
 			}
 		}
@@ -250,11 +250,11 @@ func (nm *namespaceManager) Init(ctx context.Context, cancelCtx context.CancelFu
 			config:    v1Namespace.config,
 			plugins:   v1Namespace.plugins,
 		}
-		systemNS.LocalName = core.LegacySystemNamespace
-		systemNS.RemoteName = core.LegacySystemNamespace
+		systemNS.Name = core.LegacySystemNamespace
+		systemNS.NetworkName = core.LegacySystemNamespace
 		nm.namespaces[core.LegacySystemNamespace] = systemNS
 		err = nm.initNamespace(systemNS)
-		log.L(ctx).Infof("Initialized namespace '%s' as a copy of '%s'", core.LegacySystemNamespace, v1Namespace.LocalName)
+		log.L(ctx).Infof("Initialized namespace '%s' as a copy of '%s'", core.LegacySystemNamespace, v1Namespace.Name)
 	}
 	return err
 }
@@ -274,23 +274,25 @@ func (nm *namespaceManager) findV1Contract(ns *namespace) *core.MultipartyContra
 func (nm *namespaceManager) initNamespace(ns *namespace) (err error) {
 	var plugins *orchestrator.Plugins
 	if ns.config.Multiparty.Enabled {
-		plugins, err = nm.validateMultiPartyConfig(nm.ctx, ns.LocalName, ns.plugins)
+		plugins, err = nm.validateMultiPartyConfig(nm.ctx, ns.Name, ns.plugins)
 	} else {
-		plugins, err = nm.validateNonMultipartyConfig(nm.ctx, ns.LocalName, ns.plugins)
+		plugins, err = nm.validateNonMultipartyConfig(nm.ctx, ns.Name, ns.plugins)
 	}
 	if err != nil {
 		return err
 	}
 
 	database := plugins.Database.Plugin
-	existing, err := database.GetNamespace(nm.ctx, ns.LocalName)
+	existing, err := database.GetNamespace(nm.ctx, ns.Name)
 	switch {
 	case err != nil:
 		return err
 	case existing != nil:
 		ns.Created = existing.Created
 		ns.Contracts = existing.Contracts
-		// TODO: should we check for discrepancies in the multiparty contract config?
+		if ns.NetworkName != existing.NetworkName {
+			log.L(nm.ctx).Warnf("Namespace '%s' - network name unexpectedly changed from '%s' to '%s'", ns.Name, existing.NetworkName, ns.NetworkName)
+		}
 	default:
 		ns.Created = fftypes.Now()
 		ns.Contracts = &core.MultipartyContracts{
@@ -314,8 +316,8 @@ func (nm *namespaceManager) initNamespace(ns *namespace) (err error) {
 		<-orCtx.Done()
 		nm.nsMux.Lock()
 		defer nm.nsMux.Unlock()
-		log.L(nm.ctx).Infof("Terminated namespace '%s'", ns.LocalName)
-		delete(nm.namespaces, ns.LocalName)
+		log.L(nm.ctx).Infof("Terminated namespace '%s'", ns.Name)
+		delete(nm.namespaces, ns.Name)
 	}()
 	return nil
 }
@@ -440,8 +442,8 @@ func (nm *namespaceManager) loadPlugins(ctx context.Context) (err error) {
 
 func (nm *namespaceManager) getTokensPlugins(ctx context.Context) (plugins map[string]tokensPlugin, err error) {
 	plugins = make(map[string]tokensPlugin)
-	// Remote names must be unique
-	remoteNames := make(map[string]bool)
+	// Broadcast names must be unique
+	broadcastNames := make(map[string]bool)
 
 	tokensConfigArraySize := tokensConfig.ArraySize()
 	for i := 0; i < tokensConfigArraySize; i++ {
@@ -450,16 +452,16 @@ func (nm *namespaceManager) getTokensPlugins(ctx context.Context) (plugins map[s
 		if err != nil {
 			return nil, err
 		}
-		remoteName := config.GetString(coreconfig.PluginRemoteName)
-		// If there is no remote name, use the plugin name
-		if remoteName == "" {
-			remoteName = name
+		broadcastName := config.GetString(coreconfig.PluginBroadcastName)
+		// If there is no broadcast name, use the plugin name
+		if broadcastName == "" {
+			broadcastName = name
 		}
-		if _, exists := remoteNames[remoteName]; exists {
-			return nil, i18n.NewError(ctx, coremsgs.MsgDuplicatePluginRemoteName, "tokens", remoteName)
+		if _, exists := broadcastNames[broadcastName]; exists {
+			return nil, i18n.NewError(ctx, coremsgs.MsgDuplicatePluginBroadcastName, "tokens", broadcastName)
 		}
-		remoteNames[remoteName] = true
-		nm.tokenRemoteNames[name] = remoteName
+		broadcastNames[broadcastName] = true
+		nm.tokenBroadcastNames[name] = broadcastName
 
 		plugin, err := tifactory.GetPlugin(ctx, pluginType)
 		if err != nil {
@@ -489,7 +491,7 @@ func (nm *namespaceManager) getTokensPlugins(ctx context.Context) (plugins map[s
 			if err = fftypes.ValidateFFNameField(ctx, name, "name"); err != nil {
 				return nil, err
 			}
-			nm.tokenRemoteNames[name] = name
+			nm.tokenBroadcastNames[name] = name
 
 			plugin, err := tifactory.GetPlugin(ctx, pluginType)
 			if err != nil {
@@ -784,13 +786,9 @@ func (nm *namespaceManager) loadNamespace(ctx context.Context, name string, inde
 	if err := fftypes.ValidateFFNameField(ctx, name, fmt.Sprintf("namespaces.predefined[%d].name", index)); err != nil {
 		return nil, err
 	}
-	remoteName := conf.GetString(coreconfig.NamespaceRemoteName)
-	if name == core.LegacySystemNamespace || remoteName == core.LegacySystemNamespace {
+	if name == core.LegacySystemNamespace {
 		return nil, i18n.NewError(ctx, coremsgs.MsgFFSystemReservedName, core.LegacySystemNamespace)
 	}
-	if remoteName == "" {
-		remoteName = name
-	}
 
 	keyNormalization := conf.GetString(coreconfig.NamespaceAssetKeyNormalization)
 	if keyNormalization == "" {
@@ -836,6 +834,17 @@ func (nm *namespaceManager) loadNamespace(ctx context.Context, name string, inde
 		multipartyEnabled = orgName != "" || orgKey != ""
 	}
 
+	var networkName string
+	if multipartyEnabled.(bool) {
+		networkName = multipartyConf.GetString(coreconfig.NamespaceMultipartyNetworkNamespace)
+		if networkName == core.LegacySystemNamespace {
+			return nil, i18n.NewError(ctx, coremsgs.MsgFFSystemReservedName, core.LegacySystemNamespace)
+		}
+		if networkName == "" {
+			networkName = name
+		}
+	}
+
 	// If no plugins are listed under this namespace, use all defined plugins by default
 	pluginsRaw := conf.Get(coreconfig.NamespacePlugins)
 	plugins := conf.GetStringSlice(coreconfig.NamespacePlugins)
@@ -866,9 +875,9 @@ func (nm *namespaceManager) loadNamespace(ctx context.Context, name string, inde
 	}
 
 	config := orchestrator.Config{
-		DefaultKey:       conf.GetString(coreconfig.NamespaceDefaultKey),
-		TokenRemoteNames: nm.tokenRemoteNames,
-		KeyNormalization: keyNormalization,
+		DefaultKey:          conf.GetString(coreconfig.NamespaceDefaultKey),
+		TokenBroadcastNames: nm.tokenBroadcastNames,
+		KeyNormalization:    keyNormalization,
 	}
 	if multipartyEnabled.(bool) {
 		contractsConf := multipartyConf.SubArray(coreconfig.NamespaceMultipartyContract)
@@ -901,8 +910,8 @@ func (nm *namespaceManager) loadNamespace(ctx context.Context, name string, inde
 
 	return &namespace{
 		Namespace: core.Namespace{
-			LocalName:   name,
-			RemoteName:  remoteName,
+			Name:        name,
+			NetworkName: networkName,
 			Description: conf.GetString(coreconfig.NamespaceDescription),
 		},
 		config:  config,
diff --git a/internal/orchestrator/chart.go b/internal/orchestrator/chart.go
index 802672177..fd1cc388f 100644
--- a/internal/orchestrator/chart.go
+++ b/internal/orchestrator/chart.go
@@ -49,7 +49,7 @@ func (or *orchestrator) GetChartHistogram(ctx context.Context, startTime int64,
 
 	intervals := or.getHistogramIntervals(startTime, endTime, buckets)
 
-	histogram, err := or.database().GetChartHistogram(ctx, or.namespace.LocalName, intervals, collection)
+	histogram, err := or.database().GetChartHistogram(ctx, or.namespace.Name, intervals, collection)
 	if err != nil {
 		return nil, err
 	}
diff --git a/internal/orchestrator/data_query.go b/internal/orchestrator/data_query.go
index 05c2a9145..ea67f42e5 100644
--- a/internal/orchestrator/data_query.go
+++ b/internal/orchestrator/data_query.go
@@ -48,7 +48,7 @@ func (or *orchestrator) GetTransactionOperations(ctx context.Context, id string)
 	filter := fb.And(
 		fb.Eq("tx", u),
 	)
-	return or.database().GetOperations(ctx, or.namespace.LocalName, filter)
+	return or.database().GetOperations(ctx, or.namespace.Name, filter)
 }
 
 func (or *orchestrator) getMessageByID(ctx context.Context, id string) (*core.Message, error) {
@@ -56,7 +56,7 @@ func (or *orchestrator) getMessageByID(ctx context.Context, id string) (*core.Me
 	if err != nil {
 		return nil, err
 	}
-	msg, err := or.database().GetMessageByID(ctx, or.namespace.LocalName, u)
+	msg, err := or.database().GetMessageByID(ctx, or.namespace.Name, u)
 	if err == nil && msg == nil {
 		return nil, i18n.NewError(ctx, coremsgs.Msg404NotFound)
 	}
@@ -93,7 +93,7 @@ func (or *orchestrator) GetBatchByID(ctx context.Context, id string) (*core.Batc
 	if err != nil {
 		return nil, err
 	}
-	return or.database().GetBatchByID(ctx, or.namespace.LocalName, u)
+	return or.database().GetBatchByID(ctx, or.namespace.Name, u)
 }
 
 func (or *orchestrator) GetDataByID(ctx context.Context, id string) (*core.Data, error) {
@@ -101,7 +101,7 @@ func (or *orchestrator) GetDataByID(ctx context.Context, id string) (*core.Data,
 	if err != nil {
 		return nil, err
 	}
-	return or.database().GetDataByID(ctx, or.namespace.LocalName, u, true)
+	return or.database().GetDataByID(ctx, or.namespace.Name, u, true)
 }
 
 func (or *orchestrator) GetDatatypeByID(ctx context.Context, id string) (*core.Datatype, error) {
@@ -109,14 +109,14 @@ func (or *orchestrator) GetDatatypeByID(ctx context.Context, id string) (*core.D
 	if err != nil {
 		return nil, err
 	}
-	return or.database().GetDatatypeByID(ctx, or.namespace.LocalName, u)
+	return or.database().GetDatatypeByID(ctx, or.namespace.Name, u)
 }
 
 func (or *orchestrator) GetDatatypeByName(ctx context.Context, name, version string) (*core.Datatype, error) {
 	if err := fftypes.ValidateFFNameFieldNoUUID(ctx, name, "name"); err != nil {
 		return nil, err
 	}
-	return or.database().GetDatatypeByName(ctx, or.namespace.LocalName, name, version)
+	return or.database().GetDatatypeByName(ctx, or.namespace.Name, name, version)
 }
 
 func (or *orchestrator) GetOperationByID(ctx context.Context, id string) (*core.Operation, error) {
@@ -132,19 +132,19 @@ func (or *orchestrator) GetEventByID(ctx context.Context, id string) (*core.Even
 	if err != nil {
 		return nil, err
 	}
-	return or.database().GetEventByID(ctx, or.namespace.LocalName, u)
+	return or.database().GetEventByID(ctx, or.namespace.Name, u)
 }
 
 func (or *orchestrator) GetTransactions(ctx context.Context, filter database.AndFilter) ([]*core.Transaction, *database.FilterResult, error) {
-	return or.database().GetTransactions(ctx, or.namespace.LocalName, filter)
+	return or.database().GetTransactions(ctx, or.namespace.Name, filter)
 }
 
 func (or *orchestrator) GetMessages(ctx context.Context, filter database.AndFilter) ([]*core.Message, *database.FilterResult, error) {
-	return or.database().GetMessages(ctx, or.namespace.LocalName, filter)
+	return or.database().GetMessages(ctx, or.namespace.Name, filter)
 }
 
 func (or *orchestrator) GetMessagesWithData(ctx context.Context, filter database.AndFilter) ([]*core.MessageInOut, *database.FilterResult, error) {
-	msgs, fr, err := or.database().GetMessages(ctx, or.namespace.LocalName, filter)
+	msgs, fr, err := or.database().GetMessages(ctx, or.namespace.Name, filter)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -176,7 +176,7 @@ func (or *orchestrator) getMessageTransactionID(ctx context.Context, id string)
 		if msg.BatchID == nil {
 			return nil, i18n.NewError(ctx, coremsgs.MsgBatchNotSet)
 		}
-		batch, err := or.database().GetBatchByID(ctx, or.namespace.LocalName, msg.BatchID)
+		batch, err := or.database().GetBatchByID(ctx, or.namespace.Name, msg.BatchID)
 		if err != nil {
 			return nil, err
 		}
@@ -215,15 +215,15 @@ func (or *orchestrator) GetMessageEvents(ctx context.Context, id string, filter
 	}
 	filter = filter.Condition(filter.Builder().In("reference", referencedIDs))
 	// Execute the filter
-	return or.database().GetEvents(ctx, or.namespace.LocalName, filter)
+	return or.database().GetEvents(ctx, or.namespace.Name, filter)
 }
 
 func (or *orchestrator) GetBatches(ctx context.Context, filter database.AndFilter) ([]*core.BatchPersisted, *database.FilterResult, error) {
-	return or.database().GetBatches(ctx, or.namespace.LocalName, filter)
+	return or.database().GetBatches(ctx, or.namespace.Name, filter)
 }
 
 func (or *orchestrator) GetData(ctx context.Context, filter database.AndFilter) (core.DataArray, *database.FilterResult, error) {
-	return or.database().GetData(ctx, or.namespace.LocalName, filter)
+	return or.database().GetData(ctx, or.namespace.Name, filter)
 }
 
 func (or *orchestrator) GetMessagesForData(ctx context.Context, id string, filter database.AndFilter) ([]*core.Message, *database.FilterResult, error) {
@@ -231,19 +231,19 @@ func (or *orchestrator) GetMessagesForData(ctx context.Context, id string, filte
 	if err != nil {
 		return nil, nil, err
 	}
-	return or.database().GetMessagesForData(ctx, or.namespace.LocalName, u, filter)
+	return or.database().GetMessagesForData(ctx, or.namespace.Name, u, filter)
 }
 
 func (or *orchestrator) GetDatatypes(ctx context.Context, filter database.AndFilter) ([]*core.Datatype, *database.FilterResult, error) {
-	return or.database().GetDatatypes(ctx, or.namespace.LocalName, filter)
+	return or.database().GetDatatypes(ctx, or.namespace.Name, filter)
 }
 
 func (or *orchestrator) GetOperations(ctx context.Context, filter database.AndFilter) ([]*core.Operation, *database.FilterResult, error) {
-	return or.database().GetOperations(ctx, or.namespace.LocalName, filter)
+	return or.database().GetOperations(ctx, or.namespace.Name, filter)
 }
 
 func (or *orchestrator) GetEvents(ctx context.Context, filter database.AndFilter) ([]*core.Event, *database.FilterResult, error) {
-	return or.database().GetEvents(ctx, or.namespace.LocalName, filter)
+	return or.database().GetEvents(ctx, or.namespace.Name, filter)
 }
 
 func (or *orchestrator) GetBlockchainEventByID(ctx context.Context, id string) (*core.BlockchainEvent, error) {
@@ -255,7 +255,7 @@ func (or *orchestrator) GetBlockchainEventByID(ctx context.Context, id string) (
 }
 
 func (or *orchestrator) GetBlockchainEvents(ctx context.Context, filter database.AndFilter) ([]*core.BlockchainEvent, *database.FilterResult, error) {
-	return or.database().GetBlockchainEvents(ctx, or.namespace.LocalName, filter)
+	return or.database().GetBlockchainEvents(ctx, or.namespace.Name, filter)
 }
 
 func (or *orchestrator) GetTransactionBlockchainEvents(ctx context.Context, id string) ([]*core.BlockchainEvent, *database.FilterResult, error) {
@@ -264,15 +264,15 @@ func (or *orchestrator) GetTransactionBlockchainEvents(ctx context.Context, id s
 		return nil, nil, err
 	}
 	fb := database.BlockchainEventQueryFactory.NewFilter(ctx)
-	return or.database().GetBlockchainEvents(ctx, or.namespace.LocalName, fb.And(fb.Eq("tx.id", u)))
+	return or.database().GetBlockchainEvents(ctx, or.namespace.Name, fb.And(fb.Eq("tx.id", u)))
 }
 
 func (or *orchestrator) GetPins(ctx context.Context, filter database.AndFilter) ([]*core.Pin, *database.FilterResult, error) {
-	return or.database().GetPins(ctx, or.namespace.LocalName, filter)
+	return or.database().GetPins(ctx, or.namespace.Name, filter)
 }
 
 func (or *orchestrator) GetEventsWithReferences(ctx context.Context, filter database.AndFilter) ([]*core.EnrichedEvent, *database.FilterResult, error) {
-	events, fr, err := or.database().GetEvents(ctx, or.namespace.LocalName, filter)
+	events, fr, err := or.database().GetEvents(ctx, or.namespace.Name, filter)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/internal/orchestrator/orchestrator.go b/internal/orchestrator/orchestrator.go
index c6c1c42ff..97c3e8371 100644
--- a/internal/orchestrator/orchestrator.go
+++ b/internal/orchestrator/orchestrator.go
@@ -169,10 +169,10 @@ type Plugins struct {
 }
 
 type Config struct {
-	DefaultKey       string
-	KeyNormalization string
-	Multiparty       multiparty.Config
-	TokenRemoteNames map[string]string
+	DefaultKey          string
+	KeyNormalization    string
+	Multiparty          multiparty.Config
+	TokenBroadcastNames map[string]string
 }
 
 type orchestrator struct {
@@ -213,9 +213,9 @@ func NewOrchestrator(ns *core.Namespace, config Config, plugins *Plugins, metric
 }
 
 func (or *orchestrator) Init(ctx context.Context, cancelCtx context.CancelFunc) (err error) {
-	namespaceLog := or.namespace.LocalName
-	if or.namespace.RemoteName != or.namespace.LocalName {
-		namespaceLog += "->" + or.namespace.RemoteName
+	namespaceLog := or.namespace.Name
+	if or.namespace.NetworkName != "" && or.namespace.NetworkName != or.namespace.Name {
+		namespaceLog += "->" + or.namespace.NetworkName
 	}
 	or.ctx, or.cancelCtx = context.WithCancel(log.WithLogField(ctx, "ns", namespaceLog))
 	err = or.initComponents(or.ctx)
@@ -351,38 +351,38 @@ func (or *orchestrator) MultiParty() multiparty.Manager {
 }
 
 func (or *orchestrator) initHandlers(ctx context.Context) (err error) {
-	or.plugins.Database.Plugin.SetHandler(or.namespace.LocalName, or)
+	or.plugins.Database.Plugin.SetHandler(or.namespace.Name, or)
 
 	if or.plugins.Blockchain.Plugin != nil {
-		or.plugins.Blockchain.Plugin.SetHandler(or.namespace.LocalName, or.events)
-		or.plugins.Blockchain.Plugin.SetOperationHandler(or.namespace.LocalName, &or.bc)
+		or.plugins.Blockchain.Plugin.SetHandler(or.namespace.Name, or.events)
+		or.plugins.Blockchain.Plugin.SetOperationHandler(or.namespace.Name, &or.bc)
 	}
 
 	if or.plugins.SharedStorage.Plugin != nil {
-		or.plugins.SharedStorage.Plugin.SetHandler(or.namespace.LocalName, &or.bc)
+		or.plugins.SharedStorage.Plugin.SetHandler(or.namespace.Name, &or.bc)
 	}
 
 	if or.plugins.DataExchange.Plugin != nil {
 		fb := database.IdentityQueryFactory.NewFilter(ctx)
-		nodes, _, err := or.database().GetIdentities(ctx, or.namespace.LocalName, fb.And(
+		nodes, _, err := or.database().GetIdentities(ctx, or.namespace.Name, fb.And(
 			fb.Eq("type", core.IdentityTypeNode),
 		))
 		if err != nil {
 			return err
 		}
 		for _, node := range nodes {
-			err = or.plugins.DataExchange.Plugin.AddNode(ctx, or.namespace.RemoteName, node.Name, node.Profile)
+			err = or.plugins.DataExchange.Plugin.AddNode(ctx, or.namespace.NetworkName, node.Name, node.Profile)
 			if err != nil {
 				return err
 			}
 		}
-		or.plugins.DataExchange.Plugin.SetHandler(or.namespace.RemoteName, or.config.Multiparty.Node.Name, or.events)
-		or.plugins.DataExchange.Plugin.SetOperationHandler(or.namespace.LocalName, &or.bc)
+		or.plugins.DataExchange.Plugin.SetHandler(or.namespace.NetworkName, or.config.Multiparty.Node.Name, or.events)
+		or.plugins.DataExchange.Plugin.SetOperationHandler(or.namespace.Name, &or.bc)
 	}
 
 	for _, token := range or.plugins.Tokens {
-		token.Plugin.SetHandler(or.namespace.LocalName, or.events)
-		token.Plugin.SetOperationHandler(or.namespace.LocalName, &or.bc)
+		token.Plugin.SetHandler(or.namespace.Name, or.events)
+		token.Plugin.SetOperationHandler(or.namespace.Name, &or.bc)
 	}
 
 	return nil
@@ -391,11 +391,11 @@ func (or *orchestrator) initHandlers(ctx context.Context) (err error) {
 func (or *orchestrator) initManagers(ctx context.Context) (err error) {
 
 	if or.txHelper == nil {
-		or.txHelper = txcommon.NewTransactionHelper(or.namespace.LocalName, or.database(), or.data)
+		or.txHelper = txcommon.NewTransactionHelper(or.namespace.Name, or.database(), or.data)
 	}
 
 	if or.operations == nil {
-		if or.operations, err = operations.NewOperationsManager(ctx, or.namespace.LocalName, or.database(), or.txHelper); err != nil {
+		if or.operations, err = operations.NewOperationsManager(ctx, or.namespace.Name, or.database(), or.txHelper); err != nil {
 			return err
 		}
 	}
@@ -413,36 +413,36 @@ func (or *orchestrator) initManagers(ctx context.Context) (err error) {
 	}
 
 	if or.identity == nil {
-		or.identity, err = identity.NewIdentityManager(ctx, or.namespace.LocalName, or.config.DefaultKey, or.database(), or.blockchain(), or.multiparty)
+		or.identity, err = identity.NewIdentityManager(ctx, or.namespace.Name, or.config.DefaultKey, or.database(), or.blockchain(), or.multiparty)
 		if err != nil {
 			return err
 		}
 	}
 
-	or.syncasync = syncasync.NewSyncAsyncBridge(ctx, or.namespace.LocalName, or.database(), or.data, or.operations)
+	or.syncasync = syncasync.NewSyncAsyncBridge(ctx, or.namespace.Name, or.database(), or.data, or.operations)
 
 	if or.config.Multiparty.Enabled {
 		if or.batch == nil {
-			or.batch, err = batch.NewBatchManager(ctx, or.namespace.LocalName, or.database(), or.data, or.identity, or.txHelper)
+			or.batch, err = batch.NewBatchManager(ctx, or.namespace.Name, or.database(), or.data, or.identity, or.txHelper)
 			if err != nil {
 				return err
 			}
 		}
 
 		if or.messaging == nil {
-			if or.messaging, err = privatemessaging.NewPrivateMessaging(ctx, or.namespace.Ref(), or.database(), or.dataexchange(), or.blockchain(), or.identity, or.batch, or.data, or.syncasync, or.multiparty, or.metrics, or.operations); err != nil {
+			if or.messaging, err = privatemessaging.NewPrivateMessaging(ctx, or.namespace, or.database(), or.dataexchange(), or.blockchain(), or.identity, or.batch, or.data, or.syncasync, or.multiparty, or.metrics, or.operations); err != nil {
 				return err
 			}
 		}
 
 		if or.broadcast == nil {
-			if or.broadcast, err = broadcast.NewBroadcastManager(ctx, or.namespace.Ref(), or.database(), or.blockchain(), or.dataexchange(), or.sharedstorage(), or.identity, or.data, or.batch, or.syncasync, or.multiparty, or.metrics, or.operations); err != nil {
+			if or.broadcast, err = broadcast.NewBroadcastManager(ctx, or.namespace, or.database(), or.blockchain(), or.dataexchange(), or.sharedstorage(), or.identity, or.data, or.batch, or.syncasync, or.multiparty, or.metrics, or.operations); err != nil {
 				return err
 			}
 		}
 
 		if or.sharedDownload == nil {
-			or.sharedDownload, err = shareddownload.NewDownloadManager(ctx, or.namespace.Ref(), or.database(), or.sharedstorage(), or.dataexchange(), or.operations, &or.bc)
+			or.sharedDownload, err = shareddownload.NewDownloadManager(ctx, or.namespace, or.database(), or.sharedstorage(), or.dataexchange(), or.operations, &or.bc)
 			if err != nil {
 				return err
 			}
@@ -450,7 +450,7 @@ func (or *orchestrator) initManagers(ctx context.Context) (err error) {
 	}
 
 	if or.assets == nil {
-		or.assets, err = assets.NewAssetManager(ctx, or.namespace.LocalName, or.config.KeyNormalization, or.database(), or.tokens(), or.identity, or.syncasync, or.broadcast, or.messaging, or.metrics, or.operations, or.txHelper)
+		or.assets, err = assets.NewAssetManager(ctx, or.namespace.Name, or.config.KeyNormalization, or.database(), or.tokens(), or.identity, or.syncasync, or.broadcast, or.messaging, or.metrics, or.operations, or.txHelper)
 		if err != nil {
 			return err
 		}
@@ -458,7 +458,7 @@ func (or *orchestrator) initManagers(ctx context.Context) (err error) {
 
 	if or.blockchain() != nil {
 		if or.contracts == nil {
-			or.contracts, err = contracts.NewContractManager(ctx, or.namespace.LocalName, or.database(), or.blockchain(), or.identity, or.operations, or.txHelper, or.syncasync)
+			or.contracts, err = contracts.NewContractManager(ctx, or.namespace.Name, or.database(), or.blockchain(), or.identity, or.operations, or.txHelper, or.syncasync)
 			if err != nil {
 				return err
 			}
@@ -466,14 +466,14 @@ func (or *orchestrator) initManagers(ctx context.Context) (err error) {
 	}
 
 	if or.defsender == nil {
-		or.defsender, or.defhandler, err = definitions.NewDefinitionSender(ctx, or.namespace.LocalName, or.namespace.RemoteName, or.config.Multiparty.Enabled, or.database(), or.blockchain(), or.dataexchange(), or.broadcast, or.identity, or.data, or.assets, or.contracts, or.config.TokenRemoteNames)
+		or.defsender, or.defhandler, err = definitions.NewDefinitionSender(ctx, or.namespace, or.config.Multiparty.Enabled, or.database(), or.blockchain(), or.dataexchange(), or.broadcast, or.identity, or.data, or.assets, or.contracts, or.config.TokenBroadcastNames)
 		if err != nil {
 			return err
 		}
 	}
 
 	if or.networkmap == nil {
-		or.networkmap, err = networkmap.NewNetworkMap(ctx, or.namespace.LocalName, or.database(), or.dataexchange(), or.defsender, or.identity, or.syncasync, or.multiparty)
+		or.networkmap, err = networkmap.NewNetworkMap(ctx, or.namespace.Name, or.database(), or.dataexchange(), or.defsender, or.identity, or.syncasync, or.multiparty)
 		if err != nil {
 			return err
 		}
@@ -484,7 +484,7 @@ func (or *orchestrator) initManagers(ctx context.Context) (err error) {
 
 func (or *orchestrator) initComponents(ctx context.Context) (err error) {
 	if or.data == nil {
-		or.data, err = data.NewDataManager(ctx, or.namespace.Ref(), or.database(), or.dataexchange())
+		or.data, err = data.NewDataManager(ctx, or.namespace, or.database(), or.dataexchange())
 		if err != nil {
 			return err
 		}
@@ -495,7 +495,7 @@ func (or *orchestrator) initComponents(ctx context.Context) (err error) {
 	}
 
 	if or.events == nil {
-		or.events, err = events.NewEventManager(ctx, or.namespace.Ref(), or.database(), or.blockchain(), or.identity, or.defhandler, or.data, or.defsender, or.broadcast, or.messaging, or.assets, or.sharedDownload, or.metrics, or.operations, or.txHelper, or.plugins.Events, or.multiparty)
+		or.events, err = events.NewEventManager(ctx, or.namespace, or.database(), or.blockchain(), or.identity, or.defhandler, or.data, or.defsender, or.broadcast, or.messaging, or.assets, or.sharedDownload, or.metrics, or.operations, or.txHelper, or.plugins.Events, or.multiparty)
 		if err != nil {
 			return err
 		}
@@ -518,7 +518,7 @@ func (or *orchestrator) SubmitNetworkAction(ctx context.Context, action *core.Ne
 }
 
 func (or *orchestrator) Authorize(ctx context.Context, authReq *fftypes.AuthReq) error {
-	authReq.Namespace = or.namespace.LocalName
+	authReq.Namespace = or.namespace.Name
 	if or.plugins.Auth.Plugin != nil {
 		return or.plugins.Auth.Plugin.Authorize(ctx, authReq)
 	}
diff --git a/internal/orchestrator/persistence_events.go b/internal/orchestrator/persistence_events.go
index eb82a6c84..fb95b6fa4 100644
--- a/internal/orchestrator/persistence_events.go
+++ b/internal/orchestrator/persistence_events.go
@@ -24,7 +24,7 @@ import (
 )
 
 func (or *orchestrator) OrderedUUIDCollectionNSEvent(resType database.OrderedUUIDCollectionNS, eventType core.ChangeEventType, ns string, id *fftypes.UUID, sequence int64) {
-	if ns != or.namespace.LocalName {
+	if ns != or.namespace.Name {
 		log.L(or.ctx).Debugf("Ignoring database event from different namespace '%s'", ns)
 		return
 	}
@@ -37,7 +37,7 @@ func (or *orchestrator) OrderedUUIDCollectionNSEvent(resType database.OrderedUUI
 }
 
 func (or *orchestrator) OrderedCollectionNSEvent(resType database.OrderedCollectionNS, eventType core.ChangeEventType, ns string, sequence int64) {
-	if ns != or.namespace.LocalName {
+	if ns != or.namespace.Name {
 		log.L(or.ctx).Debugf("Ignoring database event from different namespace '%s'", ns)
 		return
 	}
@@ -47,7 +47,7 @@ func (or *orchestrator) OrderedCollectionNSEvent(resType database.OrderedCollect
 }
 
 func (or *orchestrator) UUIDCollectionNSEvent(resType database.UUIDCollectionNS, eventType core.ChangeEventType, ns string, id *fftypes.UUID) {
-	if ns != or.namespace.LocalName {
+	if ns != or.namespace.Name {
 		log.L(or.ctx).Debugf("Ignoring database event from different namespace '%s'", ns)
 		return
 	}
diff --git a/internal/orchestrator/subscriptions.go b/internal/orchestrator/subscriptions.go
index a9131eae1..742de7a80 100644
--- a/internal/orchestrator/subscriptions.go
+++ b/internal/orchestrator/subscriptions.go
@@ -38,7 +38,7 @@ func (or *orchestrator) CreateUpdateSubscription(ctx context.Context, subDef *co
 func (or *orchestrator) createUpdateSubscription(ctx context.Context, subDef *core.Subscription, mustNew bool) (*core.Subscription, error) {
 	subDef.ID = fftypes.NewUUID()
 	subDef.Created = fftypes.Now()
-	subDef.Namespace = or.namespace.LocalName
+	subDef.Namespace = or.namespace.Name
 	subDef.Ephemeral = false
 	if err := fftypes.ValidateFFNameFieldNoUUID(ctx, subDef.Name, "name"); err != nil {
 		return nil, err
@@ -55,7 +55,7 @@ func (or *orchestrator) DeleteSubscription(ctx context.Context, id string) error
 	if err != nil {
 		return err
 	}
-	sub, err := or.database().GetSubscriptionByID(ctx, or.namespace.LocalName, u)
+	sub, err := or.database().GetSubscriptionByID(ctx, or.namespace.Name, u)
 	if err != nil {
 		return err
 	}
@@ -66,7 +66,7 @@ func (or *orchestrator) DeleteSubscription(ctx context.Context, id string) error
 }
 
 func (or *orchestrator) GetSubscriptions(ctx context.Context, filter database.AndFilter) ([]*core.Subscription, *database.FilterResult, error) {
-	return or.database().GetSubscriptions(ctx, or.namespace.LocalName, filter)
+	return or.database().GetSubscriptions(ctx, or.namespace.Name, filter)
 }
 
 func (or *orchestrator) GetSubscriptionByID(ctx context.Context, id string) (*core.Subscription, error) {
@@ -74,5 +74,5 @@ func (or *orchestrator) GetSubscriptionByID(ctx context.Context, id string) (*co
 	if err != nil {
 		return nil, err
 	}
-	return or.database().GetSubscriptionByID(ctx, or.namespace.LocalName, u)
+	return or.database().GetSubscriptionByID(ctx, or.namespace.Name, u)
 }
diff --git a/internal/orchestrator/txn_status.go b/internal/orchestrator/txn_status.go
index 3a602aff0..8e2412f06 100644
--- a/internal/orchestrator/txn_status.go
+++ b/internal/orchestrator/txn_status.go
@@ -101,7 +101,7 @@ func (or *orchestrator) GetTransactionStatus(ctx context.Context, id string) (*c
 			updateStatus(result, core.OpStatusPending)
 		}
 		f := database.BatchQueryFactory.NewFilter(ctx)
-		switch batches, _, err := or.database().GetBatches(ctx, or.namespace.LocalName, f.Eq("tx.id", id)); {
+		switch batches, _, err := or.database().GetBatches(ctx, or.namespace.Name, f.Eq("tx.id", id)); {
 		case err != nil:
 			return nil, err
 		case len(batches) == 0:
@@ -120,7 +120,7 @@ func (or *orchestrator) GetTransactionStatus(ctx context.Context, id string) (*c
 	case core.TransactionTypeTokenPool:
 		// Note: no assumptions about blockchain events here (may or may not contain one)
 		f := database.TokenPoolQueryFactory.NewFilter(ctx)
-		switch pools, _, err := or.database().GetTokenPools(ctx, or.namespace.LocalName, f.Eq("tx.id", id)); {
+		switch pools, _, err := or.database().GetTokenPools(ctx, or.namespace.Name, f.Eq("tx.id", id)); {
 		case err != nil:
 			return nil, err
 		case len(pools) == 0:
@@ -150,7 +150,7 @@ func (or *orchestrator) GetTransactionStatus(ctx context.Context, id string) (*c
 			updateStatus(result, core.OpStatusPending)
 		}
 		f := database.TokenTransferQueryFactory.NewFilter(ctx)
-		switch transfers, _, err := or.database().GetTokenTransfers(ctx, or.namespace.LocalName, f.Eq("tx.id", id)); {
+		switch transfers, _, err := or.database().GetTokenTransfers(ctx, or.namespace.Name, f.Eq("tx.id", id)); {
 		case err != nil:
 			return nil, err
 		case len(transfers) == 0:
@@ -172,7 +172,7 @@ func (or *orchestrator) GetTransactionStatus(ctx context.Context, id string) (*c
 			updateStatus(result, core.OpStatusPending)
 		}
 		f := database.TokenApprovalQueryFactory.NewFilter(ctx)
-		switch approvals, _, err := or.database().GetTokenApprovals(ctx, or.namespace.LocalName, f.Eq("tx.id", id)); {
+		switch approvals, _, err := or.database().GetTokenApprovals(ctx, or.namespace.Name, f.Eq("tx.id", id)); {
 		case err != nil:
 			return nil, err
 		case len(approvals) == 0:
diff --git a/internal/privatemessaging/groupmanager.go b/internal/privatemessaging/groupmanager.go
index eab422a45..66db90f4e 100644
--- a/internal/privatemessaging/groupmanager.go
+++ b/internal/privatemessaging/groupmanager.go
@@ -40,7 +40,7 @@ type GroupManager interface {
 }
 
 type groupManager struct {
-	namespace     core.NamespaceRef
+	namespace     *core.Namespace
 	database      database.Plugin
 	identity      identity.Manager
 	data          data.Manager
@@ -63,7 +63,7 @@ func (gm *groupManager) EnsureLocalGroup(ctx context.Context, group *core.Group)
 	// the group via the blockchain.
 	// So this method checks if a group exists, and if it doesn't inserts it.
 	// We do assume the other side has sent the batch init of the group (rather than generating a second one)
-	if g, err := gm.database.GetGroupByHash(ctx, gm.namespace.LocalName, group.Hash); err != nil {
+	if g, err := gm.database.GetGroupByHash(ctx, gm.namespace.Name, group.Hash); err != nil {
 		return false, err
 	} else if g != nil {
 		// The group already exists
@@ -88,7 +88,7 @@ func (gm *groupManager) groupInit(ctx context.Context, signer *core.SignerRef, g
 	data := &core.Data{
 		Validator: core.ValidatorTypeSystemDefinition,
 		ID:        fftypes.NewUUID(),
-		Namespace: gm.namespace.LocalName, // must go in the same ordering context as the message
+		Namespace: gm.namespace.Name, // must go in the same ordering context as the message
 		Created:   fftypes.Now(),
 	}
 	b, err := json.Marshal(&group)
@@ -102,15 +102,15 @@ func (gm *groupManager) groupInit(ctx context.Context, signer *core.SignerRef, g
 	if err != nil {
 		return i18n.WrapError(ctx, err, coremsgs.MsgSerializationFailed)
 	}
-	group.LocalNamespace = gm.namespace.LocalName
+	group.LocalNamespace = gm.namespace.Name
 
 	// Create a private send message referring to the data
 	msg := &core.Message{
 		State:          core.MessageStateReady,
-		LocalNamespace: gm.namespace.LocalName,
+		LocalNamespace: gm.namespace.Name, // Must go into the same ordering context as the message itself
 		Header: core.MessageHeader{
 			Group:     group.Hash,
-			Namespace: gm.namespace.RemoteName, // Must go into the same ordering context as the message itself
+			Namespace: gm.namespace.NetworkName,
 			Type:      core.MessageTypeGroupInit,
 			SignerRef: *signer,
 			Tag:       core.SystemTagDefineGroup,
@@ -150,11 +150,11 @@ func (gm *groupManager) GetGroupByID(ctx context.Context, hash string) (*core.Gr
 	if err != nil {
 		return nil, err
 	}
-	return gm.database.GetGroupByHash(ctx, gm.namespace.LocalName, h)
+	return gm.database.GetGroupByHash(ctx, gm.namespace.Name, h)
 }
 
 func (gm *groupManager) GetGroups(ctx context.Context, filter database.AndFilter) ([]*core.Group, *database.FilterResult, error) {
-	return gm.database.GetGroups(ctx, gm.namespace.LocalName, filter)
+	return gm.database.GetGroups(ctx, gm.namespace.Name, filter)
 }
 
 func (gm *groupManager) getGroupNodes(ctx context.Context, groupHash *fftypes.Bytes32, allowNil bool) (*core.Group, []*core.Identity, error) {
@@ -165,7 +165,7 @@ func (gm *groupManager) getGroupNodes(ctx context.Context, groupHash *fftypes.By
 		return ghe.group, ghe.nodes, nil
 	}
 
-	group, err := gm.database.GetGroupByHash(ctx, gm.namespace.LocalName, groupHash)
+	group, err := gm.database.GetGroupByHash(ctx, gm.namespace.Name, groupHash)
 	if err != nil || (allowNil && group == nil) {
 		return nil, nil, err
 	}
@@ -227,7 +227,7 @@ func (gm *groupManager) ResolveInitGroup(ctx context.Context, msg *core.Message)
 			return nil, nil
 		}
 		newGroup.Message = msg.Header.ID
-		newGroup.LocalNamespace = gm.namespace.LocalName
+		newGroup.LocalNamespace = gm.namespace.Name
 		err = gm.database.UpsertGroup(ctx, &newGroup, database.UpsertOptimizationNew /* we think we're first to create this */)
 		if err != nil {
 			return nil, err
@@ -236,7 +236,7 @@ func (gm *groupManager) ResolveInitGroup(ctx context.Context, msg *core.Message)
 	}
 
 	// Get the existing group
-	group, err := gm.database.GetGroupByHash(ctx, gm.namespace.LocalName, msg.Header.Group)
+	group, err := gm.database.GetGroupByHash(ctx, gm.namespace.Name, msg.Header.Group)
 	if err != nil {
 		return group, err
 	}
diff --git a/internal/privatemessaging/message.go b/internal/privatemessaging/message.go
index 28a2d9367..7213fdb99 100644
--- a/internal/privatemessaging/message.go
+++ b/internal/privatemessaging/message.go
@@ -98,8 +98,8 @@ func (s *messageSender) SendAndWait(ctx context.Context) error {
 func (s *messageSender) setDefaults() {
 	msg := s.msg.Message
 	msg.Header.ID = fftypes.NewUUID()
-	msg.Header.Namespace = s.mgr.namespace.RemoteName
-	msg.LocalNamespace = s.mgr.namespace.LocalName
+	msg.Header.Namespace = s.mgr.namespace.NetworkName
+	msg.LocalNamespace = s.mgr.namespace.Name
 	msg.State = core.MessageStateReady
 	if msg.Header.Type == "" {
 		msg.Header.Type = core.MessageTypePrivate
diff --git a/internal/privatemessaging/operations.go b/internal/privatemessaging/operations.go
index 670c60e0b..4db922ee7 100644
--- a/internal/privatemessaging/operations.go
+++ b/internal/privatemessaging/operations.go
@@ -102,13 +102,13 @@ func (pm *privateMessaging) PrepareOperation(ctx context.Context, op *core.Opera
 		} else if node == nil {
 			return nil, i18n.NewError(ctx, coremsgs.Msg404NotFound)
 		}
-		group, err := pm.database.GetGroupByHash(ctx, pm.namespace.LocalName, groupHash)
+		group, err := pm.database.GetGroupByHash(ctx, pm.namespace.Name, groupHash)
 		if err != nil {
 			return nil, err
 		} else if group == nil {
 			return nil, i18n.NewError(ctx, coremsgs.Msg404NotFound)
 		}
-		bp, err := pm.database.GetBatchByID(ctx, pm.namespace.LocalName, batchID)
+		bp, err := pm.database.GetBatchByID(ctx, pm.namespace.Name, batchID)
 		if err != nil {
 			return nil, err
 		} else if bp == nil {
diff --git a/internal/privatemessaging/privatemessaging.go b/internal/privatemessaging/privatemessaging.go
index f044dfeb8..678330063 100644
--- a/internal/privatemessaging/privatemessaging.go
+++ b/internal/privatemessaging/privatemessaging.go
@@ -61,7 +61,7 @@ type privateMessaging struct {
 	groupManager
 
 	ctx                   context.Context
-	namespace             core.NamespaceRef
+	namespace             *core.Namespace
 	database              database.Plugin
 	identity              identity.Manager
 	exchange              dataexchange.Plugin
@@ -82,7 +82,7 @@ type blobTransferTracker struct {
 	op       *core.PreparedOperation
 }
 
-func NewPrivateMessaging(ctx context.Context, ns core.NamespaceRef, di database.Plugin, dx dataexchange.Plugin, bi blockchain.Plugin, im identity.Manager, ba batch.Manager, dm data.Manager, sa syncasync.Bridge, mult multiparty.Manager, mm metrics.Manager, om operations.Manager) (Manager, error) {
+func NewPrivateMessaging(ctx context.Context, ns *core.Namespace, di database.Plugin, dx dataexchange.Plugin, bi blockchain.Plugin, im identity.Manager, ba batch.Manager, dm data.Manager, sa syncasync.Bridge, mult multiparty.Manager, mm metrics.Manager, om operations.Manager) (Manager, error) {
 	if di == nil || im == nil || dx == nil || bi == nil || ba == nil || dm == nil || mm == nil || om == nil || mult == nil {
 		return nil, i18n.NewError(ctx, coremsgs.MsgInitializationNilDepError, "PrivateMessaging")
 	}
@@ -172,7 +172,7 @@ func (pm *privateMessaging) dispatchUnpinnedBatch(ctx context.Context, state *ba
 
 func (pm *privateMessaging) dispatchBatchCommon(ctx context.Context, state *batch.DispatchState) error {
 	batch := state.Persisted.GenInflight(state.Messages, state.Data)
-	batch.Namespace = pm.namespace.RemoteName
+	batch.Namespace = pm.namespace.NetworkName
 	tw := &core.TransportWrapper{
 		Batch: batch,
 	}
@@ -214,7 +214,7 @@ func (pm *privateMessaging) prepareBlobTransfers(ctx context.Context, data core.
 
 				op := core.NewOperation(
 					pm.exchange,
-					pm.namespace.LocalName,
+					pm.namespace.Name,
 					txid,
 					core.OpTypeDataExchangeSendBlob)
 				addTransferBlobInputs(op, node.ID, blob.Hash)
@@ -290,7 +290,7 @@ func (pm *privateMessaging) sendData(ctx context.Context, tw *core.TransportWrap
 
 			op := core.NewOperation(
 				pm.exchange,
-				pm.namespace.LocalName,
+				pm.namespace.Name,
 				batch.Payload.TX.ID,
 				core.OpTypeDataExchangeSendBatch)
 			addBatchSendInputs(op, node.ID, batch.Group, batch.ID)
diff --git a/internal/privatemessaging/recipients.go b/internal/privatemessaging/recipients.go
index 633f70529..89d57d0f9 100644
--- a/internal/privatemessaging/recipients.go
+++ b/internal/privatemessaging/recipients.go
@@ -31,7 +31,7 @@ import (
 func (pm *privateMessaging) resolveRecipientList(ctx context.Context, in *core.MessageInOut) error {
 	if in.Header.Group != nil {
 		log.L(ctx).Debugf("Group '%s' specified for message", in.Header.Group)
-		group, err := pm.database.GetGroupByHash(ctx, pm.namespace.LocalName, in.Header.Group)
+		group, err := pm.database.GetGroupByHash(ctx, pm.namespace.Name, in.Header.Group)
 		if err != nil {
 			return err
 		}
@@ -66,7 +66,7 @@ func (pm *privateMessaging) getFirstNodeForOrg(ctx context.Context, identity *co
 			fb.Eq("parent", identity.ID),
 			fb.Eq("type", core.IdentityTypeNode),
 		)
-		nodes, _, err := pm.database.GetIdentities(ctx, pm.namespace.LocalName, filter)
+		nodes, _, err := pm.database.GetIdentities(ctx, pm.namespace.Name, filter)
 		if err != nil || len(nodes) == 0 {
 			return nil, err
 		}
diff --git a/internal/reference/reference.go b/internal/reference/reference.go
index 1171ab1e7..6ae060429 100644
--- a/internal/reference/reference.go
+++ b/internal/reference/reference.go
@@ -501,8 +501,8 @@ func GenerateObjectsReferenceMarkdown(ctx context.Context) (map[string][]byte, e
 		},
 
 		&core.Namespace{
-			LocalName:   "default",
-			RemoteName:  "default",
+			Name:        "default",
+			NetworkName: "default",
 			Description: "Default predefined namespace",
 			Created:     fftypes.UnixTime(1652664196),
 		},
diff --git a/internal/shareddownload/download_manager.go b/internal/shareddownload/download_manager.go
index 82b7db7ae..ee4ec4b12 100644
--- a/internal/shareddownload/download_manager.go
+++ b/internal/shareddownload/download_manager.go
@@ -51,7 +51,7 @@ type Manager interface {
 type downloadManager struct {
 	ctx                        context.Context
 	cancelFunc                 func()
-	namespace                  core.NamespaceRef
+	namespace                  *core.Namespace
 	database                   database.Plugin
 	sharedstorage              sharedstorage.Plugin // optional
 	dataexchange               dataexchange.Plugin
@@ -79,7 +79,7 @@ type Callbacks interface {
 	SharedStorageBlobDownloaded(hash fftypes.Bytes32, size int64, payloadRef string)
 }
 
-func NewDownloadManager(ctx context.Context, ns core.NamespaceRef, di database.Plugin, ss sharedstorage.Plugin, dx dataexchange.Plugin, om operations.Manager, cb Callbacks) (Manager, error) {
+func NewDownloadManager(ctx context.Context, ns *core.Namespace, di database.Plugin, ss sharedstorage.Plugin, dx dataexchange.Plugin, om operations.Manager, cb Callbacks) (Manager, error) {
 	if di == nil || dx == nil || ss == nil || cb == nil {
 		return nil, i18n.NewError(ctx, coremsgs.MsgInitializationNilDepError, "DownloadManager")
 	}
@@ -172,7 +172,7 @@ func (dm *downloadManager) recoverDownloads(startupTime *fftypes.FFTime) {
 			Sort("created").
 			Skip(page * pageSize).
 			Limit(pageSize)
-		pendingOps, _, err := dm.database.GetOperations(dm.ctx, dm.namespace.LocalName, filter)
+		pendingOps, _, err := dm.database.GetOperations(dm.ctx, dm.namespace.Name, filter)
 		if err != nil {
 			log.L(dm.ctx).Errorf("Error while recovering pending downloads (retries=%d): %s", errorAttempts, err)
 			errorAttempts++
@@ -222,13 +222,13 @@ func (dm *downloadManager) waitAndRetryDownload(work *downloadWork) {
 }
 
 func (dm *downloadManager) InitiateDownloadBatch(ctx context.Context, tx *fftypes.UUID, payloadRef string) error {
-	op := core.NewOperation(dm.sharedstorage, dm.namespace.LocalName, tx, core.OpTypeSharedStorageDownloadBatch)
+	op := core.NewOperation(dm.sharedstorage, dm.namespace.Name, tx, core.OpTypeSharedStorageDownloadBatch)
 	addDownloadBatchInputs(op, payloadRef)
 	return dm.createAndDispatchOp(ctx, op, opDownloadBatch(op, payloadRef))
 }
 
 func (dm *downloadManager) InitiateDownloadBlob(ctx context.Context, tx *fftypes.UUID, dataID *fftypes.UUID, payloadRef string) error {
-	op := core.NewOperation(dm.sharedstorage, dm.namespace.LocalName, tx, core.OpTypeSharedStorageDownloadBlob)
+	op := core.NewOperation(dm.sharedstorage, dm.namespace.Name, tx, core.OpTypeSharedStorageDownloadBlob)
 	addDownloadBlobInputs(op, dataID, payloadRef)
 	return dm.createAndDispatchOp(ctx, op, opDownloadBlob(op, dataID, payloadRef))
 }
diff --git a/internal/shareddownload/operations.go b/internal/shareddownload/operations.go
index cbcd310de..356a900bb 100644
--- a/internal/shareddownload/operations.go
+++ b/internal/shareddownload/operations.go
@@ -148,7 +148,7 @@ func (dm *downloadManager) downloadBlob(ctx context.Context, data downloadBlobDa
 	defer reader.Close()
 
 	// ... to data exchange
-	dxPayloadRef, hash, blobSize, err := dm.dataexchange.UploadBlob(ctx, dm.namespace.RemoteName, *data.DataID, reader)
+	dxPayloadRef, hash, blobSize, err := dm.dataexchange.UploadBlob(ctx, dm.namespace.NetworkName, *data.DataID, reader)
 	if err != nil {
 		return nil, false, i18n.WrapError(ctx, err, coremsgs.MsgDownloadSharedFailed, data.PayloadRef)
 	}
diff --git a/internal/tokens/tifactory/factory.go b/internal/tokens/tifactory/factory.go
index 099c6e027..089b9734d 100644
--- a/internal/tokens/tifactory/factory.go
+++ b/internal/tokens/tifactory/factory.go
@@ -34,7 +34,7 @@ var pluginsByName = map[string]func() tokens.Plugin{
 func InitConfig(config config.ArraySection) {
 	config.AddKnownKey(coreconfig.PluginConfigName)
 	config.AddKnownKey(coreconfig.PluginConfigType)
-	config.AddKnownKey(coreconfig.PluginRemoteName)
+	config.AddKnownKey(coreconfig.PluginBroadcastName)
 	for name, plugin := range pluginsByName {
 		plugin().InitConfig(config.SubSection(name))
 	}
diff --git a/mocks/blockchainmocks/plugin.go b/mocks/blockchainmocks/plugin.go
index 723f750ab..d13c8b9f8 100644
--- a/mocks/blockchainmocks/plugin.go
+++ b/mocks/blockchainmocks/plugin.go
@@ -37,18 +37,18 @@ func (_m *Plugin) AddContractListener(ctx context.Context, subscription *core.Co
 }
 
 // AddFireflySubscription provides a mock function with given fields: ctx, namespace, location, firstEvent
-func (_m *Plugin) AddFireflySubscription(ctx context.Context, namespace core.NamespaceRef, location *fftypes.JSONAny, firstEvent string) (string, error) {
+func (_m *Plugin) AddFireflySubscription(ctx context.Context, namespace *core.Namespace, location *fftypes.JSONAny, firstEvent string) (string, error) {
 	ret := _m.Called(ctx, namespace, location, firstEvent)
 
 	var r0 string
-	if rf, ok := ret.Get(0).(func(context.Context, core.NamespaceRef, *fftypes.JSONAny, string) string); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *core.Namespace, *fftypes.JSONAny, string) string); ok {
 		r0 = rf(ctx, namespace, location, firstEvent)
 	} else {
 		r0 = ret.Get(0).(string)
 	}
 
 	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, core.NamespaceRef, *fftypes.JSONAny, string) error); ok {
+	if rf, ok := ret.Get(1).(func(context.Context, *core.Namespace, *fftypes.JSONAny, string) error); ok {
 		r1 = rf(ctx, namespace, location, firstEvent)
 	} else {
 		r1 = ret.Error(1)
diff --git a/mocks/dataexchangemocks/plugin.go b/mocks/dataexchangemocks/plugin.go
index 725a72968..371892133 100644
--- a/mocks/dataexchangemocks/plugin.go
+++ b/mocks/dataexchangemocks/plugin.go
@@ -23,13 +23,13 @@ type Plugin struct {
 	mock.Mock
 }
 
-// AddNode provides a mock function with given fields: ctx, remoteNamespace, nodeName, peer
-func (_m *Plugin) AddNode(ctx context.Context, remoteNamespace string, nodeName string, peer fftypes.JSONObject) error {
-	ret := _m.Called(ctx, remoteNamespace, nodeName, peer)
+// AddNode provides a mock function with given fields: ctx, networkNamespace, nodeName, peer
+func (_m *Plugin) AddNode(ctx context.Context, networkNamespace string, nodeName string, peer fftypes.JSONObject) error {
+	ret := _m.Called(ctx, networkNamespace, nodeName, peer)
 
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, fftypes.JSONObject) error); ok {
-		r0 = rf(ctx, remoteNamespace, nodeName, peer)
+		r0 = rf(ctx, networkNamespace, nodeName, peer)
 	} else {
 		r0 = ret.Error(0)
 	}
@@ -160,9 +160,9 @@ func (_m *Plugin) SendMessage(ctx context.Context, nsOpID string, peer fftypes.J
 	return r0
 }
 
-// SetHandler provides a mock function with given fields: remoteNamespace, nodeName, handler
-func (_m *Plugin) SetHandler(remoteNamespace string, nodeName string, handler dataexchange.Callbacks) {
-	_m.Called(remoteNamespace, nodeName, handler)
+// SetHandler provides a mock function with given fields: networkNamespace, nodeName, handler
+func (_m *Plugin) SetHandler(networkNamespace string, nodeName string, handler dataexchange.Callbacks) {
+	_m.Called(networkNamespace, nodeName, handler)
 }
 
 // SetOperationHandler provides a mock function with given fields: namespace, handler
diff --git a/pkg/blockchain/plugin.go b/pkg/blockchain/plugin.go
index 7a897b1ef..40f8dca76 100644
--- a/pkg/blockchain/plugin.go
+++ b/pkg/blockchain/plugin.go
@@ -58,7 +58,7 @@ type Plugin interface {
 	NormalizeSigningKey(ctx context.Context, keyRef string) (string, error)
 
 	// SubmitBatchPin sequences a batch of message globally to all viewers of a given ledger
-	SubmitBatchPin(ctx context.Context, nsOpID, remtoeNamespace, signingKey string, batch *BatchPin, location *fftypes.JSONAny) error
+	SubmitBatchPin(ctx context.Context, nsOpID, networkNamespace, signingKey string, batch *BatchPin, location *fftypes.JSONAny) error
 
 	// SubmitNetworkAction writes a special "BatchPin" event which signals the plugin to take an action
 	SubmitNetworkAction(ctx context.Context, nsOpID, signingKey string, action core.NetworkActionType, location *fftypes.JSONAny) error
@@ -94,7 +94,7 @@ type Plugin interface {
 	GetAndConvertDeprecatedContractConfig(ctx context.Context) (location *fftypes.JSONAny, fromBlock string, err error)
 
 	// AddFireflySubscription creates a FireFly BatchPin subscription for the provided location
-	AddFireflySubscription(ctx context.Context, namespace core.NamespaceRef, location *fftypes.JSONAny, firstEvent string) (subID string, err error)
+	AddFireflySubscription(ctx context.Context, namespace *core.Namespace, location *fftypes.JSONAny, firstEvent string) (subID string, err error)
 
 	// RemoveFireFlySubscription removes the provided FireFly subscription
 	RemoveFireflySubscription(ctx context.Context, subID string)
diff --git a/pkg/core/namespace.go b/pkg/core/namespace.go
index fa8efb3f8..e307e92b1 100644
--- a/pkg/core/namespace.go
+++ b/pkg/core/namespace.go
@@ -28,8 +28,8 @@ import (
 // Namespace is an isolated set of named resources, to allow multiple applications to co-exist in the same network, with the same named objects.
 // Can be used for use case segregation, or multi-tenancy.
 type Namespace struct {
-	LocalName   string               `ffstruct:"Namespace" json:"name"`
-	RemoteName  string               `ffstruct:"Namespace" json:"remoteName"`
+	Name        string               `ffstruct:"Namespace" json:"name"`
+	NetworkName string               `ffstruct:"Namespace" json:"networkName"`
 	Description string               `ffstruct:"Namespace" json:"description"`
 	Created     *fftypes.FFTime      `ffstruct:"Namespace" json:"created" ffexcludeinput:"true"`
 	Contracts   *MultipartyContracts `ffstruct:"Namespace" json:"-"`
@@ -56,18 +56,6 @@ type MultipartyContractInfo struct {
 	Version      int    `ffstruct:"MultipartyContract" json:"version,omitempty"`
 }
 
-type NamespaceRef struct {
-	LocalName  string
-	RemoteName string
-}
-
-func (n *Namespace) Ref() NamespaceRef {
-	return NamespaceRef{
-		LocalName:  n.LocalName,
-		RemoteName: n.RemoteName,
-	}
-}
-
 // NetworkActionType is a type of action to perform
 type NetworkActionType = fftypes.FFEnum
 
diff --git a/pkg/dataexchange/plugin.go b/pkg/dataexchange/plugin.go
index 36e59d4f6..0751a3009 100644
--- a/pkg/dataexchange/plugin.go
+++ b/pkg/dataexchange/plugin.go
@@ -65,7 +65,7 @@ type Plugin interface {
 
 	// SetHandler registers a handler to receive callbacks
 	// Plugin will attempt (but is not guaranteed) to deliver events only for the given namespace and node
-	SetHandler(remoteNamespace, nodeName string, handler Callbacks)
+	SetHandler(networkNamespace, nodeName string, handler Callbacks)
 
 	// SetOperationHandler registers a handler to receive async operation status
 	// If namespace is set, plugin will attempt to deliver only events for that namespace
@@ -82,7 +82,7 @@ type Plugin interface {
 
 	// AddNode registers details on a node in the multiparty network
 	// This may be information loaded from the database at init, or received in flight while running
-	AddNode(ctx context.Context, remoteNamespace, nodeName string, peer fftypes.JSONObject) (err error)
+	AddNode(ctx context.Context, networkNamespace, nodeName string, peer fftypes.JSONObject) (err error)
 
 	// UploadBlob streams a blob to storage, and returns the hash to confirm the hash calculated in Core matches the hash calculated in the plugin
 	UploadBlob(ctx context.Context, ns string, id fftypes.UUID, content io.Reader) (payloadRef string, hash *fftypes.Bytes32, size int64, err error)


Test patch:
diff --git a/internal/blockchain/common/common_test.go b/internal/blockchain/common/common_test.go
index 38ae778d8..d1aee5572 100644
--- a/internal/blockchain/common/common_test.go
+++ b/internal/blockchain/common/common_test.go
@@ -201,8 +201,7 @@ func TestGetNamespaceFromSubName(t *testing.T) {
 }
 
 func TestSubscriptionsAddRemoveSubscription(t *testing.T) {
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
-
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	subs := NewFireflySubscriptions()
 
 	subs.AddSubscription(context.Background(), ns, 2, "sub1", nil)
@@ -212,8 +211,7 @@ func TestSubscriptionsAddRemoveSubscription(t *testing.T) {
 }
 
 func TestSubscriptionsAddRemoveSubscriptionV1(t *testing.T) {
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
-
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	subs := NewFireflySubscriptions()
 
 	subs.AddSubscription(context.Background(), ns, 1, "sub1", nil)
diff --git a/internal/blockchain/ethereum/ethereum_test.go b/internal/blockchain/ethereum/ethereum_test.go
index 6aeba046f..609c456b1 100644
--- a/internal/blockchain/ethereum/ethereum_test.go
+++ b/internal/blockchain/ethereum/ethereum_test.go
@@ -491,7 +491,7 @@ func TestInitAllExistingStreams(t *testing.T) {
 
 	err := e.Init(e.ctx, utConfig, e.metrics)
 	assert.NoError(t, err)
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err = e.AddFireflySubscription(e.ctx, ns, location, "oldest")
 	assert.NoError(t, err)
 
@@ -531,7 +531,7 @@ func TestInitAllExistingStreamsV1(t *testing.T) {
 
 	err := e.Init(e.ctx, utConfig, e.metrics)
 	assert.NoError(t, err)
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err = e.AddFireflySubscription(e.ctx, ns, location, "oldest")
 	assert.NoError(t, err)
 
@@ -571,7 +571,7 @@ func TestInitAllExistingStreamsOld(t *testing.T) {
 
 	err := e.Init(e.ctx, utConfig, e.metrics)
 	assert.NoError(t, err)
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err = e.AddFireflySubscription(e.ctx, ns, location, "oldest")
 	assert.NoError(t, err)
 
@@ -611,7 +611,7 @@ func TestInitAllExistingStreamsInvalidName(t *testing.T) {
 
 	err := e.Init(e.ctx, utConfig, e.metrics)
 	assert.NoError(t, err)
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err = e.AddFireflySubscription(e.ctx, ns, location, "oldest")
 	assert.Regexp(t, "FF10416", err)
 }
@@ -1026,7 +1026,7 @@ func TestHandleMessageBatchPinOK(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", nil,
 	)
 
@@ -1116,7 +1116,7 @@ func TestHandleMessageBatchPinMissingAddress(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", nil,
 	)
 
@@ -1134,7 +1134,7 @@ func TestHandleMessageBatchPinEmpty(t *testing.T) {
 	}
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", nil,
 	)
 
@@ -1158,7 +1158,7 @@ func TestHandleMessageBatchMissingData(t *testing.T) {
 	}
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", nil,
 	)
 
@@ -1184,7 +1184,7 @@ func TestHandleMessageBatchPinBadTransactionID(t *testing.T) {
 	}
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", nil,
 	)
 
@@ -1221,7 +1221,7 @@ func TestHandleMessageBatchPinBadIDentity(t *testing.T) {
 	}
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", nil,
 	)
 
@@ -1712,7 +1712,7 @@ func TestHandleMessageContractEventOldSubscription(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", nil,
 	)
 	e.streams = newTestStreamManager(e.client)
@@ -1788,7 +1788,7 @@ func TestHandleMessageContractEventErrorOldSubscription(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", nil,
 	)
 	e.streams = newTestStreamManager(e.client)
@@ -1851,7 +1851,7 @@ func TestHandleMessageContractEventWithNamespace(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", nil,
 	)
 	e.streams = newTestStreamManager(e.client)
@@ -1925,7 +1925,7 @@ func TestHandleMessageContractEventNoNamespaceHandlers(t *testing.T) {
 	e.SetHandler("ns2", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", nil,
 	)
 	e.streams = newTestStreamManager(e.client)
@@ -1976,7 +1976,7 @@ func TestHandleMessageContractEventSubNameError(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", nil,
 	)
 	e.streams = newTestStreamManager(e.client)
@@ -2024,7 +2024,7 @@ func TestHandleMessageContractEventError(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", nil,
 	)
 	e.streams = newTestStreamManager(e.client)
@@ -2663,7 +2663,7 @@ func TestHandleNetworkAction(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", nil,
 	)
 
@@ -2715,7 +2715,7 @@ func TestHandleNetworkActionFail(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", nil,
 	)
 
@@ -2970,7 +2970,7 @@ func TestAddSubBadLocation(t *testing.T) {
 		"bad": "bad",
 	}.String())
 
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err := e.AddFireflySubscription(e.ctx, ns, location, "oldest")
 	assert.Regexp(t, "FF10310", err)
 }
@@ -3008,7 +3008,7 @@ func TestAddAndRemoveFireflySubscription(t *testing.T) {
 		"address": "0x123",
 	}.String())
 
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	subID, err := e.AddFireflySubscription(e.ctx, ns, location, "newest")
 	assert.NoError(t, err)
 	assert.NotNil(t, e.subs.GetSubscription("sub1"))
@@ -3050,7 +3050,7 @@ func TestAddFireflySubscriptionV1(t *testing.T) {
 		"address": "0x123",
 	}.String())
 
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err = e.AddFireflySubscription(e.ctx, ns, location, "newest")
 	assert.NoError(t, err)
 	assert.NotNil(t, e.subs.GetSubscription("sub1"))
@@ -3087,7 +3087,7 @@ func TestAddFireflySubscriptionQuerySubsFail(t *testing.T) {
 		"address": "0x123",
 	}.String())
 
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err = e.AddFireflySubscription(e.ctx, ns, location, "oldest")
 	assert.Regexp(t, "FF10111", err)
 }
@@ -3124,7 +3124,7 @@ func TestAddFireflySubscriptionCreateError(t *testing.T) {
 		"address": "0x123",
 	}.String())
 
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err = e.AddFireflySubscription(e.ctx, ns, location, "oldest")
 	assert.Regexp(t, "FF10111", err)
 }
@@ -3160,7 +3160,7 @@ func TestAddFireflySubscriptionGetVersionError(t *testing.T) {
 		"address": "0x123",
 	}.String())
 
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err = e.AddFireflySubscription(e.ctx, ns, location, "oldest")
 	assert.Regexp(t, "FF10111", err)
 }
diff --git a/internal/blockchain/fabric/fabric_test.go b/internal/blockchain/fabric/fabric_test.go
index 1e88fda77..632498deb 100644
--- a/internal/blockchain/fabric/fabric_test.go
+++ b/internal/blockchain/fabric/fabric_test.go
@@ -247,7 +247,7 @@ func TestInitAllExistingStreams(t *testing.T) {
 
 	err := e.Init(e.ctx, utConfig, &metricsmocks.Manager{})
 	assert.NoError(t, err)
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err = e.AddFireflySubscription(e.ctx, ns, location, "oldest")
 	assert.NoError(t, err)
 
@@ -286,7 +286,7 @@ func TestInitAllExistingStreamsV1(t *testing.T) {
 
 	err := e.Init(e.ctx, utConfig, &metricsmocks.Manager{})
 	assert.NoError(t, err)
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err = e.AddFireflySubscription(e.ctx, ns, location, "oldest")
 	assert.NoError(t, err)
 
@@ -323,7 +323,7 @@ func TestAddFireflySubscriptionQuerySubsFail(t *testing.T) {
 
 	err := e.Init(e.ctx, utConfig, &metricsmocks.Manager{})
 	assert.NoError(t, err)
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err = e.AddFireflySubscription(e.ctx, ns, location, "newest")
 	assert.Regexp(t, "pop", err)
 }
@@ -359,7 +359,7 @@ func TestAddFireflySubscriptionGetVersionError(t *testing.T) {
 
 	err := e.Init(e.ctx, utConfig, &metricsmocks.Manager{})
 	assert.NoError(t, err)
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err = e.AddFireflySubscription(e.ctx, ns, location, "newest")
 	assert.Regexp(t, "pop", err)
 }
@@ -395,7 +395,7 @@ func TestAddAndRemoveFireflySubscriptionDeprecatedSubName(t *testing.T) {
 
 	err := e.Init(e.ctx, utConfig, &metricsmocks.Manager{})
 	assert.NoError(t, err)
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	subID, err := e.AddFireflySubscription(e.ctx, ns, location, "oldest")
 	assert.NoError(t, err)
 
@@ -438,7 +438,7 @@ func TestAddFireflySubscriptionInvalidSubName(t *testing.T) {
 
 	err := e.Init(e.ctx, utConfig, &metricsmocks.Manager{})
 	assert.NoError(t, err)
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err = e.AddFireflySubscription(e.ctx, ns, location, "oldest")
 	assert.Regexp(t, "FF10416", err)
 }
@@ -448,7 +448,7 @@ func TestAddFFSubscriptionBadLocation(t *testing.T) {
 	location := fftypes.JSONAnyPtr(fftypes.JSONObject{
 		"bad": "bad",
 	}.String())
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err := e.AddFireflySubscription(e.ctx, ns, location, "oldest")
 	assert.Regexp(t, "F10310", err)
 }
@@ -564,7 +564,7 @@ func TestSubQueryCreateError(t *testing.T) {
 
 	err := e.Init(e.ctx, utConfig, &metricsmocks.Manager{})
 	assert.NoError(t, err)
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err = e.AddFireflySubscription(e.ctx, ns, location, "oldest")
 	assert.Regexp(t, "FF10284.*pop", err)
 
@@ -605,7 +605,7 @@ func TestSubQueryCreate(t *testing.T) {
 
 	err := e.Init(e.ctx, utConfig, &metricsmocks.Manager{})
 	assert.NoError(t, err)
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err = e.AddFireflySubscription(e.ctx, ns, location, "oldest")
 	assert.NoError(t, err)
 
@@ -997,7 +997,7 @@ func TestHandleMessageBatchPinOK(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", "firefly",
 	)
 
@@ -1059,7 +1059,7 @@ func TestHandleMessageBatchPinMissingChaincodeID(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", "firefly",
 	)
 
@@ -1091,7 +1091,7 @@ func TestHandleMessageUnknownEventName(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", "firefly",
 	)
 
@@ -1112,7 +1112,7 @@ func TestHandleMessageBatchPinBadPayloadEncoding(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", "firefly",
 	)
 
@@ -1728,7 +1728,7 @@ func TestHandleMessageContractOldSubError(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", "firefly",
 	)
 
@@ -1809,7 +1809,7 @@ func TestHandleMessageContractGetSubError(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-b5b97a4e-a317-4053-6400-1474650efcb5", "firefly",
 	)
 
@@ -2187,7 +2187,7 @@ func TestHandleNetworkAction(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", "firefly",
 	)
 
@@ -2231,7 +2231,7 @@ func TestHandleNetworkActionFail(t *testing.T) {
 	e.SetHandler("ns1", em)
 	e.subs.AddSubscription(
 		context.Background(),
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		1, "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", "firefly",
 	)
 
diff --git a/internal/broadcast/manager_test.go b/internal/broadcast/manager_test.go
index 45ab6144f..8d9fb4820 100644
--- a/internal/broadcast/manager_test.go
+++ b/internal/broadcast/manager_test.go
@@ -76,7 +76,7 @@ func newTestBroadcastCommon(t *testing.T, metricsEnabled bool) (*broadcastManage
 	}
 
 	ctx, cancel := context.WithCancel(context.Background())
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	b, err := NewBroadcastManager(ctx, ns, mdi, mbi, mdx, mpi, mim, mdm, mba, msa, mmp, mmi, mom)
 	assert.NoError(t, err)
 	return b.(*broadcastManager), cancel
@@ -94,7 +94,7 @@ func newTestBroadcastWithMetrics(t *testing.T) (*broadcastManager, func()) {
 }
 
 func TestInitFail(t *testing.T) {
-	_, err := NewBroadcastManager(context.Background(), core.NamespaceRef{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
+	_, err := NewBroadcastManager(context.Background(), &core.Namespace{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
 	assert.Regexp(t, "FF10128", err)
 }
 
diff --git a/internal/data/data_manager_test.go b/internal/data/data_manager_test.go
index 549340133..1328f4e7d 100644
--- a/internal/data/data_manager_test.go
+++ b/internal/data/data_manager_test.go
@@ -41,7 +41,7 @@ func newTestDataManager(t *testing.T) (*dataManager, context.Context, func()) {
 		Concurrency: true,
 	})
 	mdx := &dataexchangemocks.Plugin{}
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	dm, err := NewDataManager(ctx, ns, mdi, mdx)
 	assert.NoError(t, err)
 	dm.Start()
@@ -213,7 +213,7 @@ func TestWriteNewMessageE2E(t *testing.T) {
 }
 
 func TestInitBadDeps(t *testing.T) {
-	_, err := NewDataManager(context.Background(), core.NamespaceRef{}, nil, nil)
+	_, err := NewDataManager(context.Background(), &core.Namespace{}, nil, nil)
 	assert.Regexp(t, "FF10128", err)
 }
 
diff --git a/internal/database/sqlcommon/namespace_sql_test.go b/internal/database/sqlcommon/namespace_sql_test.go
index e5b21b020..b0928537a 100644
--- a/internal/database/sqlcommon/namespace_sql_test.go
+++ b/internal/database/sqlcommon/namespace_sql_test.go
@@ -38,9 +38,9 @@ func TestNamespacesE2EWithDB(t *testing.T) {
 
 	// Create a new namespace entry
 	namespace := &core.Namespace{
-		LocalName:  "namespace1",
-		RemoteName: "default",
-		Created:    fftypes.Now(),
+		Name:        "namespace1",
+		NetworkName: "default",
+		Created:     fftypes.Now(),
 		Contracts: &core.MultipartyContracts{
 			Active: &core.MultipartyContract{
 				Index: 1,
@@ -52,7 +52,7 @@ func TestNamespacesE2EWithDB(t *testing.T) {
 	assert.NoError(t, err)
 
 	// Check we get the exact same namespace back
-	namespaceRead, err := s.GetNamespace(ctx, namespace.LocalName)
+	namespaceRead, err := s.GetNamespace(ctx, namespace.Name)
 	assert.NoError(t, err)
 	assert.NotNil(t, namespaceRead)
 	namespaceJson, _ := json.Marshal(&namespace)
@@ -62,7 +62,7 @@ func TestNamespacesE2EWithDB(t *testing.T) {
 	// Update the namespace (this is testing what's possible at the database layer,
 	// and does not account for the verification that happens at the higher level)
 	namespaceUpdated := &core.Namespace{
-		LocalName:   "namespace1",
+		Name:        "namespace1",
 		Description: "description1",
 		Created:     fftypes.Now(),
 	}
@@ -70,7 +70,7 @@ func TestNamespacesE2EWithDB(t *testing.T) {
 	assert.NoError(t, err)
 
 	// Check we get the exact same data back - note the removal of one of the namespace elements
-	namespaceRead, err = s.GetNamespace(ctx, namespace.LocalName)
+	namespaceRead, err = s.GetNamespace(ctx, namespace.Name)
 	assert.NoError(t, err)
 	namespaceJson, _ = json.Marshal(&namespaceUpdated)
 	namespaceReadJson, _ = json.Marshal(&namespaceRead)
@@ -92,7 +92,7 @@ func TestUpsertNamespaceFailSelect(t *testing.T) {
 	mock.ExpectBegin()
 	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
 	mock.ExpectRollback()
-	err := s.UpsertNamespace(context.Background(), &core.Namespace{LocalName: "name1"}, true)
+	err := s.UpsertNamespace(context.Background(), &core.Namespace{Name: "name1"}, true)
 	assert.Regexp(t, "FF10115", err)
 	assert.NoError(t, mock.ExpectationsWereMet())
 }
@@ -103,7 +103,7 @@ func TestUpsertNamespaceFailInsert(t *testing.T) {
 	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{}))
 	mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop"))
 	mock.ExpectRollback()
-	err := s.UpsertNamespace(context.Background(), &core.Namespace{LocalName: "name1"}, true)
+	err := s.UpsertNamespace(context.Background(), &core.Namespace{Name: "name1"}, true)
 	assert.Regexp(t, "FF10116", err)
 	assert.NoError(t, mock.ExpectationsWereMet())
 }
@@ -115,7 +115,7 @@ func TestUpsertNamespaceFailUpdate(t *testing.T) {
 		AddRow("name1"))
 	mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop"))
 	mock.ExpectRollback()
-	err := s.UpsertNamespace(context.Background(), &core.Namespace{LocalName: "name1"}, true)
+	err := s.UpsertNamespace(context.Background(), &core.Namespace{Name: "name1"}, true)
 	assert.Regexp(t, "FF10117", err)
 	assert.NoError(t, mock.ExpectationsWereMet())
 }
@@ -126,7 +126,7 @@ func TestUpsertNamespaceFailCommit(t *testing.T) {
 	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"name"}))
 	mock.ExpectExec("INSERT .*").WillReturnResult(sqlmock.NewResult(1, 1))
 	mock.ExpectCommit().WillReturnError(fmt.Errorf("pop"))
-	err := s.UpsertNamespace(context.Background(), &core.Namespace{LocalName: "name1"}, true)
+	err := s.UpsertNamespace(context.Background(), &core.Namespace{Name: "name1"}, true)
 	assert.Regexp(t, "FF10119", err)
 	assert.NoError(t, mock.ExpectationsWereMet())
 }
diff --git a/internal/definitions/handler_test.go b/internal/definitions/handler_test.go
index 349e982d0..7fd07003d 100644
--- a/internal/definitions/handler_test.go
+++ b/internal/definitions/handler_test.go
@@ -44,7 +44,8 @@ func newTestDefinitionHandler(t *testing.T) (*definitionHandler, *testDefinition
 	tokenNames := make(map[string]string)
 	tokenNames["remote1"] = "connector1"
 	mbi.On("VerifierType").Return(core.VerifierTypeEthAddress).Maybe()
-	dh, _ := newDefinitionHandler(context.Background(), "ns1", "ns1", false, mdi, mbi, mdx, mdm, mim, mam, mcm, tokenNames)
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
+	dh, _ := newDefinitionHandler(context.Background(), ns, false, mdi, mbi, mdx, mdm, mim, mam, mcm, tokenNames)
 	return dh, newTestDefinitionBatchState(t)
 }
 
@@ -70,7 +71,7 @@ func (bs *testDefinitionBatchState) assertNoFinalizers() {
 }
 
 func TestInitFail(t *testing.T) {
-	_, err := newDefinitionHandler(context.Background(), "", "", false, nil, nil, nil, nil, nil, nil, nil, nil)
+	_, err := newDefinitionHandler(context.Background(), &core.Namespace{}, false, nil, nil, nil, nil, nil, nil, nil, nil)
 	assert.Regexp(t, "FF10128", err)
 }
 
diff --git a/internal/definitions/sender_test.go b/internal/definitions/sender_test.go
index 32cf21236..5e86bc917 100644
--- a/internal/definitions/sender_test.go
+++ b/internal/definitions/sender_test.go
@@ -44,17 +44,18 @@ func newTestDefinitionSender(t *testing.T) (*definitionSender, func()) {
 	mdm := &datamocks.Manager{}
 	mam := &assetmocks.Manager{}
 	mcm := &contractmocks.Manager{}
-	tokenRemoteNames := make(map[string]string)
-	tokenRemoteNames["connector1"] = "remote1"
+	tokenBroadcastNames := make(map[string]string)
+	tokenBroadcastNames["connector1"] = "remote1"
 
 	ctx, cancel := context.WithCancel(context.Background())
-	ds, _, err := NewDefinitionSender(ctx, "ns1", "ns1", false, mdi, mbi, mdx, mbm, mim, mdm, mam, mcm, tokenRemoteNames)
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
+	ds, _, err := NewDefinitionSender(ctx, ns, false, mdi, mbi, mdx, mbm, mim, mdm, mam, mcm, tokenBroadcastNames)
 	assert.NoError(t, err)
 	return ds.(*definitionSender), cancel
 }
 
 func TestInitSenderFail(t *testing.T) {
-	_, _, err := NewDefinitionSender(context.Background(), "", "", false, nil, nil, nil, nil, nil, nil, nil, nil, nil)
+	_, _, err := NewDefinitionSender(context.Background(), &core.Namespace{}, false, nil, nil, nil, nil, nil, nil, nil, nil, nil)
 	assert.Regexp(t, "FF10128", err)
 }
 
diff --git a/internal/events/dx_callbacks_test.go b/internal/events/dx_callbacks_test.go
index 48efc894f..845294d4d 100644
--- a/internal/events/dx_callbacks_test.go
+++ b/internal/events/dx_callbacks_test.go
@@ -242,7 +242,7 @@ func TestMessageReceivePersistBatchError(t *testing.T) {
 func TestMessageReceivedWrongNS(t *testing.T) {
 	em, cancel := newTestEventManager(t)
 	defer cancel()
-	em.namespace.RemoteName = "ns2"
+	em.namespace.NetworkName = "ns2"
 
 	_, b := sampleBatchTransfer(t, core.TransactionTypeBatchPin)
 
@@ -460,7 +460,7 @@ func TestPrivateBlobReceivedGetBlobsFails(t *testing.T) {
 func TestPrivateBlobReceivedWrongNS(t *testing.T) {
 	em, cancel := newTestEventManager(t)
 	cancel() // retryable error
-	em.namespace.RemoteName = "ns2"
+	em.namespace.NetworkName = "ns2"
 	hash := fftypes.NewRandB32()
 
 	mdx := &dataexchangemocks.Plugin{}
diff --git a/internal/events/event_manager_test.go b/internal/events/event_manager_test.go
index 5dab001a2..e919c2d19 100644
--- a/internal/events/event_manager_test.go
+++ b/internal/events/event_manager_test.go
@@ -99,7 +99,7 @@ func newTestEventManagerCommon(t *testing.T, metrics, dbconcurrency bool) (*even
 	mdi.On("Capabilities").Return(&database.Capabilities{Concurrency: dbconcurrency}).Maybe()
 	mev.On("SetHandler", "ns1", mock.Anything).Return(nil).Maybe()
 	mev.On("ValidateOptions", mock.Anything).Return(nil).Maybe()
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	emi, err := NewEventManager(ctx, ns, mdi, mbi, mim, msh, mdm, mds, mbm, mpm, mam, mdd, mmi, mom, txHelper, events, mmp)
 	em := emi.(*eventManager)
 	em.txHelper = &txcommonmocks.Helper{}
@@ -135,7 +135,7 @@ func TestStartStop(t *testing.T) {
 }
 
 func TestStartStopBadDependencies(t *testing.T) {
-	_, err := NewEventManager(context.Background(), core.NamespaceRef{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
+	_, err := NewEventManager(context.Background(), &core.Namespace{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
 	assert.Regexp(t, "FF10128", err)
 
 }
@@ -162,7 +162,7 @@ func TestStartStopEventListenerFail(t *testing.T) {
 	mdi.On("Capabilities").Return(&database.Capabilities{Concurrency: false})
 	mbi.On("VerifierType").Return(core.VerifierTypeEthAddress)
 	mev.On("SetHandler", "ns1", mock.Anything).Return(fmt.Errorf("pop"))
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	_, err := NewEventManager(context.Background(), ns, mdi, mbi, mim, msh, mdm, mds, mbm, mpm, mam, msd, mm, mom, txHelper, events, mmp)
 	assert.EqualError(t, err, "pop")
 }
diff --git a/internal/events/ss_callbacks_test.go b/internal/events/ss_callbacks_test.go
index 27eb0890c..af6a11e19 100644
--- a/internal/events/ss_callbacks_test.go
+++ b/internal/events/ss_callbacks_test.go
@@ -103,7 +103,7 @@ func TestSharedStorageBatchDownloadedNSMismatch(t *testing.T) {
 	mss := &sharedstoragemocks.Plugin{}
 	mss.On("Name").Return("utdx").Maybe()
 
-	em.namespace.RemoteName = "ns2"
+	em.namespace.NetworkName = "ns2"
 	_, err := em.SharedStorageBatchDownloaded(mss, "payload1", b)
 	assert.NoError(t, err)
 
diff --git a/internal/multiparty/manager_test.go b/internal/multiparty/manager_test.go
index bd19bd0a8..b5ddd64b6 100644
--- a/internal/multiparty/manager_test.go
+++ b/internal/multiparty/manager_test.go
@@ -58,8 +58,8 @@ func newTestMultipartyManager() *testMultipartyManager {
 		mth: &txcommonmocks.Helper{},
 		multipartyManager: multipartyManager{
 			namespace: &core.Namespace{
-				LocalName:  "ns1",
-				RemoteName: "ns1",
+				Name:        "ns1",
+				NetworkName: "ns1",
 				Contracts: &core.MultipartyContracts{
 					Active: &core.MultipartyContract{},
 				},
@@ -90,7 +90,7 @@ func TestNewMultipartyManager(t *testing.T) {
 		core.OpTypeBlockchainPinBatch,
 		core.OpTypeBlockchainNetworkAction,
 	}).Return()
-	ns := &core.Namespace{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	nm, err := NewMultipartyManager(context.Background(), ns, config, mdi, mbi, mom, mmi, mth)
 	assert.NotNil(t, nm)
 	assert.NoError(t, err)
diff --git a/internal/namespace/manager_test.go b/internal/namespace/manager_test.go
index e2b2e9fbc..655c483c2 100644
--- a/internal/namespace/manager_test.go
+++ b/internal/namespace/manager_test.go
@@ -93,10 +93,10 @@ func newTestNamespaceManager(resetConfig bool) *testNamespaceManager {
 		mev:  &eventsmocks.Plugin{},
 		auth: &authmocks.Plugin{},
 		namespaceManager: namespaceManager{
-			ctx:              context.Background(),
-			namespaces:       make(map[string]*namespace),
-			pluginNames:      make(map[string]bool),
-			tokenRemoteNames: make(map[string]string),
+			ctx:                 context.Background(),
+			namespaces:          make(map[string]*namespace),
+			pluginNames:         make(map[string]bool),
+			tokenBroadcastNames: make(map[string]string),
 		},
 	}
 	nm.plugins.blockchain = map[string]blockchainPlugin{
@@ -647,7 +647,7 @@ func TestInitNamespaceQueryFail(t *testing.T) {
 
 	ns := &namespace{
 		Namespace: core.Namespace{
-			LocalName: "default",
+			Name: "default",
 		},
 		plugins: []string{"postgres"},
 	}
@@ -664,12 +664,15 @@ func TestInitNamespaceExistingUpsertFail(t *testing.T) {
 
 	ns := &namespace{
 		Namespace: core.Namespace{
-			LocalName: "default",
+			Name: "default",
 		},
 		plugins: []string{"postgres"},
 	}
+	existing := &core.Namespace{
+		NetworkName: "ns1",
+	}
 
-	nm.mdi.On("GetNamespace", mock.Anything, "default").Return(&core.Namespace{}, nil)
+	nm.mdi.On("GetNamespace", mock.Anything, "default").Return(existing, nil)
 	nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(fmt.Errorf("pop"))
 
 	err := nm.initNamespace(ns)
@@ -995,7 +998,7 @@ func TestTokensPlugin(t *testing.T) {
 	assert.NoError(t, err)
 }
 
-func TestTokensPluginDuplicateRemoteName(t *testing.T) {
+func TestTokensPluginDuplicateBroadcastName(t *testing.T) {
 	nm := newTestNamespaceManager(true)
 	defer nm.cleanup(t)
 	tifactory.InitConfig(tokensConfig)
@@ -1004,12 +1007,12 @@ func TestTokensPluginDuplicateRemoteName(t *testing.T) {
   plugins:
     tokens:
     - name: test1
-      remotename: remote1
+      broadcastName: remote1
       type: fftokens
       fftokens:
         url: http://tokens:3000
     - name: test2
-      remotename: remote1
+      broadcastName: remote1
       type: fftokens
       fftokens:
         url: http://tokens:3000
@@ -1021,7 +1024,7 @@ func TestTokensPluginDuplicateRemoteName(t *testing.T) {
 	assert.Regexp(t, "FF10419", err)
 }
 
-func TestMultipleTokensPluginsWithRemoteName(t *testing.T) {
+func TestMultipleTokensPluginsWithBroadcastName(t *testing.T) {
 	nm := newTestNamespaceManager(true)
 	defer nm.cleanup(t)
 	tifactory.InitConfig(tokensConfig)
@@ -1030,12 +1033,12 @@ func TestMultipleTokensPluginsWithRemoteName(t *testing.T) {
   plugins:
     tokens:
     - name: test1
-      remotename: remote1
+      broadcastName: remote1
       type: fftokens
       fftokens:
         url: http://tokens:3000
     - name: test2
-      remotename: remote2
+      broadcastName: remote2
       type: fftokens
       fftokens:
         url: http://tokens:3000
@@ -1184,6 +1187,26 @@ func TestLoadNamespacesReservedName(t *testing.T) {
 	assert.Regexp(t, "FF10388", err)
 }
 
+func TestLoadNamespacesReservedNetworkName(t *testing.T) {
+	nm := newTestNamespaceManager(true)
+	defer nm.cleanup(t)
+
+	viper.SetConfigType("yaml")
+	err := viper.ReadConfig(strings.NewReader(`
+  namespaces:
+    default: ns1
+    predefined:
+    - name: ns1
+      multiparty:
+        enabled: true
+        networknamespace: ff_system
+    `))
+	assert.NoError(t, err)
+
+	err = nm.loadNamespaces(context.Background())
+	assert.Regexp(t, "FF10388", err)
+}
+
 func TestLoadNamespacesDuplicate(t *testing.T) {
 	nm := newTestNamespaceManager(true)
 	defer nm.cleanup(t)
diff --git a/internal/orchestrator/data_query_test.go b/internal/orchestrator/data_query_test.go
index a8351257b..a696f678b 100644
--- a/internal/orchestrator/data_query_test.go
+++ b/internal/orchestrator/data_query_test.go
@@ -32,7 +32,7 @@ func TestGetNamespace(t *testing.T) {
 	or := newTestOrchestrator()
 	defer or.cleanup(t)
 	ns := or.GetNamespace(context.Background())
-	assert.Equal(t, "ns", ns.LocalName)
+	assert.Equal(t, "ns", ns.Name)
 }
 
 func TestGetTransactionByID(t *testing.T) {
diff --git a/internal/orchestrator/orchestrator_test.go b/internal/orchestrator/orchestrator_test.go
index f4d6c11f7..cc61d1c71 100644
--- a/internal/orchestrator/orchestrator_test.go
+++ b/internal/orchestrator/orchestrator_test.go
@@ -114,7 +114,7 @@ func newTestOrchestrator() *testOrchestrator {
 		orchestrator: orchestrator{
 			ctx:       ctx,
 			cancelCtx: cancel,
-			namespace: &core.Namespace{LocalName: "ns", RemoteName: "ns"},
+			namespace: &core.Namespace{Name: "ns", NetworkName: "ns"},
 		},
 		mdi: &databasemocks.Plugin{},
 		mdm: &datamocks.Manager{},
@@ -192,7 +192,7 @@ func newTestOrchestrator() *testOrchestrator {
 
 func TestNewOrchestrator(t *testing.T) {
 	or := NewOrchestrator(
-		&core.Namespace{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		Config{},
 		&Plugins{},
 		&metricsmocks.Manager{},
@@ -203,7 +203,7 @@ func TestNewOrchestrator(t *testing.T) {
 func TestInitOK(t *testing.T) {
 	or := newTestOrchestrator()
 	defer or.cleanup(t)
-	or.namespace.RemoteName = "ns2"
+	or.namespace.NetworkName = "ns2"
 	or.config.Multiparty.Node.Name = "node1"
 	node := &core.Identity{
 		IdentityBase: core.IdentityBase{
@@ -439,7 +439,7 @@ func TestStartStopOk(t *testing.T) {
 
 func TestNetworkAction(t *testing.T) {
 	or := newTestOrchestrator()
-	or.namespace.LocalName = core.LegacySystemNamespace
+	or.namespace.Name = core.LegacySystemNamespace
 	action := &core.NetworkAction{Type: core.NetworkActionTerminate}
 	or.mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x123", nil)
 	or.mmp.On("SubmitNetworkAction", context.Background(), "0x123", action).Return(nil)
@@ -449,7 +449,7 @@ func TestNetworkAction(t *testing.T) {
 
 func TestNetworkActionBadKey(t *testing.T) {
 	or := newTestOrchestrator()
-	or.namespace.LocalName = core.LegacySystemNamespace
+	or.namespace.Name = core.LegacySystemNamespace
 	action := &core.NetworkAction{Type: core.NetworkActionTerminate}
 	or.mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop"))
 	err := or.SubmitNetworkAction(context.Background(), action)
diff --git a/internal/orchestrator/persistence_events_test.go b/internal/orchestrator/persistence_events_test.go
index e7c32e81b..3276082fa 100644
--- a/internal/orchestrator/persistence_events_test.go
+++ b/internal/orchestrator/persistence_events_test.go
@@ -30,7 +30,7 @@ import (
 func TestMessageCreated(t *testing.T) {
 	mb := &batchmocks.Manager{}
 	o := &orchestrator{
-		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		batch:     mb,
 	}
 	mb.On("NewMessages").Return((chan<- int64)(make(chan int64, 1)))
@@ -41,7 +41,7 @@ func TestMessageCreated(t *testing.T) {
 func TestPinCreated(t *testing.T) {
 	mem := &eventmocks.EventManager{}
 	o := &orchestrator{
-		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		events:    mem,
 	}
 	mem.On("NewPins").Return((chan<- int64)(make(chan int64, 1)))
@@ -52,7 +52,7 @@ func TestPinCreated(t *testing.T) {
 func TestEventCreated(t *testing.T) {
 	mem := &eventmocks.EventManager{}
 	o := &orchestrator{
-		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		events:    mem,
 	}
 	mem.On("NewEvents").Return((chan<- int64)(make(chan int64, 1)))
@@ -63,7 +63,7 @@ func TestEventCreated(t *testing.T) {
 func TestSubscriptionCreated(t *testing.T) {
 	mem := &eventmocks.EventManager{}
 	o := &orchestrator{
-		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		events:    mem,
 	}
 	mem.On("NewSubscriptions").Return((chan<- *fftypes.UUID)(make(chan *fftypes.UUID, 1)))
@@ -74,7 +74,7 @@ func TestSubscriptionCreated(t *testing.T) {
 func TestSubscriptionUpdated(t *testing.T) {
 	mem := &eventmocks.EventManager{}
 	o := &orchestrator{
-		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		events:    mem,
 	}
 	mem.On("SubscriptionUpdates").Return((chan<- *fftypes.UUID)(make(chan *fftypes.UUID, 1)))
@@ -85,7 +85,7 @@ func TestSubscriptionUpdated(t *testing.T) {
 func TestSubscriptionDeleted(t *testing.T) {
 	mem := &eventmocks.EventManager{}
 	o := &orchestrator{
-		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{Name: "ns1", NetworkName: "ns1"},
 		events:    mem,
 	}
 	mem.On("DeletedSubscriptions").Return((chan<- *fftypes.UUID)(make(chan *fftypes.UUID, 1)))
@@ -96,7 +96,7 @@ func TestSubscriptionDeleted(t *testing.T) {
 func TestOrderedUUIDCollectionWrongNS(t *testing.T) {
 	o := &orchestrator{
 		ctx:       context.Background(),
-		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{Name: "ns1", NetworkName: "ns1"},
 	}
 	o.OrderedUUIDCollectionNSEvent(database.CollectionMessages, core.ChangeEventTypeCreated, "ns2", fftypes.NewUUID(), 1)
 }
@@ -104,7 +104,7 @@ func TestOrderedUUIDCollectionWrongNS(t *testing.T) {
 func TestOrderedCollectionWrongNS(t *testing.T) {
 	o := &orchestrator{
 		ctx:       context.Background(),
-		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{Name: "ns1", NetworkName: "ns1"},
 	}
 	o.OrderedCollectionNSEvent(database.CollectionPins, core.ChangeEventTypeCreated, "ns2", 1)
 }
@@ -112,7 +112,7 @@ func TestOrderedCollectionWrongNS(t *testing.T) {
 func TestUUIDCollectionWrongNS(t *testing.T) {
 	o := &orchestrator{
 		ctx:       context.Background(),
-		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{Name: "ns1", NetworkName: "ns1"},
 	}
 	o.UUIDCollectionNSEvent(database.CollectionSubscriptions, core.ChangeEventTypeCreated, "ns2", fftypes.NewUUID())
 }
diff --git a/internal/orchestrator/status_test.go b/internal/orchestrator/status_test.go
index 607e0729a..8e9ee165b 100644
--- a/internal/orchestrator/status_test.go
+++ b/internal/orchestrator/status_test.go
@@ -115,7 +115,7 @@ func TestGetStatusRegistered(t *testing.T) {
 	status, err := or.GetStatus(or.ctx)
 	assert.NoError(t, err)
 
-	assert.Equal(t, "ns", status.Namespace.LocalName)
+	assert.Equal(t, "ns", status.Namespace.Name)
 
 	assert.Equal(t, "org1", status.Org.Name)
 	assert.True(t, status.Org.Registered)
@@ -203,7 +203,7 @@ func TestGetStatusWrongNodeOwner(t *testing.T) {
 	status, err := or.GetStatus(or.ctx)
 	assert.NoError(t, err)
 
-	assert.Equal(t, "ns", status.Namespace.LocalName)
+	assert.Equal(t, "ns", status.Namespace.Name)
 
 	assert.Equal(t, "org1", status.Org.Name)
 	assert.True(t, status.Org.Registered)
@@ -233,7 +233,7 @@ func TestGetStatusUnregistered(t *testing.T) {
 	status, err := or.GetStatus(or.ctx)
 	assert.NoError(t, err)
 
-	assert.Equal(t, "ns", status.Namespace.LocalName)
+	assert.Equal(t, "ns", status.Namespace.Name)
 
 	assert.Equal(t, "org1", status.Org.Name)
 	assert.False(t, status.Org.Registered)
@@ -276,7 +276,7 @@ func TestGetStatusOrgOnlyRegistered(t *testing.T) {
 	status, err := or.GetStatus(or.ctx)
 	assert.NoError(t, err)
 
-	assert.Equal(t, "ns", status.Namespace.LocalName)
+	assert.Equal(t, "ns", status.Namespace.Name)
 
 	assert.Equal(t, "org1", status.Org.Name)
 	assert.True(t, status.Org.Registered)
diff --git a/internal/privatemessaging/privatemessaging_test.go b/internal/privatemessaging/privatemessaging_test.go
index 59db88396..623acd697 100644
--- a/internal/privatemessaging/privatemessaging_test.go
+++ b/internal/privatemessaging/privatemessaging_test.go
@@ -76,7 +76,7 @@ func newTestPrivateMessagingCommon(t *testing.T, metricsEnabled bool) (*privateM
 	mom.On("RegisterHandler", mock.Anything, mock.Anything, mock.Anything)
 
 	ctx, cancel := context.WithCancel(context.Background())
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	pm, err := NewPrivateMessaging(ctx, ns, mdi, mdx, mbi, mim, mba, mdm, msa, mmp, mmi, mom)
 	assert.NoError(t, err)
 
@@ -212,7 +212,7 @@ func TestDispatchBatchWithBlobs(t *testing.T) {
 }
 
 func TestNewPrivateMessagingMissingDeps(t *testing.T) {
-	_, err := NewPrivateMessaging(context.Background(), core.NamespaceRef{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
+	_, err := NewPrivateMessaging(context.Background(), &core.Namespace{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
 	assert.Regexp(t, "FF10128", err)
 }
 
diff --git a/internal/shareddownload/download_manager_test.go b/internal/shareddownload/download_manager_test.go
index 922cabc38..c84ddc609 100644
--- a/internal/shareddownload/download_manager_test.go
+++ b/internal/shareddownload/download_manager_test.go
@@ -54,7 +54,7 @@ func newTestDownloadManager(t *testing.T) (*downloadManager, func()) {
 	}).Return()
 
 	ctx, cancel := context.WithCancel(context.Background())
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{Name: "ns1", NetworkName: "ns1"}
 	pm, err := NewDownloadManager(ctx, ns, mdi, mss, mdx, mom, mci)
 	assert.NoError(t, err)
 
@@ -62,7 +62,7 @@ func newTestDownloadManager(t *testing.T) (*downloadManager, func()) {
 }
 
 func TestNewDownloadManagerMissingDeps(t *testing.T) {
-	_, err := NewDownloadManager(context.Background(), core.NamespaceRef{}, nil, nil, nil, nil, nil)
+	_, err := NewDownloadManager(context.Background(), &core.Namespace{}, nil, nil, nil, nil, nil)
 	assert.Regexp(t, "FF10128", err)
 }
 
diff --git a/pkg/core/namespace_test.go b/pkg/core/namespace_test.go
index 7b1bd614a..bb165c18d 100644
--- a/pkg/core/namespace_test.go
+++ b/pkg/core/namespace_test.go
@@ -23,16 +23,6 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestNamespace(t *testing.T) {
-	ns := &Namespace{
-		LocalName:  "local",
-		RemoteName: "remote",
-	}
-	ref := ns.Ref()
-	assert.Equal(t, "local", ref.LocalName)
-	assert.Equal(t, "remote", ref.RemoteName)
-}
-
 func TestMultipartyContractsDatabaseSerialization(t *testing.T) {
 	contracts1 := &MultipartyContracts{
 		Active: &MultipartyContract{
diff --git a/test/e2e/common.go b/test/e2e/common.go
index c6d616719..134ff53af 100644
--- a/test/e2e/common.go
+++ b/test/e2e/common.go
@@ -80,11 +80,11 @@ func RandomName(t *testing.T) string {
 	return fmt.Sprintf("e2e_%x", b)
 }
 
-func AddPluginRemoteName(data map[string]interface{}, pluginType, remoteName string) {
+func AddPluginBroadcastName(data map[string]interface{}, pluginType, broadcastName string) {
 	pluginsConfig := data["plugins"].(map[interface{}]interface{})
 	plugins := pluginsConfig[pluginType].([]interface{})
 	plugin := plugins[0].(map[interface{}]interface{})
-	plugin["remotename"] = remoteName
+	plugin["broadcastName"] = broadcastName
 }
 
 // ChangeDefaultNSPluginLocalName changes the plugin local name and updates the plugin list for the default namespace
diff --git a/test/e2e/multiparty/multi_tenancy.go b/test/e2e/multiparty/multi_tenancy.go
index 29c0243e9..35201bcc9 100644
--- a/test/e2e/multiparty/multi_tenancy.go
+++ b/test/e2e/multiparty/multi_tenancy.go
@@ -45,19 +45,19 @@ func (suite *NamespaceAliasSuite) AfterTest(suiteName, testName string) {
 	e2e.VerifyAllOperationsSucceeded(suite.T(), []*client.FireFlyClient{suite.testState.client1, suite.testState.client2}, suite.testState.startTime)
 }
 
-func newNamespace(remoteName, contractAddress string) (ns, org, node map[string]interface{}) {
+func newNamespace(networkNamespace, contractAddress string) (ns, org, node map[string]interface{}) {
 	org = make(map[string]interface{})
 	node = make(map[string]interface{})
 	ns = map[string]interface{}{
-		"remotename": remoteName,
 		"multiparty": map[string]interface{}{
-			"enabled": true,
-			"org":     org,
-			"node":    node,
+			"enabled":          true,
+			"networkNamespace": networkNamespace,
+			"org":              org,
+			"node":             node,
 			"contract": []map[string]interface{}{
 				{
 					"location":   map[string]interface{}{"address": contractAddress},
-					"firstevent": "0",
+					"firstEvent": "0",
 				},
 			},
 		},
diff --git a/test/e2e/multiparty/tokens_remote_name.go b/test/e2e/multiparty/tokens_remote_name.go
index 49ef7d7ce..d1a735c99 100644
--- a/test/e2e/multiparty/tokens_remote_name.go
+++ b/test/e2e/multiparty/tokens_remote_name.go
@@ -53,14 +53,14 @@ func (suite *TokensRemoteNameTestSuite) TestE2EFungibleTokensWithRemoteNameAsync
 	newLocalName := "tokens1"
 
 	config1 := e2e.ReadConfig(suite.T(), suite.testState.configFile1)
-	e2e.AddPluginRemoteName(config1, "tokens", "testremote")
+	e2e.AddPluginBroadcastName(config1, "tokens", "testremote")
 
 	// Change plugin local name and update plugin list
 	e2e.ChangeDefaultNSPluginLocalName(config1, "tokens", newLocalName)
 	e2e.WriteConfig(suite.T(), suite.testState.configFile1, config1)
 
 	config2 := e2e.ReadConfig(suite.T(), suite.testState.configFile2)
-	e2e.AddPluginRemoteName(config2, "tokens", "testremote")
+	e2e.AddPluginBroadcastName(config2, "tokens", "testremote")
 	e2e.WriteConfig(suite.T(), suite.testState.configFile2, config2)
 
 	admin1 := client.NewResty(suite.T())