Repository: hyperledger/firefly
Pull request #898 (2022-07-12 00:05:58)

Strip down namespace object and clean up APIs
Part of [FIR-12](https://github.com/hyperledger/firefly-fir/pull/12)
~~In a chain with #895~~

Remove all remaining functionality related to broadcasting namespaces. It was already broken from previous commits, but this is now to confirm:
- any previously-broadcast namespaces that are not spelled out in the config will cease to function in the next release
- in the future, if we want to add back dynamic namespace capability, it will probably take the form of editing the config file and adding a local API that reloads the config dynamically

Clean up the `/namespaces/{ns}` and `/namespaces/{ns}/status` APIs to be a little more organized.

Clean up all namespace database operations to happen from Namespace Manager. Pass a pointer to the namespace object to any other managers that need it.

codecov-commenter: # [Codecov](https://codecov.io/gh/hyperledger/firefly/pull/898?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger) Report
> Merging [#898](https://codecov.io/gh/hyperledger/firefly/pull/898?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger) (f3b65db) into [main](https://codecov.io/gh/hyperledger/firefly/commit/2adbebb2f7289209559f814695ecd2e541c24072?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger) (2adbebb) will **decrease** coverage by `0.00%`.
> The diff coverage is `96.77%`.

```diff
@@            Coverage Diff             @@
##             main     #898      +/-   ##
==========================================
- Coverage   99.95%   99.95%   -0.01%     
==========================================
  Files         298      297       -1     
  Lines       19341    19261      -80     
==========================================
- Hits        19333    19252      -81     
- Misses          6        7       +1     
  Partials        2        2              
```


| [Impacted Files](https://codecov.io/gh/hyperledger/firefly/pull/898?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger) | Coverage Δ | |
|---|---|---|
| [internal/apiserver/route\_get\_websockets.go](https://codecov.io/gh/hyperledger/firefly/pull/898/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvYXBpc2VydmVyL3JvdXRlX2dldF93ZWJzb2NrZXRzLmdv) | `100.00% <ø> (ø)` | |
| [internal/database/sqlcommon/sqlcommon.go](https://codecov.io/gh/hyperledger/firefly/pull/898/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvZGF0YWJhc2Uvc3FsY29tbW9uL3NxbGNvbW1vbi5nbw==) | `100.00% <ø> (ø)` | |
| [internal/definitions/handler.go](https://codecov.io/gh/hyperledger/firefly/pull/898/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvZGVmaW5pdGlvbnMvaGFuZGxlci5nbw==) | `100.00% <ø> (ø)` | |
| [internal/namespace/persistence\_events.go](https://codecov.io/gh/hyperledger/firefly/pull/898/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvbmFtZXNwYWNlL3BlcnNpc3RlbmNlX2V2ZW50cy5nbw==) | `100.00% <ø> (ø)` | |
| [internal/reference/reference.go](https://codecov.io/gh/hyperledger/firefly/pull/898/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvcmVmZXJlbmNlL3JlZmVyZW5jZS5nbw==) | `100.00% <ø> (ø)` | |
| [internal/txcommon/event\_enrich.go](https://codecov.io/gh/hyperledger/firefly/pull/898/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvdHhjb21tb24vZXZlbnRfZW5yaWNoLmdv) | `100.00% <ø> (ø)` | |
| [pkg/core/event.go](https://codecov.io/gh/hyperledger/firefly/pull/898/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-cGtnL2NvcmUvZXZlbnQuZ28=) | `100.00% <ø> (ø)` | |
| [internal/orchestrator/orchestrator.go](https://codecov.io/gh/hyperledger/firefly/pull/898/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvb3JjaGVzdHJhdG9yL29yY2hlc3RyYXRvci5nbw==) | `97.89% <88.57%> (-2.11%)` | :arrow_down: |
| [internal/apiserver/route\_get\_namespace.go](https://codecov.io/gh/hyperledger/firefly/pull/898/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvYXBpc2VydmVyL3JvdXRlX2dldF9uYW1lc3BhY2UuZ28=) | `100.00% <100.00%> (ø)` | |
| [internal/apiserver/route\_get\_status.go](https://codecov.io/gh/hyperledger/firefly/pull/898/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger#diff-aW50ZXJuYWwvYXBpc2VydmVyL3JvdXRlX2dldF9zdGF0dXMuZ28=) | `100.00% <100.00%> (ø)` | |
| ... and [13 more](https://codecov.io/gh/hyperledger/firefly/pull/898/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/hyperledger/firefly/pull/898?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/hyperledger/firefly/pull/898?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger). Last update [2adbebb...f3b65db](https://codecov.io/gh/hyperledger/firefly/pull/898?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperledger).


Patch:
diff --git a/db/migrations/postgres/000095_add_message_local_namespace.down.sql b/db/migrations/postgres/000095_add_message_local_namespace.down.sql
deleted file mode 100644
index 334f2c840..000000000
--- a/db/migrations/postgres/000095_add_message_local_namespace.down.sql
+++ /dev/null
@@ -1,4 +0,0 @@
-BEGIN;
-ALTER TABLE messages DROP COLUMN namespace_local;
-ALTER TABLE groups DROP COLUMN namespace_local;
-COMMIT;
diff --git a/db/migrations/postgres/000095_change_namespace_fields.down.sql b/db/migrations/postgres/000095_change_namespace_fields.down.sql
new file mode 100644
index 000000000..9971d262f
--- /dev/null
+++ b/db/migrations/postgres/000095_change_namespace_fields.down.sql
@@ -0,0 +1,9 @@
+BEGIN;
+ALTER TABLE messages DROP COLUMN namespace_local;
+ALTER TABLE groups DROP COLUMN namespace_local;
+
+ALTER TABLE namespaces ADD COLUMN id UUID;
+ALTER TABLE namespaces ADD COLUMN message_id UUID;
+ALTER TABLE namespaces ADD COLUMN ntype VARCHAR(64);
+ALTER TABLE namespaces DROP COLUMN remote_name;
+COMMIT;
diff --git a/db/migrations/postgres/000095_add_message_local_namespace.up.sql b/db/migrations/postgres/000095_change_namespace_fields.up.sql
similarity index 52%
rename from db/migrations/postgres/000095_add_message_local_namespace.up.sql
rename to db/migrations/postgres/000095_change_namespace_fields.up.sql
index c225b9259..97f3094c4 100644
--- a/db/migrations/postgres/000095_add_message_local_namespace.up.sql
+++ b/db/migrations/postgres/000095_change_namespace_fields.up.sql
@@ -6,4 +6,12 @@ ALTER TABLE messages ALTER COLUMN namespace_local SET NOT NULL;
 ALTER TABLE groups ADD COLUMN namespace_local VARCHAR(64);
 UPDATE groups SET namespace_local = namespace;
 ALTER TABLE groups ALTER COLUMN namespace_local SET NOT NULL;
+
+DROP INDEX namespaces_id;
+ALTER TABLE namespaces DROP COLUMN id;
+ALTER TABLE namespaces DROP COLUMN message_id;
+ALTER TABLE namespaces DROP COLUMN ntype;
+ALTER TABLE namespaces ADD COLUMN remote_name VARCHAR(64);
+UPDATE namespaces SET remote_name = name;
+ALTER TABLE namespaces ALTER COLUMN remote_name SET NOT NULL;
 COMMIT;
diff --git a/db/migrations/sqlite/000095_add_message_local_namespace.down.sql b/db/migrations/sqlite/000095_add_message_local_namespace.down.sql
deleted file mode 100644
index 784e7fb27..000000000
--- a/db/migrations/sqlite/000095_add_message_local_namespace.down.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-ALTER TABLE messages DROP COLUMN namespace_local;
-ALTER TABLE groups DROP COLUMN namespace_local;
\ No newline at end of file
diff --git a/db/migrations/sqlite/000095_add_message_local_namespace.up.sql b/db/migrations/sqlite/000095_add_message_local_namespace.up.sql
deleted file mode 100644
index d775ee40c..000000000
--- a/db/migrations/sqlite/000095_add_message_local_namespace.up.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-ALTER TABLE messages ADD COLUMN namespace_local VARCHAR(64);
-UPDATE messages SET namespace_local = namespace;
-
-ALTER TABLE groups ADD COLUMN namespace_local VARCHAR(64);
-UPDATE groups SET namespace_local = namespace;
diff --git a/db/migrations/sqlite/000095_change_namespace_fields.down.sql b/db/migrations/sqlite/000095_change_namespace_fields.down.sql
new file mode 100644
index 000000000..bf7129886
--- /dev/null
+++ b/db/migrations/sqlite/000095_change_namespace_fields.down.sql
@@ -0,0 +1,7 @@
+ALTER TABLE messages DROP COLUMN namespace_local;
+ALTER TABLE groups DROP COLUMN namespace_local;
+
+ALTER TABLE namespaces ADD COLUMN id UUID;
+ALTER TABLE namespaces ADD COLUMN message_id UUID;
+ALTER TABLE namespaces ADD COLUMN ntype VARCHAR(64);
+ALTER TABLE namespaces DROP COLUMN remote_name;
diff --git a/db/migrations/sqlite/000095_change_namespace_fields.up.sql b/db/migrations/sqlite/000095_change_namespace_fields.up.sql
new file mode 100644
index 000000000..f38ca4c96
--- /dev/null
+++ b/db/migrations/sqlite/000095_change_namespace_fields.up.sql
@@ -0,0 +1,12 @@
+ALTER TABLE messages ADD COLUMN namespace_local VARCHAR(64);
+UPDATE messages SET namespace_local = namespace;
+
+ALTER TABLE groups ADD COLUMN namespace_local VARCHAR(64);
+UPDATE groups SET namespace_local = namespace;
+
+DROP INDEX namespaces_id;
+ALTER TABLE namespaces DROP COLUMN id;
+ALTER TABLE namespaces DROP COLUMN message_id;
+ALTER TABLE namespaces DROP COLUMN ntype;
+ALTER TABLE namespaces ADD COLUMN remote_name VARCHAR(64);
+UPDATE namespaces SET remote_name = name;
diff --git a/docs/reference/types/event.md b/docs/reference/types/event.md
index 00a1198ea..2e5c20b2c 100644
--- a/docs/reference/types/event.md
+++ b/docs/reference/types/event.md
@@ -41,7 +41,7 @@ nav_order: 2
 |------------|-------------|------|
 | `id` | The UUID assigned to this event by your local FireFly node | [`UUID`](simpletypes#uuid) |
 | `sequence` | A sequence indicating the order in which events are delivered to your application. Assure to be unique per event in your local FireFly database (unlike the created timestamp) | `int64` |
-| `type` | All interesting activity in FireFly is emitted as a FireFly event, of a given type. The 'type' combined with the 'reference' can be used to determine how to process the event within your application | `FFEnum`:<br/>`"transaction_submitted"`<br/>`"message_confirmed"`<br/>`"message_rejected"`<br/>`"namespace_confirmed"`<br/>`"datatype_confirmed"`<br/>`"identity_confirmed"`<br/>`"identity_updated"`<br/>`"token_pool_confirmed"`<br/>`"token_pool_op_failed"`<br/>`"token_transfer_confirmed"`<br/>`"token_transfer_op_failed"`<br/>`"token_approval_confirmed"`<br/>`"token_approval_op_failed"`<br/>`"contract_interface_confirmed"`<br/>`"contract_api_confirmed"`<br/>`"blockchain_event_received"`<br/>`"blockchain_invoke_op_succeeded"`<br/>`"blockchain_invoke_op_failed"` |
+| `type` | All interesting activity in FireFly is emitted as a FireFly event, of a given type. The 'type' combined with the 'reference' can be used to determine how to process the event within your application | `FFEnum`:<br/>`"transaction_submitted"`<br/>`"message_confirmed"`<br/>`"message_rejected"`<br/>`"datatype_confirmed"`<br/>`"identity_confirmed"`<br/>`"identity_updated"`<br/>`"token_pool_confirmed"`<br/>`"token_pool_op_failed"`<br/>`"token_transfer_confirmed"`<br/>`"token_transfer_op_failed"`<br/>`"token_approval_confirmed"`<br/>`"token_approval_op_failed"`<br/>`"contract_interface_confirmed"`<br/>`"contract_api_confirmed"`<br/>`"blockchain_event_received"`<br/>`"blockchain_invoke_op_succeeded"`<br/>`"blockchain_invoke_op_failed"` |
 | `namespace` | The namespace of the event. Your application must subscribe to events within a namespace | `string` |
 | `reference` | The UUID of an resource that is the subject of this event. The event type determines what type of resource is referenced, and whether this field might be unset | [`UUID`](simpletypes#uuid) |
 | `correlator` | For message events, this is the 'header.cid' field from the referenced message. For certain other event types, a secondary object is referenced such as a token pool | [`UUID`](simpletypes#uuid) |
diff --git a/docs/reference/types/namespace.md b/docs/reference/types/namespace.md
index 85b04f38b..9297ccb9f 100644
--- a/docs/reference/types/namespace.md
+++ b/docs/reference/types/namespace.md
@@ -24,16 +24,10 @@ nav_order: 20
 
 ```json
 {
-    "id": "7b2d9c7e-3d60-452c-a409-05e77c855d3a",
     "name": "default",
+    "remoteName": "default",
     "description": "Default predefined namespace",
-    "type": "local",
-    "created": "2022-05-16T01:23:16Z",
-    "fireflyContract": {
-        "active": {
-            "index": 0
-        }
-    }
+    "created": "2022-05-16T01:23:16Z"
 }
 ```
 
@@ -41,30 +35,8 @@ nav_order: 20
 
 | Field Name | Description | Type |
 |------------|-------------|------|
-| `id` | The UUID of the namespace. For locally established namespaces will be different on each node in the network. For broadcast namespaces, will be the same on every node | [`UUID`](simpletypes#uuid) |
-| `message` | The UUID of broadcast message used to establish the namespace. Unset for local namespaces | [`UUID`](simpletypes#uuid) |
-| `name` | The namespace name | `string` |
+| `name` | The local namespace name | `string` |
+| `remoteName` | The namespace name within the multiparty network | `string` |
 | `description` | A description of the namespace | `string` |
-| `type` | The type of the namespace | `FFEnum`:<br/>`"local"`<br/>`"broadcast"`<br/>`"system"` |
 | `created` | The time the namespace was created | [`FFTime`](simpletypes#fftime) |
-| `fireflyContract` | Info on the FireFly smart contract configured for this namespace | [`FireFlyContracts`](#fireflycontracts) |
-
-## FireFlyContracts
-
-| Field Name | Description | Type |
-|------------|-------------|------|
-| `active` | The currently active FireFly smart contract | [`FireFlyContractInfo`](#fireflycontractinfo) |
-| `terminated` | Previously-terminated FireFly smart contracts | [`FireFlyContractInfo[]`](#fireflycontractinfo) |
-
-## FireFlyContractInfo
-
-| Field Name | Description | Type |
-|------------|-------------|------|
-| `index` | The index of this contract in the config file | `int` |
-| `finalEvent` | The identifier for the final blockchain event received from this contract before termination | `string` |
-| `location` | A blockchain specific contract identifier. For example an Ethereum contract address, or a Fabric chaincode name and channel | [`JSONAny`](simpletypes#jsonany) |
-| `firstEvent` | A blockchain specific string, such as a block number, to start listening from. The special strings 'oldest' and 'newest' are supported by all blockchain connectors | `string` |
-| `subscription` | The backend identifier of the subscription for the FireFly BatchPin contract | `string` |
-
-
 
diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml
index 34e99c65d..a368c3017 100644
--- a/docs/swagger/swagger.yaml
+++ b/docs/swagger/swagger.yaml
@@ -5400,7 +5400,6 @@ paths:
                       - transaction_submitted
                       - message_confirmed
                       - message_rejected
-                      - namespace_confirmed
                       - datatype_confirmed
                       - identity_confirmed
                       - identity_updated
@@ -5504,7 +5503,6 @@ paths:
                     - transaction_submitted
                     - message_confirmed
                     - message_rejected
-                    - namespace_confirmed
                     - datatype_confirmed
                     - identity_confirmed
                     - identity_updated
@@ -7521,7 +7519,6 @@ paths:
                       - transaction_submitted
                       - message_confirmed
                       - message_rejected
-                      - namespace_confirmed
                       - datatype_confirmed
                       - identity_confirmed
                       - identity_updated
@@ -8790,86 +8787,11 @@ paths:
                     description:
                       description: A description of the namespace
                       type: string
-                    fireflyContract:
-                      description: Info on the FireFly smart contract configured for
-                        this namespace
-                      properties:
-                        active:
-                          description: The currently active FireFly smart contract
-                          properties:
-                            finalEvent:
-                              description: The identifier for the final blockchain
-                                event received from this contract before termination
-                              type: string
-                            firstEvent:
-                              description: A blockchain specific string, such as a
-                                block number, to start listening from. The special
-                                strings 'oldest' and 'newest' are supported by all
-                                blockchain connectors
-                              type: string
-                            index:
-                              description: The index of this contract in the config
-                                file
-                              type: integer
-                            location:
-                              description: A blockchain specific contract identifier.
-                                For example an Ethereum contract address, or a Fabric
-                                chaincode name and channel
-                            subscription:
-                              description: The backend identifier of the subscription
-                                for the FireFly BatchPin contract
-                              type: string
-                          type: object
-                        terminated:
-                          description: Previously-terminated FireFly smart contracts
-                          items:
-                            description: Previously-terminated FireFly smart contracts
-                            properties:
-                              finalEvent:
-                                description: The identifier for the final blockchain
-                                  event received from this contract before termination
-                                type: string
-                              firstEvent:
-                                description: A blockchain specific string, such as
-                                  a block number, to start listening from. The special
-                                  strings 'oldest' and 'newest' are supported by all
-                                  blockchain connectors
-                                type: string
-                              index:
-                                description: The index of this contract in the config
-                                  file
-                                type: integer
-                              location:
-                                description: A blockchain specific contract identifier.
-                                  For example an Ethereum contract address, or a Fabric
-                                  chaincode name and channel
-                              subscription:
-                                description: The backend identifier of the subscription
-                                  for the FireFly BatchPin contract
-                                type: string
-                            type: object
-                          type: array
-                      type: object
-                    id:
-                      description: The UUID of the namespace. For locally established
-                        namespaces will be different on each node in the network.
-                        For broadcast namespaces, will be the same on every node
-                      format: uuid
-                      type: string
-                    message:
-                      description: The UUID of broadcast message used to establish
-                        the namespace. Unset for local namespaces
-                      format: uuid
-                      type: string
                     name:
-                      description: The namespace name
+                      description: The local namespace name
                       type: string
-                    type:
-                      description: The type of the namespace
-                      enum:
-                      - local
-                      - broadcast
-                      - system
+                    remoteName:
+                      description: The namespace name within the multiparty network
                       type: string
                   type: object
                 type: array
@@ -8910,86 +8832,11 @@ paths:
                   description:
                     description: A description of the namespace
                     type: string
-                  fireflyContract:
-                    description: Info on the FireFly smart contract configured for
-                      this namespace
-                    properties:
-                      active:
-                        description: The currently active FireFly smart contract
-                        properties:
-                          finalEvent:
-                            description: The identifier for the final blockchain event
-                              received from this contract before termination
-                            type: string
-                          firstEvent:
-                            description: A blockchain specific string, such as a block
-                              number, to start listening from. The special strings
-                              'oldest' and 'newest' are supported by all blockchain
-                              connectors
-                            type: string
-                          index:
-                            description: The index of this contract in the config
-                              file
-                            type: integer
-                          location:
-                            description: A blockchain specific contract identifier.
-                              For example an Ethereum contract address, or a Fabric
-                              chaincode name and channel
-                          subscription:
-                            description: The backend identifier of the subscription
-                              for the FireFly BatchPin contract
-                            type: string
-                        type: object
-                      terminated:
-                        description: Previously-terminated FireFly smart contracts
-                        items:
-                          description: Previously-terminated FireFly smart contracts
-                          properties:
-                            finalEvent:
-                              description: The identifier for the final blockchain
-                                event received from this contract before termination
-                              type: string
-                            firstEvent:
-                              description: A blockchain specific string, such as a
-                                block number, to start listening from. The special
-                                strings 'oldest' and 'newest' are supported by all
-                                blockchain connectors
-                              type: string
-                            index:
-                              description: The index of this contract in the config
-                                file
-                              type: integer
-                            location:
-                              description: A blockchain specific contract identifier.
-                                For example an Ethereum contract address, or a Fabric
-                                chaincode name and channel
-                            subscription:
-                              description: The backend identifier of the subscription
-                                for the FireFly BatchPin contract
-                              type: string
-                          type: object
-                        type: array
-                    type: object
-                  id:
-                    description: The UUID of the namespace. For locally established
-                      namespaces will be different on each node in the network. For
-                      broadcast namespaces, will be the same on every node
-                    format: uuid
-                    type: string
-                  message:
-                    description: The UUID of broadcast message used to establish the
-                      namespace. Unset for local namespaces
-                    format: uuid
-                    type: string
                   name:
-                    description: The namespace name
+                    description: The local namespace name
                     type: string
-                  type:
-                    description: The type of the namespace
-                    enum:
-                    - local
-                    - broadcast
-                    - system
+                  remoteName:
+                    description: The namespace name within the multiparty network
                     type: string
                 type: object
           description: Success
@@ -14818,7 +14665,6 @@ paths:
                       - transaction_submitted
                       - message_confirmed
                       - message_rejected
-                      - namespace_confirmed
                       - datatype_confirmed
                       - identity_confirmed
                       - identity_updated
@@ -14929,7 +14775,6 @@ paths:
                     - transaction_submitted
                     - message_confirmed
                     - message_rejected
-                    - namespace_confirmed
                     - datatype_confirmed
                     - identity_confirmed
                     - identity_updated
@@ -17037,7 +16882,6 @@ paths:
                       - transaction_submitted
                       - message_confirmed
                       - message_rejected
-                      - namespace_confirmed
                       - datatype_confirmed
                       - identity_confirmed
                       - identity_updated
@@ -20567,7 +20411,7 @@ paths:
       - Non-Default Namespace
   /namespaces/{ns}/status:
     get:
-      description: Gets the status of this node
+      description: Gets the status of this namespace
       operationId: getStatusNamespace
       parameters:
       - description: The namespace which scopes this request
@@ -20590,9 +20434,102 @@ paths:
             application/json:
               schema:
                 properties:
+                  multiparty:
+                    description: Information about the multi-party system configured
+                      on this namespace
+                    properties:
+                      contract:
+                        description: Information about the multi-party smart contract
+                          configured for this namespace
+                        properties:
+                          active:
+                            description: The currently active FireFly smart contract
+                            properties:
+                              firstEvent:
+                                description: A blockchain specific string, such as
+                                  a block number, to start listening from. The special
+                                  strings 'oldest' and 'newest' are supported by all
+                                  blockchain connectors
+                                type: string
+                              index:
+                                description: The index of this contract in the config
+                                  file
+                                type: integer
+                              info:
+                                description: Additional info about the current status
+                                  of the multi-party contract
+                                properties:
+                                  finalEvent:
+                                    description: The identifier for the final blockchain
+                                      event received from this contract before termination
+                                    type: string
+                                  subscription:
+                                    description: The backend identifier of the subscription
+                                      for the FireFly BatchPin contract
+                                    type: string
+                                type: object
+                              location:
+                                description: A blockchain specific contract identifier.
+                                  For example an Ethereum contract address, or a Fabric
+                                  chaincode name and channel
+                            type: object
+                          terminated:
+                            description: Previously-terminated FireFly smart contracts
+                            items:
+                              description: Previously-terminated FireFly smart contracts
+                              properties:
+                                firstEvent:
+                                  description: A blockchain specific string, such
+                                    as a block number, to start listening from. The
+                                    special strings 'oldest' and 'newest' are supported
+                                    by all blockchain connectors
+                                  type: string
+                                index:
+                                  description: The index of this contract in the config
+                                    file
+                                  type: integer
+                                info:
+                                  description: Additional info about the current status
+                                    of the multi-party contract
+                                  properties:
+                                    finalEvent:
+                                      description: The identifier for the final blockchain
+                                        event received from this contract before termination
+                                      type: string
+                                    subscription:
+                                      description: The backend identifier of the subscription
+                                        for the FireFly BatchPin contract
+                                      type: string
+                                  type: object
+                                location:
+                                  description: A blockchain specific contract identifier.
+                                    For example an Ethereum contract address, or a
+                                    Fabric chaincode name and channel
+                              type: object
+                            type: array
+                        type: object
+                      enabled:
+                        description: Whether multi-party mode is enabled for this
+                          namespace
+                        type: boolean
+                    type: object
                   namespace:
                     description: The namespace that this status applies to
-                    type: string
+                    properties:
+                      created:
+                        description: The time the namespace was created
+                        format: date-time
+                        type: string
+                      description:
+                        description: A description of the namespace
+                        type: string
+                      name:
+                        description: The local namespace name
+                        type: string
+                      remoteName:
+                        description: The namespace name within the multiparty network
+                        type: string
+                    type: object
                   node:
                     description: Details of the local node
                     properties:
@@ -20609,8 +20546,8 @@ paths:
                         type: boolean
                     type: object
                   org:
-                    description: Details of the organization identity that operates
-                      this node
+                    description: Details of the root organization identity registered
+                      for this namespace on the local node
                     properties:
                       did:
                         description: The DID of the organization identity, if registered
@@ -20649,12 +20586,12 @@ paths:
                         type: array
                     type: object
                   plugins:
-                    description: Information about plugins configured on this node
+                    description: Information about plugins configured on this namespace
                     properties:
                       blockchain:
-                        description: The blockchain plugins on this node
+                        description: The blockchain plugins on this namespace
                         items:
-                          description: The blockchain plugins on this node
+                          description: The blockchain plugins on this namespace
                           properties:
                             name:
                               description: The name of the plugin
@@ -20665,9 +20602,9 @@ paths:
                           type: object
                         type: array
                       dataExchange:
-                        description: The data exchange plugins on this node
+                        description: The data exchange plugins on this namespace
                         items:
-                          description: The data exchange plugins on this node
+                          description: The data exchange plugins on this namespace
                           properties:
                             name:
                               description: The name of the plugin
@@ -20678,9 +20615,9 @@ paths:
                           type: object
                         type: array
                       database:
-                        description: The database plugins on this node
+                        description: The database plugins on this namespace
                         items:
-                          description: The database plugins on this node
+                          description: The database plugins on this namespace
                           properties:
                             name:
                               description: The name of the plugin
@@ -20691,9 +20628,9 @@ paths:
                           type: object
                         type: array
                       events:
-                        description: The event plugins on this node
+                        description: The event plugins on this namespace
                         items:
-                          description: The event plugins on this node
+                          description: The event plugins on this namespace
                           properties:
                             name:
                               description: The name of the plugin
@@ -20704,9 +20641,9 @@ paths:
                           type: object
                         type: array
                       identity:
-                        description: The identity plugins on this node
+                        description: The identity plugins on this namespace
                         items:
-                          description: The identity plugins on this node
+                          description: The identity plugins on this namespace
                           properties:
                             name:
                               description: The name of the plugin
@@ -20717,9 +20654,9 @@ paths:
                           type: object
                         type: array
                       sharedStorage:
-                        description: The shared storage plugins on this node
+                        description: The shared storage plugins on this namespace
                         items:
-                          description: The shared storage plugins on this node
+                          description: The shared storage plugins on this namespace
                           properties:
                             name:
                               description: The name of the plugin
@@ -20730,9 +20667,9 @@ paths:
                           type: object
                         type: array
                       tokens:
-                        description: The token plugins on this node
+                        description: The token plugins on this namespace
                         items:
-                          description: The token plugins on this node
+                          description: The token plugins on this namespace
                           properties:
                             name:
                               description: The name of the plugin
@@ -28035,7 +27972,7 @@ paths:
       - Default Namespace
   /status:
     get:
-      description: Gets the status of this node
+      description: Gets the status of this namespace
       operationId: getStatus
       parameters:
       - description: Server-side request timeout (milliseconds, or set a custom suffix
@@ -28051,9 +27988,102 @@ paths:
             application/json:
               schema:
                 properties:
+                  multiparty:
+                    description: Information about the multi-party system configured
+                      on this namespace
+                    properties:
+                      contract:
+                        description: Information about the multi-party smart contract
+                          configured for this namespace
+                        properties:
+                          active:
+                            description: The currently active FireFly smart contract
+                            properties:
+                              firstEvent:
+                                description: A blockchain specific string, such as
+                                  a block number, to start listening from. The special
+                                  strings 'oldest' and 'newest' are supported by all
+                                  blockchain connectors
+                                type: string
+                              index:
+                                description: The index of this contract in the config
+                                  file
+                                type: integer
+                              info:
+                                description: Additional info about the current status
+                                  of the multi-party contract
+                                properties:
+                                  finalEvent:
+                                    description: The identifier for the final blockchain
+                                      event received from this contract before termination
+                                    type: string
+                                  subscription:
+                                    description: The backend identifier of the subscription
+                                      for the FireFly BatchPin contract
+                                    type: string
+                                type: object
+                              location:
+                                description: A blockchain specific contract identifier.
+                                  For example an Ethereum contract address, or a Fabric
+                                  chaincode name and channel
+                            type: object
+                          terminated:
+                            description: Previously-terminated FireFly smart contracts
+                            items:
+                              description: Previously-terminated FireFly smart contracts
+                              properties:
+                                firstEvent:
+                                  description: A blockchain specific string, such
+                                    as a block number, to start listening from. The
+                                    special strings 'oldest' and 'newest' are supported
+                                    by all blockchain connectors
+                                  type: string
+                                index:
+                                  description: The index of this contract in the config
+                                    file
+                                  type: integer
+                                info:
+                                  description: Additional info about the current status
+                                    of the multi-party contract
+                                  properties:
+                                    finalEvent:
+                                      description: The identifier for the final blockchain
+                                        event received from this contract before termination
+                                      type: string
+                                    subscription:
+                                      description: The backend identifier of the subscription
+                                        for the FireFly BatchPin contract
+                                      type: string
+                                  type: object
+                                location:
+                                  description: A blockchain specific contract identifier.
+                                    For example an Ethereum contract address, or a
+                                    Fabric chaincode name and channel
+                              type: object
+                            type: array
+                        type: object
+                      enabled:
+                        description: Whether multi-party mode is enabled for this
+                          namespace
+                        type: boolean
+                    type: object
                   namespace:
                     description: The namespace that this status applies to
-                    type: string
+                    properties:
+                      created:
+                        description: The time the namespace was created
+                        format: date-time
+                        type: string
+                      description:
+                        description: A description of the namespace
+                        type: string
+                      name:
+                        description: The local namespace name
+                        type: string
+                      remoteName:
+                        description: The namespace name within the multiparty network
+                        type: string
+                    type: object
                   node:
                     description: Details of the local node
                     properties:
@@ -28070,8 +28100,8 @@ paths:
                         type: boolean
                     type: object
                   org:
-                    description: Details of the organization identity that operates
-                      this node
+                    description: Details of the root organization identity registered
+                      for this namespace on the local node
                     properties:
                       did:
                         description: The DID of the organization identity, if registered
@@ -28110,12 +28140,12 @@ paths:
                         type: array
                     type: object
                   plugins:
-                    description: Information about plugins configured on this node
+                    description: Information about plugins configured on this namespace
                     properties:
                       blockchain:
-                        description: The blockchain plugins on this node
+                        description: The blockchain plugins on this namespace
                         items:
-                          description: The blockchain plugins on this node
+                          description: The blockchain plugins on this namespace
                           properties:
                             name:
                               description: The name of the plugin
@@ -28126,9 +28156,9 @@ paths:
                           type: object
                         type: array
                       dataExchange:
-                        description: The data exchange plugins on this node
+                        description: The data exchange plugins on this namespace
                         items:
-                          description: The data exchange plugins on this node
+                          description: The data exchange plugins on this namespace
                           properties:
                             name:
                               description: The name of the plugin
@@ -28139,9 +28169,9 @@ paths:
                           type: object
                         type: array
                       database:
-                        description: The database plugins on this node
+                        description: The database plugins on this namespace
                         items:
-                          description: The database plugins on this node
+                          description: The database plugins on this namespace
                           properties:
                             name:
                               description: The name of the plugin
@@ -28152,9 +28182,9 @@ paths:
                           type: object
                         type: array
                       events:
-                        description: The event plugins on this node
+                        description: The event plugins on this namespace
                         items:
-                          description: The event plugins on this node
+                          description: The event plugins on this namespace
                           properties:
                             name:
                               description: The name of the plugin
@@ -28165,9 +28195,9 @@ paths:
                           type: object
                         type: array
                       identity:
-                        description: The identity plugins on this node
+                        description: The identity plugins on this namespace
                         items:
-                          description: The identity plugins on this node
+                          description: The identity plugins on this namespace
                           properties:
                             name:
                               description: The name of the plugin
@@ -28178,9 +28208,9 @@ paths:
                           type: object
                         type: array
                       sharedStorage:
-                        description: The shared storage plugins on this node
+                        description: The shared storage plugins on this namespace
                         items:
-                          description: The shared storage plugins on this node
+                          description: The shared storage plugins on this namespace
                           properties:
                             name:
                               description: The name of the plugin
@@ -28191,9 +28221,9 @@ paths:
                           type: object
                         type: array
                       tokens:
-                        description: The token plugins on this node
+                        description: The token plugins on this namespace
                         items:
-                          description: The token plugins on this node
+                          description: The token plugins on this namespace
                           properties:
                             name:
                               description: The name of the plugin
@@ -33197,7 +33227,7 @@ paths:
       - Default Namespace
   /websockets:
     get:
-      description: Gets the status of the current WebSocket connections to this node
+      description: Gets a list of the current WebSocket connections to this node
       operationId: getWebSockets
       parameters:
       - description: Server-side request timeout (milliseconds, or set a custom suffix
diff --git a/internal/apiserver/route_get_namespace.go b/internal/apiserver/route_get_namespace.go
index bbc9c49cd..0c9f5f481 100644
--- a/internal/apiserver/route_get_namespace.go
+++ b/internal/apiserver/route_get_namespace.go
@@ -41,7 +41,7 @@ var getNamespace = &ffapi.Route{
 		CoreJSONHandler: func(r *ffapi.APIRequest, cr *coreRequest) (output interface{}, err error) {
 			ns := r.PP["ns"]
 			or := cr.mgr.Orchestrator(ns)
-			return or.GetNamespace(cr.ctx, ns)
+			return or.GetNamespace(cr.ctx), nil
 		},
 	},
 }
diff --git a/internal/apiserver/route_get_status.go b/internal/apiserver/route_get_status.go
index cdcb31ab0..cde10f149 100644
--- a/internal/apiserver/route_get_status.go
+++ b/internal/apiserver/route_get_status.go
@@ -32,7 +32,7 @@ var getStatus = &ffapi.Route{
 	QueryParams:     nil,
 	Description:     coremsgs.APIEndpointsGetStatus,
 	JSONInputValue:  nil,
-	JSONOutputValue: func() interface{} { return &core.NodeStatus{} },
+	JSONOutputValue: func() interface{} { return &core.NamespaceStatus{} },
 	JSONOutputCodes: []int{http.StatusOK},
 	Extensions: &coreExtensions{
 		CoreJSONHandler: func(r *ffapi.APIRequest, cr *coreRequest) (output interface{}, err error) {
diff --git a/internal/apiserver/route_get_websockets.go b/internal/apiserver/route_get_websockets.go
index 542dff34a..265d1d4a0 100644
--- a/internal/apiserver/route_get_websockets.go
+++ b/internal/apiserver/route_get_websockets.go
@@ -32,7 +32,7 @@ var getWebSockets = &ffapi.Route{
 	Method:          http.MethodGet,
 	PathParams:      nil,
 	QueryParams:     nil,
-	Description:     coremsgs.APIEndpointsGetStatusWebSockets,
+	Description:     coremsgs.APIEndpointsGetWebSockets,
 	JSONInputValue:  nil,
 	JSONOutputValue: func() interface{} { return &core.WebSocketStatus{} },
 	JSONOutputCodes: []int{http.StatusOK},
diff --git a/internal/apiserver/route_spi_get_namespace_by_name.go b/internal/apiserver/route_spi_get_namespace_by_name.go
index f879ee4f2..0ea161dd6 100644
--- a/internal/apiserver/route_spi_get_namespace_by_name.go
+++ b/internal/apiserver/route_spi_get_namespace_by_name.go
@@ -40,7 +40,7 @@ var spiGetNamespaceByName = &ffapi.Route{
 		CoreJSONHandler: func(r *ffapi.APIRequest, cr *coreRequest) (output interface{}, err error) {
 			ns := r.PP["ns"]
 			or := cr.mgr.Orchestrator(ns)
-			return or.GetNamespace(cr.ctx, ns)
+			return or.GetNamespace(cr.ctx), nil
 		},
 	},
 }
diff --git a/internal/coremsgs/en_api_translations.go b/internal/coremsgs/en_api_translations.go
index d3149d2ec..c80c218a1 100644
--- a/internal/coremsgs/en_api_translations.go
+++ b/internal/coremsgs/en_api_translations.go
@@ -124,8 +124,8 @@ var (
 	APIEndpointsGetOps                          = ffm("api.endpoints.getOps", "Gets a a list of operations")
 	APIEndpointsGetStatusBatchManager           = ffm("api.endpoints.getStatusBatchManager", "Gets the status of the batch manager")
 	APIEndpointsGetPins                         = ffm("api.endpoints.getPins", "Queries the list of pins received from the blockchain")
-	APIEndpointsGetStatusWebSockets             = ffm("api.endpoints.getStatusWebSockets", "Gets the status of the current WebSocket connections to this node")
-	APIEndpointsGetStatus                       = ffm("api.endpoints.getStatus", "Gets the status of this node")
+	APIEndpointsGetWebSockets                   = ffm("api.endpoints.getStatusWebSockets", "Gets a list of the current WebSocket connections to this node")
+	APIEndpointsGetStatus                       = ffm("api.endpoints.getStatus", "Gets the status of this namespace")
 	APIEndpointsGetSubscriptionByID             = ffm("api.endpoints.getSubscriptionByID", "Gets a subscription by its ID")
 	APIEndpointsGetSubscriptions                = ffm("api.endpoints.getSubscriptions", "Gets a list of subscriptions")
 	APIEndpointsGetTokenAccountPools            = ffm("api.endpoints.getTokenAccountPools", "Gets a list of token pools that contain a given token account key")
diff --git a/internal/coremsgs/en_struct_descriptions.go b/internal/coremsgs/en_struct_descriptions.go
index 696095e0f..c28a6edea 100644
--- a/internal/coremsgs/en_struct_descriptions.go
+++ b/internal/coremsgs/en_struct_descriptions.go
@@ -376,55 +376,61 @@ var (
 	VerifierCreated   = ffm("Verifier.created", "The time this verifier was created on this node")
 
 	// Namespace field descriptions
-	NamespaceID                 = ffm("Namespace.id", "The UUID of the namespace. For locally established namespaces will be different on each node in the network. For broadcast namespaces, will be the same on every node")
-	NamespaceMessage            = ffm("Namespace.message", "The UUID of broadcast message used to establish the namespace. Unset for local namespaces")
-	NamespaceName               = ffm("Namespace.name", "The namespace name")
-	NamespaceDescription        = ffm("Namespace.description", "A description of the namespace")
-	NamespaceType               = ffm("Namespace.type", "The type of the namespace")
-	NamespaceCreated            = ffm("Namespace.created", "The time the namespace was created")
-	NamespaceContract           = ffm("Namespace.fireflyContract", "Info on the FireFly smart contract configured for this namespace")
-	FireFlyContractsActive      = ffm("FireFlyContracts.active", "The currently active FireFly smart contract")
-	FireFlyContractsTerminated  = ffm("FireFlyContracts.terminated", "Previously-terminated FireFly smart contracts")
-	FireFlyContractIndex        = ffm("FireFlyContractInfo.index", "The index of this contract in the config file")
-	FireFlyContractFinalEvent   = ffm("FireFlyContractInfo.finalEvent", "The identifier for the final blockchain event received from this contract before termination")
-	FireFlyContractFirstEvent   = ffm("FireFlyContractInfo.firstEvent", "A blockchain specific string, such as a block number, to start listening from. The special strings 'oldest' and 'newest' are supported by all blockchain connectors")
-	FireFlyContractLocation     = ffm("FireFlyContractInfo.location", "A blockchain specific contract identifier. For example an Ethereum contract address, or a Fabric chaincode name and channel")
-	FireFlyContractSubscription = ffm("FireFlyContractInfo.subscription", "The backend identifier of the subscription for the FireFly BatchPin contract")
-	NetworkActionType           = ffm("NetworkAction.type", "The action to be performed")
-
-	// NodeStatus field descriptions
-	NodeNamespace  = ffm("NodeStatus.namespace", "The namespace that this status applies to")
-	NodeStatusNode = ffm("NodeStatus.node", "Details of the local node")
-	NodeStatusOrg  = ffm("NodeStatus.org", "Details of the organization identity that operates this node")
-	NodePlugins    = ffm("NodeStatus.plugins", "Information about plugins configured on this node")
-
-	// NodeStatusNode field descriptions
-	NodeStatusNodeName       = ffm("NodeStatusNode.name", "The name of this node, as specified in the local configuration")
-	NodeStatusNodeRegistered = ffm("NodeStatusNode.registered", "Whether the node has been successfully registered")
-	NodeStatusNodeID         = ffm("NodeStatusNode.id", "The UUID of the node, if registered")
-
-	// NodeStatusOrg field descriptions
-	NodeStatusOrgName       = ffm("NodeStatusOrg.name", "The name of the node operator organization, as specified in the local configuration")
-	NodeStatusOrgRegistered = ffm("NodeStatusOrg.registered", "Whether the organization has been successfully registered")
-	NodeStatusOrgDID        = ffm("NodeStatusOrg.did", "The DID of the organization identity, if registered")
-	NodeStatusOrgID         = ffm("NodeStatusOrg.id", "The UUID of the organization, if registered")
-	NodeStatusOrgVerifiers  = ffm("NodeStatusOrg.verifiers", "Array of verifiers (blockchain keys) owned by this identity")
-
-	// NodeStatusDefaults field descriptions
-	NodeStatusDefaultsNamespace = ffm("NodeStatusDefaults.namespace", "The default namespace on this node")
-
-	// NodeStatusPlugins field descriptions
-	NodeStatusPluginsBlockchain    = ffm("NodeStatusPlugins.blockchain", "The blockchain plugins on this node")
-	NodeStatusPluginsDatabase      = ffm("NodeStatusPlugins.database", "The database plugins on this node")
-	NodeStatusPluginsDataExchange  = ffm("NodeStatusPlugins.dataExchange", "The data exchange plugins on this node")
-	Events                         = ffm("NodeStatusPlugins.events", "The event plugins on this node")
-	NodeStatusPluginsIdentity      = ffm("NodeStatusPlugins.identity", "The identity plugins on this node")
-	NodeStatusPluginsSharedStorage = ffm("NodeStatusPlugins.sharedStorage", "The shared storage plugins on this node")
-	NodeStatusPluginsTokens        = ffm("NodeStatusPlugins.tokens", "The token plugins on this node")
-
-	// NodeStatusPlugin field descriptions
-	NodeStatusPluginName = ffm("NodeStatusPlugin.name", "The name of the plugin")
-	NodeStatusPluginType = ffm("NodeStatusPlugin.pluginType", "The type of the plugin")
+	NamespaceID                    = ffm("Namespace.id", "The UUID of the namespace. For locally established namespaces will be different on each node in the network. For broadcast namespaces, will be the same on every node")
+	NamespaceMessage               = ffm("Namespace.message", "The UUID of broadcast message used to establish the namespace. Unset for local namespaces")
+	NamespaceName                  = ffm("Namespace.name", "The local namespace name")
+	NamespaceRemoteName            = ffm("Namespace.remoteName", "The namespace name within the multiparty network")
+	NamespaceDescription           = ffm("Namespace.description", "A description of the namespace")
+	NamespaceType                  = ffm("Namespace.type", "The type of the namespace")
+	NamespaceCreated               = ffm("Namespace.created", "The time the namespace was created")
+	MultipartyContractsActive      = ffm("MultipartyContracts.active", "The currently active FireFly smart contract")
+	MultipartyContractsTerminated  = ffm("MultipartyContracts.terminated", "Previously-terminated FireFly smart contracts")
+	MultipartyContractIndex        = ffm("MultipartyContract.index", "The index of this contract in the config file")
+	MultipartyContractFinalEvent   = ffm("MultipartyContract.finalEvent", "The identifier for the final blockchain event received from this contract before termination")
+	MultipartyContractFirstEvent   = ffm("MultipartyContract.firstEvent", "A blockchain specific string, such as a block number, to start listening from. The special strings 'oldest' and 'newest' are supported by all blockchain connectors")
+	MultipartyContractLocation     = ffm("MultipartyContract.location", "A blockchain specific contract identifier. For example an Ethereum contract address, or a Fabric chaincode name and channel")
+	MultipartyContractSubscription = ffm("MultipartyContract.subscription", "The backend identifier of the subscription for the FireFly BatchPin contract")
+	MultipartyContractInfo         = ffm("MultipartyContract.info", "Additional info about the current status of the multi-party contract")
+	NetworkActionType              = ffm("NetworkAction.type", "The action to be performed")
+
+	// NamespaceStatus field descriptions
+	NodeNamespace       = ffm("NamespaceStatus.namespace", "The namespace that this status applies to")
+	NamespaceStatusNode = ffm("NamespaceStatus.node", "Details of the local node")
+	NamespaceStatusOrg  = ffm("NamespaceStatus.org", "Details of the root organization identity registered for this namespace on the local node")
+	NamespacePlugins    = ffm("NamespaceStatus.plugins", "Information about plugins configured on this namespace")
+	NamespaceMultiparty = ffm("NamespaceStatus.multiparty", "Information about the multi-party system configured on this namespace")
+
+	// NamespaceStatusNode field descriptions
+	NamespaceStatusNodeName       = ffm("NamespaceStatusNode.name", "The name of this node, as specified in the local configuration")
+	NamespaceStatusNodeRegistered = ffm("NamespaceStatusNode.registered", "Whether the node has been successfully registered")
+	NamespaceStatusNodeID         = ffm("NamespaceStatusNode.id", "The UUID of the node, if registered")
+
+	// NamespaceStatusOrg field descriptions
+	NamespaceStatusOrgName       = ffm("NamespaceStatusOrg.name", "The name of the node operator organization, as specified in the local configuration")
+	NamespaceStatusOrgRegistered = ffm("NamespaceStatusOrg.registered", "Whether the organization has been successfully registered")
+	NamespaceStatusOrgDID        = ffm("NamespaceStatusOrg.did", "The DID of the organization identity, if registered")
+	NamespaceStatusOrgID         = ffm("NamespaceStatusOrg.id", "The UUID of the organization, if registered")
+	NamespaceStatusOrgVerifiers  = ffm("NamespaceStatusOrg.verifiers", "Array of verifiers (blockchain keys) owned by this identity")
+
+	// NamespaceStatusDefaults field descriptions
+	NamespaceStatusDefaultsNamespace = ffm("NamespaceStatusDefaults.namespace", "The default namespace on this node")
+
+	// NamespaceStatusPlugins field descriptions
+	NamespaceStatusPluginsBlockchain    = ffm("NamespaceStatusPlugins.blockchain", "The blockchain plugins on this namespace")
+	NamespaceStatusPluginsDatabase      = ffm("NamespaceStatusPlugins.database", "The database plugins on this namespace")
+	NamespaceStatusPluginsDataExchange  = ffm("NamespaceStatusPlugins.dataExchange", "The data exchange plugins on this namespace")
+	NamespacePluginsEvents              = ffm("NamespaceStatusPlugins.events", "The event plugins on this namespace")
+	NamespaceStatusPluginsIdentity      = ffm("NamespaceStatusPlugins.identity", "The identity plugins on this namespace")
+	NamespaceStatusPluginsSharedStorage = ffm("NamespaceStatusPlugins.sharedStorage", "The shared storage plugins on this namespace")
+	NamespaceStatusPluginsTokens        = ffm("NamespaceStatusPlugins.tokens", "The token plugins on this namespace")
+
+	// NamespaceStatusPlugin field descriptions
+	NamespaceStatusPluginName = ffm("NamespaceStatusPlugin.name", "The name of the plugin")
+	NamespaceStatusPluginType = ffm("NamespaceStatusPlugin.pluginType", "The type of the plugin")
+
+	// NamespaceStatusMultiparty field descriptions
+	NamespaceMultipartyEnabled  = ffm("NamespaceStatusMultiparty.enabled", "Whether multi-party mode is enabled for this namespace")
+	NamespaceMultipartyContract = ffm("NamespaceStatusMultiparty.contract", "Information about the multi-party smart contract configured for this namespace")
 
 	// BatchManagerStatus field descriptions
 	BatchManagerStatusProcessors = ffm("BatchManagerStatus.processors", "An array of currently active batch processors")
diff --git a/internal/data/data_manager.go b/internal/data/data_manager.go
index b9ffaf5a6..18d60f437 100644
--- a/internal/data/data_manager.go
+++ b/internal/data/data_manager.go
@@ -51,6 +51,7 @@ type Manager interface {
 	UploadBlob(ctx context.Context, inData *core.DataRefOrValue, blob *ffapi.Multipart, autoMeta bool) (*core.Data, error)
 	DownloadBlob(ctx context.Context, dataID string) (*core.Blob, io.ReadCloser, error)
 	HydrateBatch(ctx context.Context, persistedBatch *core.BatchPersisted) (*core.Batch, error)
+	Start()
 	WaitStop()
 }
 
@@ -123,10 +124,13 @@ func NewDataManager(ctx context.Context, ns core.NamespaceRef, di database.Plugi
 		batchTimeout: config.GetDuration(coreconfig.MessageWriterBatchTimeout),
 		maxInserts:   config.GetInt(coreconfig.MessageWriterBatchMaxInserts),
 	})
-	dm.messageWriter.start()
 	return dm, nil
 }
 
+func (dm *dataManager) Start() {
+	dm.messageWriter.start()
+}
+
 func (dm *dataManager) CheckDatatype(ctx context.Context, datatype *core.Datatype) error {
 	_, err := newJSONValidator(ctx, dm.namespace.LocalName, datatype)
 	return err
diff --git a/internal/database/sqlcommon/namespace_sql.go b/internal/database/sqlcommon/namespace_sql.go
index 79526917f..7a228a3b9 100644
--- a/internal/database/sqlcommon/namespace_sql.go
+++ b/internal/database/sqlcommon/namespace_sql.go
@@ -21,20 +21,16 @@ import (
 	"database/sql"
 
 	sq "github.com/Masterminds/squirrel"
-	"github.com/hyperledger/firefly-common/pkg/fftypes"
 	"github.com/hyperledger/firefly-common/pkg/i18n"
 	"github.com/hyperledger/firefly-common/pkg/log"
 	"github.com/hyperledger/firefly/internal/coremsgs"
 	"github.com/hyperledger/firefly/pkg/core"
-	"github.com/hyperledger/firefly/pkg/database"
 )
 
 var (
 	namespaceColumns = []string{
-		"id",
-		"message_id",
-		"ntype",
 		"name",
+		"remote_name",
 		"description",
 		"created",
 		"firefly_contracts",
@@ -54,26 +50,14 @@ func (s *SQLCommon) UpsertNamespace(ctx context.Context, namespace *core.Namespa
 	if allowExisting {
 		// Do a select within the transaction to determine if the UUID already exists
 		namespaceRows, _, err := s.queryTx(ctx, namespacesTable, tx,
-			sq.Select("id").
+			sq.Select("seq").
 				From(namespacesTable).
-				Where(sq.Eq{"name": namespace.Name}),
+				Where(sq.Eq{"name": namespace.LocalName}),
 		)
 		if err != nil {
 			return err
 		}
 		existing = namespaceRows.Next()
-
-		if existing {
-			var id fftypes.UUID
-			_ = namespaceRows.Scan(&id)
-			if namespace.ID != nil {
-				if *namespace.ID != id {
-					namespaceRows.Close()
-					return database.IDMismatch
-				}
-			}
-			namespace.ID = &id // Update on returned object
-		}
 		namespaceRows.Close()
 	}
 
@@ -81,40 +65,27 @@ func (s *SQLCommon) UpsertNamespace(ctx context.Context, namespace *core.Namespa
 		// Update the namespace
 		if _, err = s.updateTx(ctx, namespacesTable, tx,
 			sq.Update(namespacesTable).
-				// Note we do not update ID
-				Set("message_id", namespace.Message).
-				Set("ntype", string(namespace.Type)).
-				Set("name", namespace.Name).
+				Set("remote_name", namespace.RemoteName).
 				Set("description", namespace.Description).
 				Set("created", namespace.Created).
 				Set("firefly_contracts", namespace.Contracts).
-				Where(sq.Eq{"name": namespace.Name}),
-			func() {
-				s.callbacks.UUIDCollectionEvent(database.CollectionNamespaces, core.ChangeEventTypeUpdated, namespace.ID)
-			},
+				Where(sq.Eq{"name": namespace.LocalName}),
+			nil,
 		); err != nil {
 			return err
 		}
 	} else {
-		if namespace.ID == nil {
-			namespace.ID = fftypes.NewUUID()
-		}
-
 		if _, err = s.insertTx(ctx, namespacesTable, tx,
 			sq.Insert(namespacesTable).
 				Columns(namespaceColumns...).
 				Values(
-					namespace.ID,
-					namespace.Message,
-					string(namespace.Type),
-					namespace.Name,
+					namespace.LocalName,
+					namespace.RemoteName,
 					namespace.Description,
 					namespace.Created,
 					namespace.Contracts,
 				),
-			func() {
-				s.callbacks.UUIDCollectionEvent(database.CollectionNamespaces, core.ChangeEventTypeCreated, namespace.ID)
-			},
+			nil,
 		); err != nil {
 			return err
 		}
@@ -126,10 +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.ID,
-		&namespace.Message,
-		&namespace.Type,
-		&namespace.Name,
+		&namespace.LocalName,
+		&namespace.RemoteName,
 		&namespace.Description,
 		&namespace.Created,
 		&namespace.Contracts,
@@ -167,28 +136,3 @@ func (s *SQLCommon) getNamespaceEq(ctx context.Context, eq sq.Eq, textName strin
 func (s *SQLCommon) GetNamespace(ctx context.Context, name string) (message *core.Namespace, err error) {
 	return s.getNamespaceEq(ctx, sq.Eq{"name": name}, name)
 }
-
-func (s *SQLCommon) GetNamespaceByID(ctx context.Context, id *fftypes.UUID) (ns *core.Namespace, err error) {
-	return s.getNamespaceEq(ctx, sq.Eq{"id": id}, id.String())
-}
-
-func (s *SQLCommon) DeleteNamespace(ctx context.Context, id *fftypes.UUID) (err error) {
-
-	ctx, tx, autoCommit, err := s.beginOrUseTx(ctx)
-	if err != nil {
-		return err
-	}
-	defer s.rollbackTx(ctx, tx, autoCommit)
-
-	err = s.deleteTx(ctx, namespacesTable, tx, sq.Delete(namespacesTable).Where(sq.Eq{
-		"id": id,
-	}),
-		func() {
-			s.callbacks.UUIDCollectionEvent(database.CollectionNamespaces, core.ChangeEventTypeDeleted, id)
-		})
-	if err != nil {
-		return err
-	}
-
-	return s.commitTx(ctx, tx, autoCommit)
-}
diff --git a/internal/database/sqlcommon/sqlcommon.go b/internal/database/sqlcommon/sqlcommon.go
index 654796887..ddfc6bedd 100644
--- a/internal/database/sqlcommon/sqlcommon.go
+++ b/internal/database/sqlcommon/sqlcommon.go
@@ -75,12 +75,6 @@ func (cb *callbacks) UUIDCollectionNSEvent(resType database.UUIDCollectionNS, ev
 	}
 }
 
-func (cb *callbacks) UUIDCollectionEvent(resType database.UUIDCollection, eventType core.ChangeEventType, id *fftypes.UUID) {
-	if cb, ok := cb.handlers[database.GlobalHandler]; ok {
-		cb.UUIDCollectionEvent(resType, eventType, id)
-	}
-}
-
 func (cb *callbacks) HashCollectionNSEvent(resType database.HashCollectionNS, eventType core.ChangeEventType, ns string, hash *fftypes.Bytes32) {
 	if cb, ok := cb.handlers[ns]; ok {
 		cb.HashCollectionNSEvent(resType, eventType, ns, hash)
diff --git a/internal/definitions/handler.go b/internal/definitions/handler.go
index 0d7abf2c6..637a0766f 100644
--- a/internal/definitions/handler.go
+++ b/internal/definitions/handler.go
@@ -113,8 +113,6 @@ func (dh *definitionHandler) HandleDefinitionBroadcast(ctx context.Context, stat
 	switch msg.Header.Tag {
 	case core.SystemTagDefineDatatype:
 		return dh.handleDatatypeBroadcast(ctx, state, msg, data, tx)
-	case core.SystemTagDefineNamespace:
-		return dh.handleNamespaceBroadcast(ctx, state, msg, data, tx)
 	case core.DeprecatedSystemTagDefineOrganization:
 		return dh.handleDeprecatedOrganizationBroadcast(ctx, state, msg, data)
 	case core.DeprecatedSystemTagDefineNode:
diff --git a/internal/definitions/handler_namespace.go b/internal/definitions/handler_namespace.go
deleted file mode 100644
index 1bf7ea5f0..000000000
--- a/internal/definitions/handler_namespace.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright © 2022 Kaleido, Inc.
-//
-// SPDX-License-Identifier: Apache-2.0
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package definitions
-
-import (
-	"context"
-
-	"github.com/hyperledger/firefly-common/pkg/fftypes"
-	"github.com/hyperledger/firefly-common/pkg/i18n"
-	"github.com/hyperledger/firefly/internal/coremsgs"
-	"github.com/hyperledger/firefly/pkg/core"
-)
-
-func (dh *definitionHandler) handleNamespaceBroadcast(ctx context.Context, state *core.BatchState, msg *core.Message, data core.DataArray, tx *fftypes.UUID) (HandlerResult, error) {
-	var ns core.Namespace
-	if valid := dh.getSystemBroadcastPayload(ctx, msg, data, &ns); !valid {
-		return HandlerResult{Action: ActionReject}, i18n.NewError(ctx, coremsgs.MsgDefRejectedBadPayload, "namespace", msg.Header.ID)
-	}
-	if err := ns.Validate(ctx, true); err != nil {
-		return HandlerResult{Action: ActionReject}, i18n.NewError(ctx, coremsgs.MsgDefRejectedValidateFail, "namespace", ns.ID, err)
-	}
-
-	existing, err := dh.database.GetNamespace(ctx, ns.Name)
-	if err != nil {
-		return HandlerResult{Action: ActionRetry}, err
-	}
-	if existing != nil {
-		if existing.Type != core.NamespaceTypeLocal {
-			return HandlerResult{Action: ActionReject}, i18n.NewError(ctx, coremsgs.MsgDefRejectedConflict, "namespace", ns.ID, existing.ID)
-		}
-		// Remove the local definition
-		if err = dh.database.DeleteNamespace(ctx, existing.ID); err != nil {
-			return HandlerResult{Action: ActionRetry}, err
-		}
-	}
-
-	if err = dh.database.UpsertNamespace(ctx, &ns, false); err != nil {
-		return HandlerResult{Action: ActionRetry}, err
-	}
-
-	state.AddFinalize(func(ctx context.Context) error {
-		event := core.NewEvent(core.EventTypeNamespaceConfirmed, ns.Name, ns.ID, tx, core.SystemTopicDefinitions)
-		return dh.database.InsertEvent(ctx, event)
-	})
-	return HandlerResult{Action: ActionConfirm}, nil
-}
diff --git a/internal/events/event_manager.go b/internal/events/event_manager.go
index dc8b3cd62..a35b27075 100644
--- a/internal/events/event_manager.go
+++ b/internal/events/event_manager.go
@@ -81,7 +81,7 @@ type EventManager interface {
 	TokensTransferred(ti tokens.Plugin, transfer *tokens.TokenTransfer) error
 	TokensApproved(ti tokens.Plugin, approval *tokens.TokenApproval) error
 
-	GetPlugins() []*core.NodeStatusPlugin
+	GetPlugins() []*core.NamespaceStatusPlugin
 
 	// Internal events
 	sysmessaging.SystemEvents
@@ -256,12 +256,12 @@ func (em *eventManager) AddSystemEventListener(ns string, el system.EventListene
 	return em.internalEvents.AddListener(ns, el)
 }
 
-func (em *eventManager) GetPlugins() []*core.NodeStatusPlugin {
-	eventsArray := make([]*core.NodeStatusPlugin, 0)
+func (em *eventManager) GetPlugins() []*core.NamespaceStatusPlugin {
+	eventsArray := make([]*core.NamespaceStatusPlugin, 0)
 	plugins := em.subManager.transports
 
 	for name := range plugins {
-		eventsArray = append(eventsArray, &core.NodeStatusPlugin{
+		eventsArray = append(eventsArray, &core.NamespaceStatusPlugin{
 			PluginType: name,
 		})
 	}
diff --git a/internal/events/network_action.go b/internal/events/network_action.go
index 7f61e4936..4a8e42df2 100644
--- a/internal/events/network_action.go
+++ b/internal/events/network_action.go
@@ -17,8 +17,6 @@
 package events
 
 import (
-	"context"
-
 	"github.com/hyperledger/firefly-common/pkg/fftypes"
 	"github.com/hyperledger/firefly-common/pkg/log"
 	"github.com/hyperledger/firefly/pkg/blockchain"
@@ -26,20 +24,7 @@ import (
 )
 
 func (em *eventManager) actionTerminate(location *fftypes.JSONAny, event *blockchain.Event) error {
-	namespace, err := em.database.GetNamespace(em.ctx, em.namespace.LocalName)
-	if err != nil {
-		return err
-	}
-	if err := em.multiparty.TerminateContract(em.ctx, &namespace.Contracts, location, event); err != nil {
-		return err
-	}
-	// Currently, a termination event is implied to apply to ALL namespaces
-	return em.database.RunAsGroup(em.ctx, func(ctx context.Context) error {
-		if err := em.database.UpsertNamespace(em.ctx, namespace, true); err != nil {
-			return err
-		}
-		return nil
-	})
+	return em.multiparty.TerminateContract(em.ctx, location, event)
 }
 
 func (em *eventManager) BlockchainNetworkAction(action string, location *fftypes.JSONAny, event *blockchain.Event, signingKey *core.VerifierRef) error {
diff --git a/internal/multiparty/manager.go b/internal/multiparty/manager.go
index b36a7e4b7..c72f2fe33 100644
--- a/internal/multiparty/manager.go
+++ b/internal/multiparty/manager.go
@@ -39,15 +39,15 @@ type Manager interface {
 	RootOrg() RootOrg
 
 	// ConfigureContract initializes the subscription to the FireFly contract
-	// - Checks the provided contract info against the plugin's configuration, and updates it as needed
+	// - Checks the namespace contract info against the plugin's configuration, and updates it as needed
 	// - Initializes the contract info for performing BatchPin transactions, and initializes subscriptions for BatchPin events
-	ConfigureContract(ctx context.Context, contracts *core.FireFlyContracts) (err error)
+	ConfigureContract(ctx context.Context) (err error)
 
 	// TerminateContract marks the given event as the last one to be parsed on the current FireFly contract
 	// - Validates that the event came from the currently active FireFly contract
 	// - Re-initializes the plugin against the next configured FireFly contract
-	// - Updates the provided contract info to record the point of termination and the newly active contract
-	TerminateContract(ctx context.Context, contracts *core.FireFlyContracts, location *fftypes.JSONAny, termination *blockchain.Event) (err error)
+	// - Updates the namespace contract info to record the point of termination and the newly active contract
+	TerminateContract(ctx context.Context, location *fftypes.JSONAny, termination *blockchain.Event) (err error)
 
 	// GetNetworkVersion returns the network version of the active FireFly contract
 	GetNetworkVersion() int
@@ -83,22 +83,17 @@ type Contract struct {
 type multipartyManager struct {
 	ctx            context.Context
 	stop           func()
-	namespace      core.NamespaceRef
+	namespace      *core.Namespace
 	database       database.Plugin
 	blockchain     blockchain.Plugin
 	operations     operations.Manager
 	metrics        metrics.Manager
 	txHelper       txcommon.Helper
 	config         Config
-	activeContract struct {
-		location       *fftypes.JSONAny
-		firstEvent     string
-		networkVersion int
-		subscription   string
-	}
+	networkVersion int
 }
 
-func NewMultipartyManager(ctx context.Context, stop func(), ns core.NamespaceRef, config Config, di database.Plugin, bi blockchain.Plugin, om operations.Manager, mm metrics.Manager, th txcommon.Helper) (Manager, error) {
+func NewMultipartyManager(ctx context.Context, stop func(), ns *core.Namespace, config Config, di database.Plugin, bi blockchain.Plugin, om operations.Manager, mm metrics.Manager, th txcommon.Helper) (Manager, error) {
 	if di == nil || bi == nil || mm == nil || om == nil || th == nil {
 		return nil, i18n.NewError(ctx, coremsgs.MsgInitializationNilDepError, "MultipartyManager")
 	}
@@ -128,7 +123,8 @@ func (mm *multipartyManager) RootOrg() RootOrg {
 	return mm.config.Org
 }
 
-func (mm *multipartyManager) ConfigureContract(ctx context.Context, contracts *core.FireFlyContracts) (err error) {
+func (mm *multipartyManager) ConfigureContract(ctx context.Context) (err error) {
+	contracts := &mm.namespace.Contracts
 	log.L(ctx).Infof("Resolving FireFly contract at index %d", contracts.Active.Index)
 	location, firstEvent, err := mm.resolveFireFlyContract(ctx, contracts.Active.Index)
 	if err != nil {
@@ -140,6 +136,10 @@ func (mm *multipartyManager) ConfigureContract(ctx context.Context, contracts *c
 		return err
 	}
 
+	if !contracts.Active.Location.IsNil() && contracts.Active.Location.String() != location.String() {
+		log.L(ctx).Warnf("FireFly contract location changed from %s to %s", contracts.Active.Location, location)
+	}
+
 	if mm.namespace.LocalName == core.LegacySystemNamespace && version > 1 {
 		// ff_system namespace should stop its orchestrator for network V2+
 		mm.stop()
@@ -148,15 +148,15 @@ func (mm *multipartyManager) ConfigureContract(ctx context.Context, contracts *c
 
 	subID, err := mm.blockchain.AddFireflySubscription(ctx, mm.namespace.LocalName, location, firstEvent)
 	if err == nil {
-		mm.activeContract.location = location
-		mm.activeContract.firstEvent = firstEvent
-		mm.activeContract.networkVersion = version
-		mm.activeContract.subscription = subID
-		contracts.Active = core.FireFlyContractInfo{
-			Location:     location,
-			FirstEvent:   firstEvent,
-			Subscription: subID,
+		mm.networkVersion = version
+		contracts.Active = core.MultipartyContract{
+			Location:   location,
+			FirstEvent: firstEvent,
+			Info: core.MultipartyContractInfo{
+				Subscription: subID,
+			},
 		}
+		err = mm.database.UpsertNamespace(ctx, mm.namespace, true)
 	}
 	return err
 }
@@ -164,7 +164,8 @@ func (mm *multipartyManager) ConfigureContract(ctx context.Context, contracts *c
 func (mm *multipartyManager) resolveFireFlyContract(ctx context.Context, contractIndex int) (location *fftypes.JSONAny, firstEvent string, err error) {
 	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, contractIndex))
+			return nil, "", i18n.NewError(ctx, coremsgs.MsgInvalidFireFlyContractIndex,
+				fmt.Sprintf("%s.multiparty.contracts[%d]", mm.namespace.LocalName, contractIndex))
 		}
 		active := mm.config.Contracts[contractIndex]
 		location = active.Location
@@ -180,22 +181,22 @@ func (mm *multipartyManager) resolveFireFlyContract(ctx context.Context, contrac
 	return location, firstEvent, err
 }
 
-func (mm *multipartyManager) TerminateContract(ctx context.Context, contracts *core.FireFlyContracts, location *fftypes.JSONAny, termination *blockchain.Event) (err error) {
-	// TODO: Investigate if it better to consolidate DB termination here
+func (mm *multipartyManager) TerminateContract(ctx context.Context, location *fftypes.JSONAny, termination *blockchain.Event) (err error) {
+	contracts := &mm.namespace.Contracts
 	if contracts.Active.Location.String() != location.String() {
 		log.L(ctx).Warnf("Ignoring termination event from contract at '%s', which does not match active '%s'", location, contracts.Active.Location)
 		return nil
 	}
 	log.L(ctx).Infof("Processing termination of contract #%d at '%s'", contracts.Active.Index, contracts.Active.Location)
-	contracts.Active.FinalEvent = termination.ProtocolID
+	contracts.Active.Info.FinalEvent = termination.ProtocolID
 	contracts.Terminated = append(contracts.Terminated, contracts.Active)
-	contracts.Active = core.FireFlyContractInfo{Index: contracts.Active.Index + 1}
-	mm.blockchain.RemoveFireflySubscription(ctx, mm.activeContract.subscription)
-	return mm.ConfigureContract(ctx, contracts)
+	contracts.Active = core.MultipartyContract{Index: contracts.Active.Index + 1}
+	mm.blockchain.RemoveFireflySubscription(ctx, contracts.Active.Info.Subscription)
+	return mm.ConfigureContract(ctx)
 }
 
 func (mm *multipartyManager) GetNetworkVersion() int {
-	return mm.activeContract.networkVersion
+	return mm.networkVersion
 }
 
 func (mm *multipartyManager) SubmitNetworkAction(ctx context.Context, signingKey string, action *core.NetworkAction) error {
diff --git a/internal/multiparty/operations.go b/internal/multiparty/operations.go
index cd127726b..8ce97cb05 100644
--- a/internal/multiparty/operations.go
+++ b/internal/multiparty/operations.go
@@ -108,6 +108,7 @@ func (mm *multipartyManager) RunOperation(ctx context.Context, op *core.Prepared
 	switch data := op.Data.(type) {
 	case batchPinData:
 		batch := data.Batch
+		contract := mm.namespace.Contracts.Active
 		return nil, false, mm.blockchain.SubmitBatchPin(ctx, op.NamespacedIDString(), batch.Key, &blockchain.BatchPin{
 			Namespace:       batch.Namespace,
 			TransactionID:   batch.TX.ID,
@@ -115,10 +116,11 @@ func (mm *multipartyManager) RunOperation(ctx context.Context, op *core.Prepared
 			BatchHash:       batch.Hash,
 			BatchPayloadRef: data.PayloadRef,
 			Contexts:        data.Contexts,
-		}, mm.activeContract.location)
+		}, contract.Location)
 
 	case networkActionData:
-		return nil, false, mm.blockchain.SubmitNetworkAction(ctx, op.NamespacedIDString(), data.Key, data.Type, mm.activeContract.location)
+		contract := mm.namespace.Contracts.Active
+		return nil, false, mm.blockchain.SubmitNetworkAction(ctx, op.NamespacedIDString(), data.Key, data.Type, contract.Location)
 
 	default:
 		return nil, false, i18n.NewError(ctx, coremsgs.MsgOperationDataIncorrect, op.Data)
diff --git a/internal/namespace/manager.go b/internal/namespace/manager.go
index ee13325a1..99a880415 100644
--- a/internal/namespace/manager.go
+++ b/internal/namespace/manager.go
@@ -218,10 +218,29 @@ func (nm *namespaceManager) Init(ctx context.Context) (err error) {
 }
 
 func (nm *namespaceManager) initNamespace(name string, ns *namespace) error {
+	stored, err := ns.plugins.Database.Plugin.GetNamespace(nm.ctx, name)
+	switch {
+	case err != nil:
+		return err
+	case stored != nil:
+		stored.RemoteName = ns.remoteName
+		stored.Description = ns.description
+		// TODO: should we check for discrepancies in the multiparty contract config?
+	default:
+		stored = &core.Namespace{
+			LocalName:   name,
+			RemoteName:  ns.remoteName,
+			Description: ns.description,
+			Created:     fftypes.Now(),
+		}
+	}
+	if err = ns.plugins.Database.Plugin.UpsertNamespace(nm.ctx, stored, true); err != nil {
+		return err
+	}
+
 	or := nm.utOrchestrator
 	if or == nil {
-		names := core.NamespaceRef{LocalName: name, RemoteName: ns.remoteName}
-		or = orchestrator.NewOrchestrator(names, ns.config, ns.plugins, nm.metrics)
+		or = orchestrator.NewOrchestrator(stored, ns.config, ns.plugins, nm.metrics)
 	}
 	if err := or.Init(nm.ctx); err != nil {
 		return err
@@ -956,11 +975,8 @@ func (nm *namespaceManager) GetNamespaces(ctx context.Context) ([]*core.Namespac
 	nm.nsMux.Lock()
 	defer nm.nsMux.Unlock()
 	results := make([]*core.Namespace, 0, len(nm.namespaces))
-	for name, ns := range nm.namespaces {
-		results = append(results, &core.Namespace{
-			Name:        name,
-			Description: ns.description,
-		})
+	for _, ns := range nm.namespaces {
+		results = append(results, ns.orchestrator.GetNamespace(ctx))
 	}
 	return results, nil
 }
diff --git a/internal/namespace/persistence_events.go b/internal/namespace/persistence_events.go
index 38772529c..05b597443 100644
--- a/internal/namespace/persistence_events.go
+++ b/internal/namespace/persistence_events.go
@@ -55,14 +55,6 @@ func (nm *namespaceManager) UUIDCollectionNSEvent(resType database.UUIDCollectio
 	})
 }
 
-func (nm *namespaceManager) UUIDCollectionEvent(resType database.UUIDCollection, eventType core.ChangeEventType, id *fftypes.UUID) {
-	nm.adminEvents.Dispatch(&core.ChangeEvent{
-		Collection: string(resType),
-		Type:       eventType,
-		ID:         id,
-	})
-}
-
 func (nm *namespaceManager) HashCollectionNSEvent(resType database.HashCollectionNS, eventType core.ChangeEventType, ns string, hash *fftypes.Bytes32) {
 	nm.adminEvents.Dispatch(&core.ChangeEvent{
 		Collection: string(resType),
diff --git a/internal/orchestrator/data_query.go b/internal/orchestrator/data_query.go
index da5aab502..e9eb76d2d 100644
--- a/internal/orchestrator/data_query.go
+++ b/internal/orchestrator/data_query.go
@@ -27,8 +27,8 @@ import (
 	"github.com/hyperledger/firefly/pkg/database"
 )
 
-func (or *orchestrator) GetNamespace(ctx context.Context, ns string) (*core.Namespace, error) {
-	return or.database().GetNamespace(ctx, ns)
+func (or *orchestrator) GetNamespace(ctx context.Context) *core.Namespace {
+	return or.namespace
 }
 
 func (or *orchestrator) GetTransactionByID(ctx context.Context, id string) (*core.Transaction, error) {
diff --git a/internal/orchestrator/orchestrator.go b/internal/orchestrator/orchestrator.go
index e4cdbb088..912b8fb84 100644
--- a/internal/orchestrator/orchestrator.go
+++ b/internal/orchestrator/orchestrator.go
@@ -69,7 +69,7 @@ type Orchestrator interface {
 	Operations() operations.Manager
 
 	// Status
-	GetStatus(ctx context.Context) (*core.NodeStatus, error)
+	GetStatus(ctx context.Context) (*core.NamespaceStatus, error)
 
 	// Subscription management
 	GetSubscriptions(ctx context.Context, filter database.AndFilter) ([]*core.Subscription, *database.FilterResult, error)
@@ -79,7 +79,7 @@ type Orchestrator interface {
 	DeleteSubscription(ctx context.Context, id string) error
 
 	// Data Query
-	GetNamespace(ctx context.Context, ns string) (*core.Namespace, error)
+	GetNamespace(ctx context.Context) *core.Namespace
 	GetTransactionByID(ctx context.Context, id string) (*core.Transaction, error)
 	GetTransactionOperations(ctx context.Context, id string) ([]*core.Operation, *database.FilterResult, error)
 	GetTransactionBlockchainEvents(ctx context.Context, id string) ([]*core.BlockchainEvent, *database.FilterResult, error)
@@ -179,7 +179,7 @@ type orchestrator struct {
 	cancelCtx      context.CancelFunc
 	onStop         func()
 	started        bool
-	namespace      core.NamespaceRef
+	namespace      *core.Namespace
 	config         Config
 	plugins        Plugins
 	multiparty     multiparty.Manager       // only for multiparty
@@ -203,7 +203,7 @@ type orchestrator struct {
 	txHelper       txcommon.Helper
 }
 
-func NewOrchestrator(ns core.NamespaceRef, config Config, plugins Plugins, metrics metrics.Manager) Orchestrator {
+func NewOrchestrator(ns *core.Namespace, config Config, plugins Plugins, metrics metrics.Manager) Orchestrator {
 	or := &orchestrator{
 		namespace: ns,
 		config:    config,
@@ -257,21 +257,9 @@ func (or *orchestrator) tokens() map[string]tokens.Plugin {
 
 func (or *orchestrator) Start(onStop func()) (err error) {
 	or.onStop = onStop
+	or.data.Start()
 	if or.config.Multiparty.Enabled {
-		var ns *core.Namespace
-		ns, err = or.database().GetNamespace(or.ctx, or.namespace.LocalName)
-		if err == nil {
-			if ns == nil {
-				ns = &core.Namespace{
-					Name:    or.namespace.LocalName,
-					Created: fftypes.Now(),
-				}
-			}
-			err = or.multiparty.ConfigureContract(or.ctx, &ns.Contracts)
-		}
-		if err == nil {
-			err = or.database().UpsertNamespace(or.ctx, ns, true)
-		}
+		err = or.multiparty.ConfigureContract(or.ctx)
 		if err == nil {
 			err = or.batch.Start()
 		}
@@ -288,6 +276,7 @@ func (or *orchestrator) Start(onStop func()) (err error) {
 	if err == nil {
 		err = or.operations.Start()
 	}
+
 	or.started = true
 	return err
 }
@@ -319,6 +308,10 @@ func (or *orchestrator) WaitStop() {
 		or.sharedDownload.WaitStop()
 		or.sharedDownload = nil
 	}
+	if or.events != nil {
+		or.events.WaitStop()
+		or.events = nil
+	}
 	if or.operations != nil {
 		or.operations.WaitStop()
 		or.operations = nil
@@ -447,19 +440,19 @@ func (or *orchestrator) initManagers(ctx context.Context) (err error) {
 		}
 
 		if or.messaging == 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 {
+			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 {
 				return err
 			}
 		}
 
 		if or.broadcast == 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 {
+			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 {
 				return err
 			}
 		}
 
 		if or.sharedDownload == nil {
-			or.sharedDownload, err = shareddownload.NewDownloadManager(ctx, or.namespace, or.database(), or.sharedstorage(), or.dataexchange(), or.operations, &or.bc)
+			or.sharedDownload, err = shareddownload.NewDownloadManager(ctx, or.namespace.Ref(), or.database(), or.sharedstorage(), or.dataexchange(), or.operations, &or.bc)
 			if err != nil {
 				return err
 			}
@@ -501,7 +494,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, or.database(), or.dataexchange())
+		or.data, err = data.NewDataManager(ctx, or.namespace.Ref(), or.database(), or.dataexchange())
 		if err != nil {
 			return err
 		}
@@ -512,7 +505,7 @@ func (or *orchestrator) initComponents(ctx context.Context) (err error) {
 	}
 
 	if or.events == nil {
-		or.events, err = events.NewEventManager(ctx, or.namespace, or, or.database(), or.blockchain(), or.identity, or.defhandler, or.data, or.defsender, or.broadcast, or.messaging, or.assets, or.sharedDownload, or.metrics, or.txHelper, or.plugins.Events, or.multiparty)
+		or.events, err = events.NewEventManager(ctx, or.namespace.Ref(), or, or.database(), or.blockchain(), or.identity, or.defhandler, or.data, or.defsender, or.broadcast, or.messaging, or.assets, or.sharedDownload, or.metrics, or.txHelper, or.plugins.Events, or.multiparty)
 		if err != nil {
 			return err
 		}
diff --git a/internal/orchestrator/persistence_events.go b/internal/orchestrator/persistence_events.go
index 5c6963ca6..eb82a6c84 100644
--- a/internal/orchestrator/persistence_events.go
+++ b/internal/orchestrator/persistence_events.go
@@ -61,10 +61,6 @@ func (or *orchestrator) UUIDCollectionNSEvent(resType database.UUIDCollectionNS,
 	}
 }
 
-func (or *orchestrator) UUIDCollectionEvent(resType database.UUIDCollection, eventType core.ChangeEventType, id *fftypes.UUID) {
-	// do nothing
-}
-
 func (or *orchestrator) HashCollectionNSEvent(resType database.HashCollectionNS, eventType core.ChangeEventType, ns string, hash *fftypes.Bytes32) {
 	// do nothing
 }
diff --git a/internal/orchestrator/status.go b/internal/orchestrator/status.go
index ca5ea4720..033b74535 100644
--- a/internal/orchestrator/status.go
+++ b/internal/orchestrator/status.go
@@ -28,48 +28,48 @@ import (
 	"github.com/hyperledger/firefly/pkg/database"
 )
 
-func (or *orchestrator) getPlugins() core.NodeStatusPlugins {
+func (or *orchestrator) getPlugins() core.NamespaceStatusPlugins {
 	// Plugins can have more than one name, so they must be iterated over
-	tokensArray := make([]*core.NodeStatusPlugin, 0)
+	tokensArray := make([]*core.NamespaceStatusPlugin, 0)
 	for _, plugin := range or.plugins.Tokens {
-		tokensArray = append(tokensArray, &core.NodeStatusPlugin{
+		tokensArray = append(tokensArray, &core.NamespaceStatusPlugin{
 			Name:       plugin.Name,
 			PluginType: plugin.Plugin.Name(),
 		})
 	}
 
-	blockchainsArray := make([]*core.NodeStatusPlugin, 0)
-	blockchainsArray = append(blockchainsArray, &core.NodeStatusPlugin{
+	blockchainsArray := make([]*core.NamespaceStatusPlugin, 0)
+	blockchainsArray = append(blockchainsArray, &core.NamespaceStatusPlugin{
 		Name:       or.plugins.Blockchain.Name,
 		PluginType: or.plugins.Blockchain.Plugin.Name(),
 	})
 
-	databasesArray := make([]*core.NodeStatusPlugin, 0)
-	databasesArray = append(databasesArray, &core.NodeStatusPlugin{
+	databasesArray := make([]*core.NamespaceStatusPlugin, 0)
+	databasesArray = append(databasesArray, &core.NamespaceStatusPlugin{
 		Name:       or.plugins.Database.Name,
 		PluginType: or.plugins.Database.Plugin.Name(),
 	})
 
-	sharedstorageArray := make([]*core.NodeStatusPlugin, 0)
-	sharedstorageArray = append(sharedstorageArray, &core.NodeStatusPlugin{
+	sharedstorageArray := make([]*core.NamespaceStatusPlugin, 0)
+	sharedstorageArray = append(sharedstorageArray, &core.NamespaceStatusPlugin{
 		Name:       or.plugins.SharedStorage.Name,
 		PluginType: or.plugins.SharedStorage.Plugin.Name(),
 	})
 
-	dataexchangeArray := make([]*core.NodeStatusPlugin, 0)
-	dataexchangeArray = append(dataexchangeArray, &core.NodeStatusPlugin{
+	dataexchangeArray := make([]*core.NamespaceStatusPlugin, 0)
+	dataexchangeArray = append(dataexchangeArray, &core.NamespaceStatusPlugin{
 		Name:       or.plugins.DataExchange.Name,
 		PluginType: or.plugins.DataExchange.Plugin.Name(),
 	})
 
-	return core.NodeStatusPlugins{
+	return core.NamespaceStatusPlugins{
 		Blockchain:    blockchainsArray,
 		Database:      databasesArray,
 		SharedStorage: sharedstorageArray,
 		DataExchange:  dataexchangeArray,
 		Events:        or.events.GetPlugins(),
 		Tokens:        tokensArray,
-		Identity:      []*core.NodeStatusPlugin{},
+		Identity:      []*core.NamespaceStatusPlugin{},
 	}
 }
 
@@ -90,21 +90,29 @@ func (or *orchestrator) GetNodeUUID(ctx context.Context) (node *fftypes.UUID) {
 	return or.node
 }
 
-func (or *orchestrator) GetStatus(ctx context.Context) (status *core.NodeStatus, err error) {
+func (or *orchestrator) GetStatus(ctx context.Context) (status *core.NamespaceStatus, err error) {
 
 	org, err := or.identity.GetMultipartyRootOrg(ctx)
 	if err != nil {
 		log.L(ctx).Warnf("Failed to query local org for status: %s", err)
 	}
-	status = &core.NodeStatus{
-		Namespace: or.namespace.LocalName,
-		Node: core.NodeStatusNode{
+
+	status = &core.NamespaceStatus{
+		Namespace: or.namespace,
+		Node: core.NamespaceStatusNode{
 			Name: config.GetString(coreconfig.NodeName),
 		},
-		Org: core.NodeStatusOrg{
+		Org: core.NamespaceStatusOrg{
 			Name: or.config.Multiparty.Org.Name,
 		},
 		Plugins: or.getPlugins(),
+		Multiparty: core.NamespaceStatusMultiparty{
+			Enabled: or.config.Multiparty.Enabled,
+		},
+	}
+
+	if or.config.Multiparty.Enabled {
+		status.Multiparty.Contracts = &or.namespace.Contracts
 	}
 
 	if org != nil {
diff --git a/internal/reference/reference.go b/internal/reference/reference.go
index 97bdc9caf..1171ab1e7 100644
--- a/internal/reference/reference.go
+++ b/internal/reference/reference.go
@@ -501,10 +501,9 @@ func GenerateObjectsReferenceMarkdown(ctx context.Context) (map[string][]byte, e
 		},
 
 		&core.Namespace{
-			ID:          fftypes.MustParseUUID("7b2d9c7e-3d60-452c-a409-05e77c855d3a"),
-			Name:        "default",
+			LocalName:   "default",
+			RemoteName:  "default",
 			Description: "Default predefined namespace",
-			Type:        core.NamespaceTypeLocal,
 			Created:     fftypes.UnixTime(1652664196),
 		},
 
diff --git a/internal/txcommon/event_enrich.go b/internal/txcommon/event_enrich.go
index bec9bf46d..bd0e38973 100644
--- a/internal/txcommon/event_enrich.go
+++ b/internal/txcommon/event_enrich.go
@@ -70,12 +70,6 @@ func (t *transactionHelper) EnrichEvent(ctx context.Context, event *core.Event)
 			return nil, err
 		}
 		e.Identity = identity
-	case core.EventTypeNamespaceConfirmed:
-		ns, err := t.database.GetNamespaceByID(ctx, event.Reference)
-		if err != nil {
-			return nil, err
-		}
-		e.NamespaceDetails = ns
 	case core.EventTypePoolConfirmed:
 		tokenPool, err := t.database.GetTokenPoolByID(ctx, t.namespace, event.Reference)
 		if err != nil {
diff --git a/mocks/databasemocks/callbacks.go b/mocks/databasemocks/callbacks.go
index bbc8124f2..11ad8d2ce 100644
--- a/mocks/databasemocks/callbacks.go
+++ b/mocks/databasemocks/callbacks.go
@@ -31,11 +31,6 @@ func (_m *Callbacks) OrderedUUIDCollectionNSEvent(resType database.OrderedUUIDCo
 	_m.Called(resType, eventType, namespace, id, sequence)
 }
 
-// UUIDCollectionEvent provides a mock function with given fields: resType, eventType, id
-func (_m *Callbacks) UUIDCollectionEvent(resType database.UUIDCollection, eventType core.ChangeEventType, id *fftypes.UUID) {
-	_m.Called(resType, eventType, id)
-}
-
 // UUIDCollectionNSEvent provides a mock function with given fields: resType, eventType, namespace, id
 func (_m *Callbacks) UUIDCollectionNSEvent(resType database.UUIDCollectionNS, eventType core.ChangeEventType, namespace string, id *fftypes.UUID) {
 	_m.Called(resType, eventType, namespace, id)
diff --git a/mocks/databasemocks/plugin.go b/mocks/databasemocks/plugin.go
index 6bb698418..dfa4aa140 100644
--- a/mocks/databasemocks/plugin.go
+++ b/mocks/databasemocks/plugin.go
@@ -65,20 +65,6 @@ func (_m *Plugin) DeleteContractListenerByID(ctx context.Context, namespace stri
 	return r0
 }
 
-// DeleteNamespace provides a mock function with given fields: ctx, id
-func (_m *Plugin) DeleteNamespace(ctx context.Context, id *fftypes.UUID) error {
-	ret := _m.Called(ctx, id)
-
-	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, *fftypes.UUID) error); ok {
-		r0 = rf(ctx, id)
-	} else {
-		r0 = ret.Error(0)
-	}
-
-	return r0
-}
-
 // DeleteNextPin provides a mock function with given fields: ctx, sequence
 func (_m *Plugin) DeleteNextPin(ctx context.Context, sequence int64) error {
 	ret := _m.Called(ctx, sequence)
@@ -1268,29 +1254,6 @@ func (_m *Plugin) GetNamespace(ctx context.Context, name string) (*core.Namespac
 	return r0, r1
 }
 
-// GetNamespaceByID provides a mock function with given fields: ctx, id
-func (_m *Plugin) GetNamespaceByID(ctx context.Context, id *fftypes.UUID) (*core.Namespace, error) {
-	ret := _m.Called(ctx, id)
-
-	var r0 *core.Namespace
-	if rf, ok := ret.Get(0).(func(context.Context, *fftypes.UUID) *core.Namespace); ok {
-		r0 = rf(ctx, id)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*core.Namespace)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, *fftypes.UUID) error); ok {
-		r1 = rf(ctx, id)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
 // GetNextPinByContextAndIdentity provides a mock function with given fields: ctx, _a1, identity
 func (_m *Plugin) GetNextPinByContextAndIdentity(ctx context.Context, _a1 *fftypes.Bytes32, identity string) (*core.NextPin, error) {
 	ret := _m.Called(ctx, _a1, identity)
diff --git a/mocks/datamocks/manager.go b/mocks/datamocks/manager.go
index c857acfef..6d98dc05d 100644
--- a/mocks/datamocks/manager.go
+++ b/mocks/datamocks/manager.go
@@ -220,6 +220,11 @@ func (_m *Manager) ResolveInlineData(ctx context.Context, msg *data.NewMessage)
 	return r0
 }
 
+// Start provides a mock function with given fields:
+func (_m *Manager) Start() {
+	_m.Called()
+}
+
 // UpdateMessageCache provides a mock function with given fields: msg, _a1
 func (_m *Manager) UpdateMessageCache(msg *core.Message, _a1 core.DataArray) {
 	_m.Called(msg, _a1)
diff --git a/mocks/eventmocks/event_manager.go b/mocks/eventmocks/event_manager.go
index 05706a61e..8a5287a9b 100644
--- a/mocks/eventmocks/event_manager.go
+++ b/mocks/eventmocks/event_manager.go
@@ -133,15 +133,15 @@ func (_m *EventManager) DeletedSubscriptions() chan<- *fftypes.UUID {
 }
 
 // GetPlugins provides a mock function with given fields:
-func (_m *EventManager) GetPlugins() []*core.NodeStatusPlugin {
+func (_m *EventManager) GetPlugins() []*core.NamespaceStatusPlugin {
 	ret := _m.Called()
 
-	var r0 []*core.NodeStatusPlugin
-	if rf, ok := ret.Get(0).(func() []*core.NodeStatusPlugin); ok {
+	var r0 []*core.NamespaceStatusPlugin
+	if rf, ok := ret.Get(0).(func() []*core.NamespaceStatusPlugin); ok {
 		r0 = rf()
 	} else {
 		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]*core.NodeStatusPlugin)
+			r0 = ret.Get(0).([]*core.NamespaceStatusPlugin)
 		}
 	}
 
diff --git a/mocks/multipartymocks/manager.go b/mocks/multipartymocks/manager.go
index 91ecd050a..4b3a75d1a 100644
--- a/mocks/multipartymocks/manager.go
+++ b/mocks/multipartymocks/manager.go
@@ -21,13 +21,13 @@ type Manager struct {
 	mock.Mock
 }
 
-// ConfigureContract provides a mock function with given fields: ctx, contracts
-func (_m *Manager) ConfigureContract(ctx context.Context, contracts *core.FireFlyContracts) error {
-	ret := _m.Called(ctx, contracts)
+// ConfigureContract provides a mock function with given fields: ctx
+func (_m *Manager) ConfigureContract(ctx context.Context) error {
+	ret := _m.Called(ctx)
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, *core.FireFlyContracts) error); ok {
-		r0 = rf(ctx, contracts)
+	if rf, ok := ret.Get(0).(func(context.Context) error); ok {
+		r0 = rf(ctx)
 	} else {
 		r0 = ret.Error(0)
 	}
@@ -158,13 +158,13 @@ func (_m *Manager) SubmitNetworkAction(ctx context.Context, signingKey string, a
 	return r0
 }
 
-// TerminateContract provides a mock function with given fields: ctx, contracts, location, termination
-func (_m *Manager) TerminateContract(ctx context.Context, contracts *core.FireFlyContracts, location *fftypes.JSONAny, termination *blockchain.Event) error {
-	ret := _m.Called(ctx, contracts, location, termination)
+// TerminateContract provides a mock function with given fields: ctx, location, termination
+func (_m *Manager) TerminateContract(ctx context.Context, location *fftypes.JSONAny, termination *blockchain.Event) error {
+	ret := _m.Called(ctx, location, termination)
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, *core.FireFlyContracts, *fftypes.JSONAny, *blockchain.Event) error); ok {
-		r0 = rf(ctx, contracts, location, termination)
+	if rf, ok := ret.Get(0).(func(context.Context, *fftypes.JSONAny, *blockchain.Event) error); ok {
+		r0 = rf(ctx, location, termination)
 	} else {
 		r0 = ret.Error(0)
 	}
diff --git a/mocks/orchestratormocks/orchestrator.go b/mocks/orchestratormocks/orchestrator.go
index cddc90c89..48eee1bad 100644
--- a/mocks/orchestratormocks/orchestrator.go
+++ b/mocks/orchestratormocks/orchestrator.go
@@ -799,27 +799,20 @@ func (_m *Orchestrator) GetMessagesWithData(ctx context.Context, filter database
 	return r0, r1, r2
 }
 
-// GetNamespace provides a mock function with given fields: ctx, ns
-func (_m *Orchestrator) GetNamespace(ctx context.Context, ns string) (*core.Namespace, error) {
-	ret := _m.Called(ctx, ns)
+// GetNamespace provides a mock function with given fields: ctx
+func (_m *Orchestrator) GetNamespace(ctx context.Context) *core.Namespace {
+	ret := _m.Called(ctx)
 
 	var r0 *core.Namespace
-	if rf, ok := ret.Get(0).(func(context.Context, string) *core.Namespace); ok {
-		r0 = rf(ctx, ns)
+	if rf, ok := ret.Get(0).(func(context.Context) *core.Namespace); ok {
+		r0 = rf(ctx)
 	} else {
 		if ret.Get(0) != nil {
 			r0 = ret.Get(0).(*core.Namespace)
 		}
 	}
 
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
-		r1 = rf(ctx, ns)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
+	return r0
 }
 
 // GetOperationByID provides a mock function with given fields: ctx, id
@@ -910,15 +903,15 @@ func (_m *Orchestrator) GetPins(ctx context.Context, filter database.AndFilter)
 }
 
 // GetStatus provides a mock function with given fields: ctx
-func (_m *Orchestrator) GetStatus(ctx context.Context) (*core.NodeStatus, error) {
+func (_m *Orchestrator) GetStatus(ctx context.Context) (*core.NamespaceStatus, error) {
 	ret := _m.Called(ctx)
 
-	var r0 *core.NodeStatus
-	if rf, ok := ret.Get(0).(func(context.Context) *core.NodeStatus); ok {
+	var r0 *core.NamespaceStatus
+	if rf, ok := ret.Get(0).(func(context.Context) *core.NamespaceStatus); ok {
 		r0 = rf(ctx)
 	} else {
 		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*core.NodeStatus)
+			r0 = ret.Get(0).(*core.NamespaceStatus)
 		}
 	}
 
diff --git a/pkg/core/constants.go b/pkg/core/constants.go
index 65aa8dd16..22e157148 100644
--- a/pkg/core/constants.go
+++ b/pkg/core/constants.go
@@ -34,9 +34,6 @@ const (
 	// SystemTagDefineDatatype is the tag for messages that broadcast data definitions
 	SystemTagDefineDatatype = "ff_define_datatype"
 
-	// SystemTagDefineNamespace is the tag for messages that broadcast namespace definitions
-	SystemTagDefineNamespace = "ff_define_namespace"
-
 	// DeprecatedSystemTagDefineOrganization is the tag for messages that broadcast organization definitions
 	DeprecatedSystemTagDefineOrganization = "ff_define_organization"
 
diff --git a/pkg/core/event.go b/pkg/core/event.go
index 170b8644f..daf82405f 100644
--- a/pkg/core/event.go
+++ b/pkg/core/event.go
@@ -29,8 +29,6 @@ var (
 	EventTypeMessageConfirmed = fftypes.FFEnumValue("eventtype", "message_confirmed")
 	// EventTypeMessageRejected occurs if a message is received and confirmed from a sequencing perspective, but is rejected as invalid (mismatch to schema, or duplicate system broadcast)
 	EventTypeMessageRejected = fftypes.FFEnumValue("eventtype", "message_rejected")
-	// EventTypeNamespaceConfirmed occurs when a new namespace is ready for use (on the namespace itself)
-	EventTypeNamespaceConfirmed = fftypes.FFEnumValue("eventtype", "namespace_confirmed")
 	// EventTypeDatatypeConfirmed occurs when a new datatype is ready for use (on the namespace of the datatype)
 	EventTypeDatatypeConfirmed = fftypes.FFEnumValue("eventtype", "datatype_confirmed")
 	// EventTypeIdentityConfirmed occurs when a new identity has been confirmed, as as result of a signed claim broadcast, and any associated claim verification
@@ -83,7 +81,6 @@ type EnrichedEvent struct {
 	Datatype          *Datatype        `ffstruct:"EnrichedEvent" json:"datatype,omitempty"`
 	Identity          *Identity        `ffstruct:"EnrichedEvent" json:"identity,omitempty"`
 	Message           *Message         `ffstruct:"EnrichedEvent" json:"message,omitempty"`
-	NamespaceDetails  *Namespace       `ffstruct:"EnrichedEvent" json:"namespaceDetails,omitempty"`
 	TokenApproval     *TokenApproval   `ffstruct:"EnrichedEvent" json:"tokenApproval,omitempty"`
 	TokenPool         *TokenPool       `ffstruct:"EnrichedEvent" json:"tokenPool,omitempty"`
 	TokenTransfer     *TokenTransfer   `ffstruct:"EnrichedEvent" json:"tokenTransfer,omitempty"`
diff --git a/pkg/core/namespace.go b/pkg/core/namespace.go
index a7098d08b..45482f65c 100644
--- a/pkg/core/namespace.go
+++ b/pkg/core/namespace.go
@@ -25,41 +25,31 @@ import (
 	"github.com/hyperledger/firefly-common/pkg/i18n"
 )
 
-// NamespaceType describes when the namespace was created from local configuration, or broadcast through the network
-type NamespaceType = fftypes.FFEnum
-
-var (
-	// NamespaceTypeLocal is a namespace that only exists because it was defined in the local configuration of the node
-	NamespaceTypeLocal = fftypes.FFEnumValue("namespacetype", "local")
-	// NamespaceTypeBroadcast is a namespace that was broadcast through the network. Broadcast namespaces can overwrite a local namespace
-	NamespaceTypeBroadcast = fftypes.FFEnumValue("namespacetype", "broadcast")
-	// NamespaceTypeSystem is a reserved namespace used by FireFly itself
-	NamespaceTypeSystem = fftypes.FFEnumValue("namespacetype", "system")
-)
-
-// Namespace is a isolate set of named resources, to allow multiple applications to co-exist in the same network, with the same named objects.
+// 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 {
-	ID          *fftypes.UUID    `ffstruct:"Namespace" json:"id" ffexcludeinput:"true"`
-	Message     *fftypes.UUID    `ffstruct:"Namespace" json:"message,omitempty" ffexcludeinput:"true"`
-	Name        string           `ffstruct:"Namespace" json:"name"`
-	Description string           `ffstruct:"Namespace" json:"description"`
-	Type        NamespaceType    `ffstruct:"Namespace" json:"type" ffenum:"namespacetype" ffexcludeinput:"true"`
-	Created     *fftypes.FFTime  `ffstruct:"Namespace" json:"created" ffexcludeinput:"true"`
-	Contracts   FireFlyContracts `ffstruct:"Namespace" json:"fireflyContract" ffexcludeinput:"true"`
+	LocalName   string              `ffstruct:"Namespace" json:"name"`
+	RemoteName  string              `ffstruct:"Namespace" json:"remoteName"`
+	Description string              `ffstruct:"Namespace" json:"description"`
+	Created     *fftypes.FFTime     `ffstruct:"Namespace" json:"created" ffexcludeinput:"true"`
+	Contracts   MultipartyContracts `ffstruct:"Namespace" json:"-"`
+}
+
+type MultipartyContracts struct {
+	Active     MultipartyContract   `ffstruct:"MultipartyContracts" json:"active"`
+	Terminated []MultipartyContract `ffstruct:"MultipartyContracts" json:"terminated,omitempty"`
 }
 
-type FireFlyContracts struct {
-	Active     FireFlyContractInfo   `ffstruct:"FireFlyContracts" json:"active"`
-	Terminated []FireFlyContractInfo `ffstruct:"FireFlyContracts" json:"terminated,omitempty"`
+type MultipartyContract struct {
+	Index      int                    `ffstruct:"MultipartyContract" json:"index"`
+	Location   *fftypes.JSONAny       `ffstruct:"MultipartyContract" json:"location,omitempty"`
+	FirstEvent string                 `ffstruct:"MultipartyContract" json:"firstEvent,omitempty"`
+	Info       MultipartyContractInfo `ffstruct:"MultipartyContract" json:"info"`
 }
 
-type FireFlyContractInfo struct {
-	Index        int              `ffstruct:"FireFlyContractInfo" json:"index"`
-	FinalEvent   string           `ffstruct:"FireFlyContractInfo" json:"finalEvent,omitempty"`
-	Location     *fftypes.JSONAny `ffstruct:"FireFlyContractInfo" json:"location,omitempty"`
-	FirstEvent   string           `ffstruct:"FireFlyContractInfo" json:"firstEvent,omitempty"`
-	Subscription string           `ffstruct:"FireFlyContractInfo" json:"subscription,omitempty"`
+type MultipartyContractInfo struct {
+	Subscription string `ffstruct:"MultipartyContract" json:"subscription,omitempty"`
+	FinalEvent   string `ffstruct:"MultipartyContract" json:"finalEvent,omitempty"`
 }
 
 type NamespaceRef struct {
@@ -67,6 +57,13 @@ type NamespaceRef struct {
 	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
 
@@ -79,31 +76,8 @@ type NetworkAction struct {
 	Type NetworkActionType `ffstruct:"NetworkAction" json:"type" ffenum:"networkactiontype"`
 }
 
-func (ns *Namespace) Validate(ctx context.Context, existing bool) (err error) {
-	if err = fftypes.ValidateFFNameField(ctx, ns.Name, "name"); err != nil {
-		return err
-	}
-	if err = fftypes.ValidateLength(ctx, ns.Description, "description", 4096); err != nil {
-		return err
-	}
-	if existing {
-		if ns.ID == nil {
-			return i18n.NewError(ctx, i18n.MsgNilID)
-		}
-	}
-	return nil
-}
-
-func (ns *Namespace) Topic() string {
-	return fftypes.TypeNamespaceNameTopicHash("namespace", ns.Name, "")
-}
-
-func (ns *Namespace) SetBroadcastMessage(msgID *fftypes.UUID) {
-	ns.Message = msgID
-}
-
 // Scan implements sql.Scanner
-func (fc *FireFlyContracts) Scan(src interface{}) error {
+func (fc *MultipartyContracts) Scan(src interface{}) error {
 	switch src := src.(type) {
 	case []byte:
 		if len(src) == 0 {
@@ -118,6 +92,6 @@ func (fc *FireFlyContracts) Scan(src interface{}) error {
 }
 
 // Value implements sql.Valuer
-func (fc FireFlyContracts) Value() (driver.Value, error) {
+func (fc MultipartyContracts) Value() (driver.Value, error) {
 	return json.Marshal(fc)
 }
diff --git a/pkg/core/namespace_status.go b/pkg/core/namespace_status.go
new file mode 100644
index 000000000..9093a38d0
--- /dev/null
+++ b/pkg/core/namespace_status.go
@@ -0,0 +1,66 @@
+// Copyright © 2022 Kaleido, Inc.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package core
+
+import "github.com/hyperledger/firefly-common/pkg/fftypes"
+
+// NamespaceStatus is a set of information that represents the configuration and status of a given namespace
+type NamespaceStatus struct {
+	Namespace  *Namespace                `ffstruct:"NamespaceStatus" json:"namespace"`
+	Node       NamespaceStatusNode       `ffstruct:"NamespaceStatus" json:"node"`
+	Org        NamespaceStatusOrg        `ffstruct:"NamespaceStatus" json:"org"`
+	Plugins    NamespaceStatusPlugins    `ffstruct:"NamespaceStatus" json:"plugins"`
+	Multiparty NamespaceStatusMultiparty `ffstruct:"NamespaceStatus" json:"multiparty"`
+}
+
+// NamespaceStatusNode is the information about the local node, returned in the namespace status
+type NamespaceStatusNode struct {
+	Name       string        `ffstruct:"NamespaceStatusNode" json:"name"`
+	Registered bool          `ffstruct:"NamespaceStatusNode" json:"registered"`
+	ID         *fftypes.UUID `ffstruct:"NamespaceStatusNode" json:"id,omitempty"`
+}
+
+// NamespaceStatusOrg is the information about the node owning org, returned in the namespace status
+type NamespaceStatusOrg struct {
+	Name       string         `ffstruct:"NamespaceStatusOrg" json:"name"`
+	Registered bool           `ffstruct:"NamespaceStatusOrg" json:"registered"`
+	DID        string         `ffstruct:"NamespaceStatusOrg" json:"did,omitempty"`
+	ID         *fftypes.UUID  `ffstruct:"NamespaceStatusOrg" json:"id,omitempty"`
+	Verifiers  []*VerifierRef `ffstruct:"NamespaceStatusOrg" json:"verifiers,omitempty"`
+}
+
+// NamespaceStatusPlugins is a map of plugins configured in the namespace
+type NamespaceStatusPlugins struct {
+	Blockchain    []*NamespaceStatusPlugin `ffstruct:"NamespaceStatusPlugins" json:"blockchain"`
+	Database      []*NamespaceStatusPlugin `ffstruct:"NamespaceStatusPlugins" json:"database"`
+	DataExchange  []*NamespaceStatusPlugin `ffstruct:"NamespaceStatusPlugins" json:"dataExchange"`
+	Events        []*NamespaceStatusPlugin `ffstruct:"NamespaceStatusPlugins" json:"events"`
+	Identity      []*NamespaceStatusPlugin `ffstruct:"NamespaceStatusPlugins" json:"identity"`
+	SharedStorage []*NamespaceStatusPlugin `ffstruct:"NamespaceStatusPlugins" json:"sharedStorage"`
+	Tokens        []*NamespaceStatusPlugin `ffstruct:"NamespaceStatusPlugins" json:"tokens"`
+}
+
+// NamespaceStatusPlugin is information about a plugin
+type NamespaceStatusPlugin struct {
+	Name       string `ffstruct:"NamespaceStatusPlugin" json:"name,omitempty"`
+	PluginType string `ffstruct:"NamespaceStatusPlugin" json:"pluginType"`
+}
+
+type NamespaceStatusMultiparty struct {
+	Enabled   bool                 `ffstruct:"NamespaceStatusMultiparty" json:"enabled"`
+	Contracts *MultipartyContracts `ffstruct:"NamespaceStatusMultiparty" json:"contract,omitempty"`
+}
diff --git a/pkg/core/node_status.go b/pkg/core/node_status.go
deleted file mode 100644
index 09f58c3ae..000000000
--- a/pkg/core/node_status.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright © 2022 Kaleido, Inc.
-//
-// SPDX-License-Identifier: Apache-2.0
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package core
-
-import "github.com/hyperledger/firefly-common/pkg/fftypes"
-
-// NodeStatus is a set of information that represents the health, and identity of a node
-type NodeStatus struct {
-	Namespace string            `ffstruct:"NodeStatus" json:"namespace"`
-	Node      NodeStatusNode    `ffstruct:"NodeStatus" json:"node"`
-	Org       NodeStatusOrg     `ffstruct:"NodeStatus" json:"org"`
-	Plugins   NodeStatusPlugins `ffstruct:"NodeStatus" json:"plugins"`
-}
-
-// NodeStatusNode is the information about the local node, returned in the node status
-type NodeStatusNode struct {
-	Name       string        `ffstruct:"NodeStatusNode" json:"name"`
-	Registered bool          `ffstruct:"NodeStatusNode" json:"registered"`
-	ID         *fftypes.UUID `ffstruct:"NodeStatusNode" json:"id,omitempty"`
-}
-
-// NodeStatusOrg is the information about the node owning org, returned in the node status
-type NodeStatusOrg struct {
-	Name       string         `ffstruct:"NodeStatusOrg" json:"name"`
-	Registered bool           `ffstruct:"NodeStatusOrg" json:"registered"`
-	DID        string         `ffstruct:"NodeStatusOrg" json:"did,omitempty"`
-	ID         *fftypes.UUID  `ffstruct:"NodeStatusOrg" json:"id,omitempty"`
-	Verifiers  []*VerifierRef `ffstruct:"NodeStatusOrg" json:"verifiers,omitempty"`
-}
-
-// NodeStatusPlugins is a map of plugins configured on the node
-type NodeStatusPlugins struct {
-	Blockchain    []*NodeStatusPlugin `ffstruct:"NodeStatusPlugins" json:"blockchain"`
-	Database      []*NodeStatusPlugin `ffstruct:"NodeStatusPlugins" json:"database"`
-	DataExchange  []*NodeStatusPlugin `ffstruct:"NodeStatusPlugins" json:"dataExchange"`
-	Events        []*NodeStatusPlugin `ffstruct:"NodeStatusPlugins" json:"events"`
-	Identity      []*NodeStatusPlugin `ffstruct:"NodeStatusPlugins" json:"identity"`
-	SharedStorage []*NodeStatusPlugin `ffstruct:"NodeStatusPlugins" json:"sharedStorage"`
-	Tokens        []*NodeStatusPlugin `ffstruct:"NodeStatusPlugins" json:"tokens"`
-}
-
-// NodeStatusPlugin is information about a plugin
-type NodeStatusPlugin struct {
-	Name       string `ffstruct:"NodeStatusPlugin" json:"name,omitempty"`
-	PluginType string `ffstruct:"NodeStatusPlugin" json:"pluginType"`
-}
diff --git a/pkg/database/plugin.go b/pkg/database/plugin.go
index 1110d55b0..3beb80d50 100644
--- a/pkg/database/plugin.go
+++ b/pkg/database/plugin.go
@@ -68,17 +68,10 @@ type Plugin interface {
 
 type iNamespaceCollection interface {
 	// UpsertNamespace - Upsert a namespace
-	// Throws IDMismatch error if updating and ids don't match
 	UpsertNamespace(ctx context.Context, data *core.Namespace, allowExisting bool) (err error)
 
-	// DeleteNamespace - Delete namespace
-	DeleteNamespace(ctx context.Context, id *fftypes.UUID) (err error)
-
 	// GetNamespace - Get an namespace by name
 	GetNamespace(ctx context.Context, name string) (namespace *core.Namespace, err error)
-
-	// GetNamespaceByID - Get a namespace by ID
-	GetNamespaceByID(ctx context.Context, id *fftypes.UUID) (namespace *core.Namespace, err error)
 }
 
 type iMessageCollection interface {
@@ -637,13 +630,6 @@ const (
 	CollectionVerifiers HashCollectionNS = "verifiers"
 )
 
-// UUIDCollection is like UUIDCollectionNS, but for objects that do not reside within a namespace
-type UUIDCollection CollectionName
-
-const (
-	CollectionNamespaces UUIDCollection = "namespaces"
-)
-
 // OtherCollection are odd balls, that don't fit any of the categories above.
 // These collections do not support change events, and generally their
 // creation is coordinated with creation of another object that does support change events.
@@ -686,7 +672,6 @@ type Callbacks interface {
 	OrderedUUIDCollectionNSEvent(resType OrderedUUIDCollectionNS, eventType core.ChangeEventType, namespace string, id *fftypes.UUID, sequence int64)
 	OrderedCollectionNSEvent(resType OrderedCollectionNS, eventType core.ChangeEventType, namespace string, sequence int64)
 	UUIDCollectionNSEvent(resType UUIDCollectionNS, eventType core.ChangeEventType, namespace string, id *fftypes.UUID)
-	UUIDCollectionEvent(resType UUIDCollection, eventType core.ChangeEventType, id *fftypes.UUID)
 	HashCollectionNSEvent(resType HashCollectionNS, eventType core.ChangeEventType, namespace string, hash *fftypes.Bytes32)
 }
 


Test patch:
diff --git a/internal/apiserver/route_get_namespace_test.go b/internal/apiserver/route_get_namespace_test.go
index 2b2ca9241..cea653375 100644
--- a/internal/apiserver/route_get_namespace_test.go
+++ b/internal/apiserver/route_get_namespace_test.go
@@ -32,7 +32,7 @@ func TestGetNamespace(t *testing.T) {
 	req.Header.Set("Content-Type", "application/json; charset=utf-8")
 	res := httptest.NewRecorder()
 
-	o.On("GetNamespace", mock.Anything, "ns1").
+	o.On("GetNamespace", mock.Anything).
 		Return(&core.Namespace{}, nil)
 	r.ServeHTTP(res, req)
 
diff --git a/internal/apiserver/route_get_status_test.go b/internal/apiserver/route_get_status_test.go
index 7fc6f87e9..f697e0e33 100644
--- a/internal/apiserver/route_get_status_test.go
+++ b/internal/apiserver/route_get_status_test.go
@@ -33,7 +33,7 @@ func TestGetStatus(t *testing.T) {
 	res := httptest.NewRecorder()
 
 	o.On("GetStatus", mock.Anything).
-		Return(&core.NodeStatus{}, nil)
+		Return(&core.NamespaceStatus{}, nil)
 	r.ServeHTTP(res, req)
 
 	assert.Equal(t, 200, res.Result().StatusCode)
diff --git a/internal/apiserver/route_spi_get_namespace_by_name_test.go b/internal/apiserver/route_spi_get_namespace_by_name_test.go
index 3c6eeb73b..6f5f00cd8 100644
--- a/internal/apiserver/route_spi_get_namespace_by_name_test.go
+++ b/internal/apiserver/route_spi_get_namespace_by_name_test.go
@@ -31,7 +31,7 @@ func TestSPIGetNamespaceByName(t *testing.T) {
 	req.Header.Set("Content-Type", "application/json; charset=utf-8")
 	res := httptest.NewRecorder()
 
-	o.On("GetNamespace", mock.Anything, "ns1").
+	o.On("GetNamespace", mock.Anything).
 		Return(&core.Namespace{}, nil)
 	r.ServeHTTP(res, req)
 
diff --git a/internal/data/data_manager_test.go b/internal/data/data_manager_test.go
index 86477172b..549340133 100644
--- a/internal/data/data_manager_test.go
+++ b/internal/data/data_manager_test.go
@@ -44,6 +44,7 @@ func newTestDataManager(t *testing.T) (*dataManager, context.Context, func()) {
 	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
 	dm, err := NewDataManager(ctx, ns, mdi, mdx)
 	assert.NoError(t, err)
+	dm.Start()
 	return dm.(*dataManager), ctx, func() {
 		cancel()
 		dm.WaitStop()
diff --git a/internal/database/sqlcommon/namespace_sql_test.go b/internal/database/sqlcommon/namespace_sql_test.go
index f65176ee9..fc8dccda4 100644
--- a/internal/database/sqlcommon/namespace_sql_test.go
+++ b/internal/database/sqlcommon/namespace_sql_test.go
@@ -26,9 +26,7 @@ import (
 	"github.com/hyperledger/firefly-common/pkg/fftypes"
 	"github.com/hyperledger/firefly-common/pkg/log"
 	"github.com/hyperledger/firefly/pkg/core"
-	"github.com/hyperledger/firefly/pkg/database"
 	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/mock"
 )
 
 func TestNamespacesE2EWithDB(t *testing.T) {
@@ -40,67 +38,44 @@ func TestNamespacesE2EWithDB(t *testing.T) {
 
 	// Create a new namespace entry
 	namespace := &core.Namespace{
-		ID:      nil, // generated for us
-		Message: fftypes.NewUUID(),
-		Type:    core.NamespaceTypeLocal,
-		Name:    "namespace1",
-		Created: fftypes.Now(),
-		Contracts: core.FireFlyContracts{
-			Active: core.FireFlyContractInfo{
+		LocalName:  "namespace1",
+		RemoteName: "default",
+		Created:    fftypes.Now(),
+		Contracts: core.MultipartyContracts{
+			Active: core.MultipartyContract{
 				Index: 1,
 			},
 		},
 	}
 
-	s.callbacks.On("UUIDCollectionEvent", database.CollectionNamespaces, core.ChangeEventTypeCreated, mock.Anything, mock.Anything).Return()
-
 	err := s.UpsertNamespace(ctx, namespace, true)
 	assert.NoError(t, err)
 
 	// Check we get the exact same namespace back
-	namespaceRead, err := s.GetNamespace(ctx, namespace.Name)
+	namespaceRead, err := s.GetNamespace(ctx, namespace.LocalName)
 	assert.NoError(t, err)
 	assert.NotNil(t, namespaceRead)
 	namespaceJson, _ := json.Marshal(&namespace)
 	namespaceReadJson, _ := json.Marshal(&namespaceRead)
 	assert.Equal(t, string(namespaceJson), string(namespaceReadJson))
 
-	// Rejects attempt to update ID
-	err = s.UpsertNamespace(context.Background(), &core.Namespace{
-		ID:   fftypes.NewUUID(),
-		Name: "namespace1",
-	}, true)
-	assert.Equal(t, database.IDMismatch, err)
-
 	// 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{
-		ID:          nil, // as long as we don't specify one we're fine
-		Message:     fftypes.NewUUID(),
-		Type:        core.NamespaceTypeBroadcast,
-		Name:        "namespace1",
+		LocalName:   "namespace1",
 		Description: "description1",
 		Created:     fftypes.Now(),
 	}
-	s.callbacks.On("UUIDCollectionEvent", database.CollectionNamespaces, core.ChangeEventTypeUpdated, namespace.ID, mock.Anything).Return()
 	err = s.UpsertNamespace(context.Background(), namespaceUpdated, true)
 	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.Name)
+	namespaceRead, err = s.GetNamespace(ctx, namespace.LocalName)
 	assert.NoError(t, err)
 	namespaceJson, _ = json.Marshal(&namespaceUpdated)
 	namespaceReadJson, _ = json.Marshal(&namespaceRead)
 	assert.Equal(t, string(namespaceJson), string(namespaceReadJson))
 
-	// Delete
-	s.callbacks.On("UUIDCollectionEvent", database.CollectionNamespaces, core.ChangeEventTypeDeleted, namespace.ID, mock.Anything).Return()
-	err = s.DeleteNamespace(ctx, namespaceUpdated.ID)
-	assert.NoError(t, err)
-	namespaceRead, err = s.GetNamespace(ctx, namespace.Name)
-	assert.NoError(t, err)
-	assert.Nil(t, namespaceRead)
-
 	s.callbacks.AssertExpectations(t)
 }
 
@@ -117,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{Name: "name1"}, true)
+	err := s.UpsertNamespace(context.Background(), &core.Namespace{LocalName: "name1"}, true)
 	assert.Regexp(t, "FF10115", err)
 	assert.NoError(t, mock.ExpectationsWereMet())
 }
@@ -128,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{Name: "name1"}, true)
+	err := s.UpsertNamespace(context.Background(), &core.Namespace{LocalName: "name1"}, true)
 	assert.Regexp(t, "FF10116", err)
 	assert.NoError(t, mock.ExpectationsWereMet())
 }
@@ -140,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{Name: "name1"}, true)
+	err := s.UpsertNamespace(context.Background(), &core.Namespace{LocalName: "name1"}, true)
 	assert.Regexp(t, "FF10117", err)
 	assert.NoError(t, mock.ExpectationsWereMet())
 }
@@ -151,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{Name: "name1"}, true)
+	err := s.UpsertNamespace(context.Background(), &core.Namespace{LocalName: "name1"}, true)
 	assert.Regexp(t, "FF10119", err)
 	assert.NoError(t, mock.ExpectationsWereMet())
 }
@@ -180,67 +155,3 @@ func TestGetNamespaceByNameScanFail(t *testing.T) {
 	assert.Regexp(t, "FF10121", err)
 	assert.NoError(t, mock.ExpectationsWereMet())
 }
-
-func TestGetNamespaceByIDQueryFail(t *testing.T) {
-	s, mock := newMockProvider().init()
-	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
-	nsID := fftypes.NewUUID()
-	_, err := s.GetNamespaceByID(context.Background(), nsID)
-	assert.Regexp(t, "FF10115", err)
-	assert.NoError(t, mock.ExpectationsWereMet())
-}
-
-func TestGetNamespaceByIDNotFound(t *testing.T) {
-	s, mock := newMockProvider().init()
-	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"ntype", "namespace", "name"}))
-	nsID := fftypes.NewUUID()
-	msg, err := s.GetNamespaceByID(context.Background(), nsID)
-	assert.NoError(t, err)
-	assert.Nil(t, msg)
-	assert.NoError(t, mock.ExpectationsWereMet())
-}
-
-func TestGetNamespaceByIDScanFail(t *testing.T) {
-	s, mock := newMockProvider().init()
-	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"ntype"}).AddRow("only one"))
-	nsID := fftypes.NewUUID()
-	_, err := s.GetNamespaceByID(context.Background(), nsID)
-	assert.Regexp(t, "FF10121", err)
-	assert.NoError(t, mock.ExpectationsWereMet())
-}
-
-func TestGetNamespaceByIDSuccess(t *testing.T) {
-	s, mock := newMockProvider().init()
-	msgID := fftypes.NewUUID()
-	nsID := fftypes.NewUUID()
-	currTime := fftypes.Now()
-	nsMock := &core.Namespace{
-		ID:          nsID,
-		Message:     msgID,
-		Name:        "ns1",
-		Type:        core.NamespaceTypeLocal,
-		Description: "foo",
-		Created:     currTime,
-	}
-	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id", "message", "type", "name", "description", "created", "firefly_contracts"}).AddRow(nsID.String(), msgID.String(), core.NamespaceTypeLocal, "ns1", "foo", currTime.String(), ""))
-	ns, err := s.GetNamespaceByID(context.Background(), nsID)
-	assert.NoError(t, err)
-	assert.Equal(t, nsMock, ns)
-	assert.NoError(t, mock.ExpectationsWereMet())
-}
-
-func TestNamespaceDeleteBeginFail(t *testing.T) {
-	s, mock := newMockProvider().init()
-	mock.ExpectBegin().WillReturnError(fmt.Errorf("pop"))
-	err := s.DeleteNamespace(context.Background(), fftypes.NewUUID())
-	assert.Regexp(t, "FF10114", err)
-}
-
-func TestNamespaceDeleteFail(t *testing.T) {
-	s, mock := newMockProvider().init()
-	mock.ExpectBegin()
-	mock.ExpectExec("DELETE .*").WillReturnError(fmt.Errorf("pop"))
-	mock.ExpectRollback()
-	err := s.DeleteNamespace(context.Background(), fftypes.NewUUID())
-	assert.Regexp(t, "FF10118", err)
-}
diff --git a/internal/database/sqlcommon/sqlcommon_test.go b/internal/database/sqlcommon/sqlcommon_test.go
index 641e9a61b..3c23e50b1 100644
--- a/internal/database/sqlcommon/sqlcommon_test.go
+++ b/internal/database/sqlcommon/sqlcommon_test.go
@@ -377,12 +377,10 @@ func TestNamespaceCallbacks(t *testing.T) {
 	tcb.On("OrderedUUIDCollectionNSEvent", database.CollectionMessages, core.ChangeEventTypeCreated, "ns1", id, int64(1)).Return()
 	tcb.On("OrderedCollectionNSEvent", database.CollectionPins, core.ChangeEventTypeCreated, "ns1", int64(1)).Return()
 	tcb.On("UUIDCollectionNSEvent", database.CollectionOperations, core.ChangeEventTypeCreated, "ns1", id).Return()
-	tcb.On("UUIDCollectionEvent", database.CollectionNamespaces, core.ChangeEventTypeCreated, id).Return()
 	tcb.On("HashCollectionNSEvent", database.CollectionGroups, core.ChangeEventTypeUpdated, "ns1", hash).Return()
 
 	cb.OrderedUUIDCollectionNSEvent(database.CollectionMessages, core.ChangeEventTypeCreated, "ns1", id, 1)
 	cb.OrderedCollectionNSEvent(database.CollectionPins, core.ChangeEventTypeCreated, "ns1", 1)
 	cb.UUIDCollectionNSEvent(database.CollectionOperations, core.ChangeEventTypeCreated, "ns1", id)
-	cb.UUIDCollectionEvent(database.CollectionNamespaces, core.ChangeEventTypeCreated, id)
 	cb.HashCollectionNSEvent(database.CollectionGroups, core.ChangeEventTypeUpdated, "ns1", hash)
 }
diff --git a/internal/definitions/handler_namespace_test.go b/internal/definitions/handler_namespace_test.go
deleted file mode 100644
index b62a91a32..000000000
--- a/internal/definitions/handler_namespace_test.go
+++ /dev/null
@@ -1,283 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// SPDX-License-Identifier: Apache-2.0
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package definitions
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"testing"
-
-	"github.com/hyperledger/firefly-common/pkg/fftypes"
-	"github.com/hyperledger/firefly/mocks/databasemocks"
-	"github.com/hyperledger/firefly/pkg/core"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/mock"
-)
-
-func TestHandleDefinitionBroadcastNSOk(t *testing.T) {
-	dh, bs := newTestDefinitionHandler(t)
-
-	ns := &core.Namespace{
-		ID:   fftypes.NewUUID(),
-		Name: "ns1",
-	}
-	b, err := json.Marshal(&ns)
-	assert.NoError(t, err)
-	data := &core.Data{
-		Value: fftypes.JSONAnyPtrBytes(b),
-	}
-
-	mdi := dh.database.(*databasemocks.Plugin)
-	mdi.On("GetNamespace", mock.Anything, "ns1").Return(nil, nil)
-	mdi.On("UpsertNamespace", mock.Anything, mock.Anything, false).Return(nil)
-	mdi.On("InsertEvent", mock.Anything, mock.Anything).Return(nil)
-	action, err := dh.HandleDefinitionBroadcast(context.Background(), &bs.BatchState, &core.Message{
-		Header: core.MessageHeader{
-			Tag: core.SystemTagDefineNamespace,
-		},
-	}, core.DataArray{data}, fftypes.NewUUID())
-	assert.Equal(t, HandlerResult{Action: ActionConfirm}, action)
-	assert.NoError(t, err)
-	err = bs.RunFinalize(context.Background())
-	assert.NoError(t, err)
-
-	mdi.AssertExpectations(t)
-}
-
-func TestHandleDefinitionBroadcastNSEventFail(t *testing.T) {
-	dh, bs := newTestDefinitionHandler(t)
-
-	ns := &core.Namespace{
-		ID:   fftypes.NewUUID(),
-		Name: "ns1",
-	}
-	b, err := json.Marshal(&ns)
-	assert.NoError(t, err)
-	data := &core.Data{
-		Value: fftypes.JSONAnyPtrBytes(b),
-	}
-
-	mdi := dh.database.(*databasemocks.Plugin)
-	mdi.On("GetNamespace", mock.Anything, "ns1").Return(nil, nil)
-	mdi.On("UpsertNamespace", mock.Anything, mock.Anything, false).Return(nil)
-	mdi.On("InsertEvent", mock.Anything, mock.Anything).Return(fmt.Errorf("pop"))
-	action, err := dh.HandleDefinitionBroadcast(context.Background(), &bs.BatchState, &core.Message{
-		Header: core.MessageHeader{
-			Tag: core.SystemTagDefineNamespace,
-		},
-	}, core.DataArray{data}, fftypes.NewUUID())
-	assert.Equal(t, HandlerResult{Action: ActionConfirm}, action)
-	assert.NoError(t, err)
-	err = bs.RunFinalize(context.Background())
-	assert.EqualError(t, err, "pop")
-
-	mdi.AssertExpectations(t)
-}
-
-func TestHandleDefinitionBroadcastNSUpsertFail(t *testing.T) {
-	dh, bs := newTestDefinitionHandler(t)
-
-	ns := &core.Namespace{
-		ID:   fftypes.NewUUID(),
-		Name: "ns1",
-	}
-	b, err := json.Marshal(&ns)
-	assert.NoError(t, err)
-	data := &core.Data{
-		Value: fftypes.JSONAnyPtrBytes(b),
-	}
-
-	mdi := dh.database.(*databasemocks.Plugin)
-	mdi.On("GetNamespace", mock.Anything, "ns1").Return(nil, nil)
-	mdi.On("UpsertNamespace", mock.Anything, mock.Anything, false).Return(fmt.Errorf("pop"))
-	action, err := dh.HandleDefinitionBroadcast(context.Background(), &bs.BatchState, &core.Message{
-		Header: core.MessageHeader{
-			Tag: core.SystemTagDefineNamespace,
-		},
-	}, core.DataArray{data}, fftypes.NewUUID())
-	assert.Equal(t, HandlerResult{Action: ActionRetry}, action)
-	assert.EqualError(t, err, "pop")
-
-	mdi.AssertExpectations(t)
-	bs.assertNoFinalizers()
-}
-
-func TestHandleDefinitionBroadcastNSMissingData(t *testing.T) {
-	dh, bs := newTestDefinitionHandler(t)
-
-	action, err := dh.HandleDefinitionBroadcast(context.Background(), &bs.BatchState, &core.Message{
-		Header: core.MessageHeader{
-			Tag: core.SystemTagDefineNamespace,
-		},
-	}, core.DataArray{}, fftypes.NewUUID())
-	assert.Equal(t, HandlerResult{Action: ActionReject}, action)
-	assert.Error(t, err)
-	bs.assertNoFinalizers()
-}
-
-func TestHandleDefinitionBroadcastNSBadID(t *testing.T) {
-	dh, bs := newTestDefinitionHandler(t)
-
-	ns := &core.Namespace{}
-	b, err := json.Marshal(&ns)
-	assert.NoError(t, err)
-	data := &core.Data{
-		Value: fftypes.JSONAnyPtrBytes(b),
-	}
-
-	action, err := dh.HandleDefinitionBroadcast(context.Background(), &bs.BatchState, &core.Message{
-		Header: core.MessageHeader{
-			Tag: core.SystemTagDefineNamespace,
-		},
-	}, core.DataArray{data}, fftypes.NewUUID())
-	assert.Equal(t, HandlerResult{Action: ActionReject}, action)
-	assert.Error(t, err)
-	bs.assertNoFinalizers()
-}
-
-func TestHandleDefinitionBroadcastNSBadData(t *testing.T) {
-	dh, bs := newTestDefinitionHandler(t)
-
-	data := &core.Data{
-		Value: fftypes.JSONAnyPtr(`!{json`),
-	}
-
-	action, err := dh.HandleDefinitionBroadcast(context.Background(), &bs.BatchState, &core.Message{
-		Header: core.MessageHeader{
-			Tag: core.SystemTagDefineNamespace,
-		},
-	}, core.DataArray{data}, fftypes.NewUUID())
-	assert.Equal(t, HandlerResult{Action: ActionReject}, action)
-	assert.Error(t, err)
-	bs.assertNoFinalizers()
-}
-
-func TestHandleDefinitionBroadcastDuplicate(t *testing.T) {
-	dh, bs := newTestDefinitionHandler(t)
-
-	ns := &core.Namespace{
-		ID:   fftypes.NewUUID(),
-		Name: "ns1",
-	}
-	b, err := json.Marshal(&ns)
-	assert.NoError(t, err)
-	data := &core.Data{
-		Value: fftypes.JSONAnyPtrBytes(b),
-	}
-
-	mdi := dh.database.(*databasemocks.Plugin)
-	mdi.On("GetNamespace", mock.Anything, "ns1").Return(ns, nil)
-	action, err := dh.HandleDefinitionBroadcast(context.Background(), &bs.BatchState, &core.Message{
-		Header: core.MessageHeader{
-			Tag: core.SystemTagDefineNamespace,
-		},
-	}, core.DataArray{data}, fftypes.NewUUID())
-	assert.Equal(t, HandlerResult{Action: ActionReject}, action)
-	assert.Error(t, err)
-
-	mdi.AssertExpectations(t)
-	bs.assertNoFinalizers()
-}
-
-func TestHandleDefinitionBroadcastDuplicateOverrideLocal(t *testing.T) {
-	dh, bs := newTestDefinitionHandler(t)
-
-	ns := &core.Namespace{
-		ID:   fftypes.NewUUID(),
-		Name: "ns1",
-		Type: core.NamespaceTypeLocal,
-	}
-	b, err := json.Marshal(&ns)
-	assert.NoError(t, err)
-	data := &core.Data{
-		Value: fftypes.JSONAnyPtrBytes(b),
-	}
-
-	mdi := dh.database.(*databasemocks.Plugin)
-	mdi.On("GetNamespace", mock.Anything, "ns1").Return(ns, nil)
-	mdi.On("DeleteNamespace", mock.Anything, mock.Anything).Return(nil)
-	mdi.On("UpsertNamespace", mock.Anything, mock.Anything, false).Return(nil)
-	mdi.On("InsertEvent", mock.Anything, mock.Anything).Return(nil)
-	action, err := dh.HandleDefinitionBroadcast(context.Background(), &bs.BatchState, &core.Message{
-		Header: core.MessageHeader{
-			Tag: core.SystemTagDefineNamespace,
-		},
-	}, core.DataArray{data}, fftypes.NewUUID())
-	assert.Equal(t, HandlerResult{Action: ActionConfirm}, action)
-	assert.NoError(t, err)
-	err = bs.RunFinalize(context.Background())
-	assert.NoError(t, err)
-
-	mdi.AssertExpectations(t)
-}
-
-func TestHandleDefinitionBroadcastDuplicateOverrideLocalFail(t *testing.T) {
-	dh, bs := newTestDefinitionHandler(t)
-
-	ns := &core.Namespace{
-		ID:   fftypes.NewUUID(),
-		Name: "ns1",
-		Type: core.NamespaceTypeLocal,
-	}
-	b, err := json.Marshal(&ns)
-	assert.NoError(t, err)
-	data := &core.Data{
-		Value: fftypes.JSONAnyPtrBytes(b),
-	}
-
-	mdi := dh.database.(*databasemocks.Plugin)
-	mdi.On("GetNamespace", mock.Anything, "ns1").Return(ns, nil)
-	mdi.On("DeleteNamespace", mock.Anything, mock.Anything).Return(fmt.Errorf("pop"))
-	action, err := dh.HandleDefinitionBroadcast(context.Background(), &bs.BatchState, &core.Message{
-		Header: core.MessageHeader{
-			Tag: core.SystemTagDefineNamespace,
-		},
-	}, core.DataArray{data}, fftypes.NewUUID())
-	assert.Equal(t, HandlerResult{Action: ActionRetry}, action)
-	assert.EqualError(t, err, "pop")
-
-	mdi.AssertExpectations(t)
-	bs.assertNoFinalizers()
-}
-
-func TestHandleDefinitionBroadcastDupCheckFail(t *testing.T) {
-	dh, bs := newTestDefinitionHandler(t)
-
-	ns := &core.Namespace{
-		ID:   fftypes.NewUUID(),
-		Name: "ns1",
-	}
-	b, err := json.Marshal(&ns)
-	assert.NoError(t, err)
-	data := &core.Data{
-		Value: fftypes.JSONAnyPtrBytes(b),
-	}
-
-	mdi := dh.database.(*databasemocks.Plugin)
-	mdi.On("GetNamespace", mock.Anything, "ns1").Return(nil, fmt.Errorf("pop"))
-	action, err := dh.HandleDefinitionBroadcast(context.Background(), &bs.BatchState, &core.Message{
-		Header: core.MessageHeader{
-			Tag: core.SystemTagDefineNamespace,
-		},
-	}, core.DataArray{data}, fftypes.NewUUID())
-	assert.Equal(t, HandlerResult{Action: ActionRetry}, action)
-	assert.EqualError(t, err, "pop")
-
-	mdi.AssertExpectations(t)
-	bs.assertNoFinalizers()
-}
diff --git a/internal/definitions/sender_test.go b/internal/definitions/sender_test.go
index 345a36b4c..5ba920a12 100644
--- a/internal/definitions/sender_test.go
+++ b/internal/definitions/sender_test.go
@@ -77,7 +77,7 @@ func TestCreateDefinitionConfirm(t *testing.T) {
 	mms.On("SendAndWait", mock.Anything).Return(nil)
 
 	ds.multiparty = true
-	_, err := ds.sendDefinitionDefault(ds.ctx, &core.Namespace{}, core.SystemTagDefineNamespace, true)
+	_, err := ds.sendDefinitionDefault(ds.ctx, &core.Datatype{}, core.SystemTagDefineDatatype, true)
 	assert.NoError(t, err)
 
 	mim.AssertExpectations(t)
@@ -99,7 +99,7 @@ func TestCreateDatatypeDefinitionAsNodeConfirm(t *testing.T) {
 
 	ds.multiparty = true
 
-	_, err := ds.sendDefinitionDefault(ds.ctx, &core.Datatype{}, core.SystemTagDefineNamespace, true)
+	_, err := ds.sendDefinitionDefault(ds.ctx, &core.Datatype{}, core.SystemTagDefineDatatype, true)
 	assert.NoError(t, err)
 
 	mim.AssertExpectations(t)
@@ -115,9 +115,9 @@ func TestCreateDefinitionBadIdentity(t *testing.T) {
 
 	mim := ds.identity.(*identitymanagermocks.Manager)
 	mim.On("ResolveInputSigningIdentity", mock.Anything, mock.Anything).Return(fmt.Errorf("pop"))
-	_, err := ds.sendDefinition(ds.ctx, &core.Namespace{}, &core.SignerRef{
+	_, err := ds.sendDefinition(ds.ctx, &core.Datatype{}, &core.SignerRef{
 		Author: "wrong",
 		Key:    "wrong",
-	}, core.SystemTagDefineNamespace, false)
+	}, core.SystemTagDefineDatatype, false)
 	assert.Regexp(t, "pop", err)
 }
diff --git a/internal/events/event_manager_test.go b/internal/events/event_manager_test.go
index b2a9753bc..9ecd65a0a 100644
--- a/internal/events/event_manager_test.go
+++ b/internal/events/event_manager_test.go
@@ -423,7 +423,7 @@ func TestAddInternalListener(t *testing.T) {
 func TestGetPlugins(t *testing.T) {
 	em, _ := newTestEventManager(t)
 
-	expectedPlugins := []*core.NodeStatusPlugin{
+	expectedPlugins := []*core.NamespaceStatusPlugin{
 		{
 			PluginType: "websockets",
 		},
diff --git a/internal/events/network_action_test.go b/internal/events/network_action_test.go
index dd78a90c5..026292518 100644
--- a/internal/events/network_action_test.go
+++ b/internal/events/network_action_test.go
@@ -53,9 +53,7 @@ func TestNetworkAction(t *testing.T) {
 		return be.ProtocolID == "0001"
 	})).Return(nil)
 	mdi.On("InsertEvent", em.ctx, mock.Anything).Return(nil)
-	mdi.On("GetNamespace", em.ctx, "ns1").Return(&core.Namespace{}, nil)
-	mdi.On("UpsertNamespace", em.ctx, mock.AnythingOfType("*core.Namespace"), true).Return(nil)
-	mmp.On("TerminateContract", em.ctx, mock.AnythingOfType("*core.FireFlyContracts"), location, mock.AnythingOfType("*blockchain.Event")).Return(nil)
+	mmp.On("TerminateContract", em.ctx, location, mock.AnythingOfType("*blockchain.Event")).Return(nil)
 
 	err := em.BlockchainNetworkAction("terminate", location, event, verifier)
 	assert.NoError(t, err)
@@ -145,22 +143,6 @@ func TestNetworkActionUnknown(t *testing.T) {
 	mii.AssertExpectations(t)
 }
 
-func TestActionTerminateQueryFail(t *testing.T) {
-	em, cancel := newTestEventManager(t)
-	defer cancel()
-
-	location := fftypes.JSONAnyPtr("{}")
-
-	mdi := em.database.(*databasemocks.Plugin)
-
-	mdi.On("GetNamespace", em.ctx, "ns1").Return(nil, fmt.Errorf("pop"))
-
-	err := em.actionTerminate(location, &blockchain.Event{})
-	assert.EqualError(t, err, "pop")
-
-	mdi.AssertExpectations(t)
-}
-
 func TestActionTerminateFail(t *testing.T) {
 	em, cancel := newTestEventManager(t)
 	defer cancel()
@@ -170,28 +152,7 @@ func TestActionTerminateFail(t *testing.T) {
 	mmp := em.multiparty.(*multipartymocks.Manager)
 	mdi := em.database.(*databasemocks.Plugin)
 
-	mdi.On("GetNamespace", em.ctx, "ns1").Return(&core.Namespace{}, nil)
-	mmp.On("TerminateContract", em.ctx, mock.AnythingOfType("*core.FireFlyContracts"), location, mock.AnythingOfType("*blockchain.Event")).Return(fmt.Errorf("pop"))
-
-	err := em.actionTerminate(location, &blockchain.Event{})
-	assert.EqualError(t, err, "pop")
-
-	mmp.AssertExpectations(t)
-	mdi.AssertExpectations(t)
-}
-
-func TestActionTerminateUpsertFail(t *testing.T) {
-	em, cancel := newTestEventManager(t)
-	defer cancel()
-
-	location := fftypes.JSONAnyPtr("{}")
-
-	mmp := em.multiparty.(*multipartymocks.Manager)
-	mdi := em.database.(*databasemocks.Plugin)
-
-	mdi.On("GetNamespace", em.ctx, "ns1").Return(&core.Namespace{}, nil)
-	mdi.On("UpsertNamespace", em.ctx, mock.AnythingOfType("*core.Namespace"), true).Return(fmt.Errorf("pop"))
-	mmp.On("TerminateContract", em.ctx, mock.AnythingOfType("*core.FireFlyContracts"), location, mock.AnythingOfType("*blockchain.Event")).Return(nil)
+	mmp.On("TerminateContract", em.ctx, location, mock.AnythingOfType("*blockchain.Event")).Return(fmt.Errorf("pop"))
 
 	err := em.actionTerminate(location, &blockchain.Event{})
 	assert.EqualError(t, err, "pop")
diff --git a/internal/multiparty/manager_test.go b/internal/multiparty/manager_test.go
index 47a5456a4..c60e7aa62 100644
--- a/internal/multiparty/manager_test.go
+++ b/internal/multiparty/manager_test.go
@@ -58,7 +58,7 @@ func newTestMultipartyManager() *testMultipartyManager {
 		mth: &txcommonmocks.Helper{},
 		multipartyManager: multipartyManager{
 			ctx:       context.Background(),
-			namespace: core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+			namespace: &core.Namespace{LocalName: "ns1", RemoteName: "ns1"},
 		},
 	}
 
@@ -81,7 +81,7 @@ func TestNewMultipartyManager(t *testing.T) {
 		core.OpTypeBlockchainPinBatch,
 		core.OpTypeBlockchainNetworkAction,
 	}).Return()
-	ns := core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"}
+	ns := &core.Namespace{LocalName: "ns1", RemoteName: "ns1"}
 	nm, err := NewMultipartyManager(context.Background(), func() {}, ns, config, mdi, mbi, mom, mmi, mth)
 	assert.NotNil(t, nm)
 	assert.NoError(t, err)
@@ -91,47 +91,66 @@ func TestNewMultipartyManager(t *testing.T) {
 
 func TestInitFail(t *testing.T) {
 	config := Config{Contracts: []Contract{}}
-	_, err := NewMultipartyManager(context.Background(), func() {}, core.NamespaceRef{}, config, nil, nil, nil, nil, nil)
+	_, err := NewMultipartyManager(context.Background(), func() {}, &core.Namespace{}, config, nil, nil, nil, nil, nil)
 	assert.Regexp(t, "FF10128", err)
 }
 
 func TestConfigureContract(t *testing.T) {
-	contracts := make([]Contract, 1)
 	location := fftypes.JSONAnyPtr(fftypes.JSONObject{
 		"address": "0x123",
 	}.String())
-	contract := Contract{
+
+	mp := newTestMultipartyManager()
+	defer mp.cleanup(t)
+
+	mp.mbi.On("GetNetworkVersion", mock.Anything, mock.Anything).Return(1, nil)
+	mp.mbi.On("AddFireflySubscription", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("test", nil)
+	mp.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil)
+
+	mp.multipartyManager.config.Contracts = []Contract{{
 		FirstEvent: "0",
 		Location:   location,
-	}
+	}}
+
+	err := mp.ConfigureContract(context.Background())
+	assert.NoError(t, err)
+}
+
+func TestConfigureContractLocationChanged(t *testing.T) {
+	location := fftypes.JSONAnyPtr(fftypes.JSONObject{
+		"address": "0x123",
+	}.String())
+	location2 := fftypes.JSONAnyPtr(fftypes.JSONObject{
+		"address": "0x456",
+	}.String())
 
-	contracts[0] = contract
 	mp := newTestMultipartyManager()
 	defer mp.cleanup(t)
 
 	mp.mbi.On("GetNetworkVersion", mock.Anything, mock.Anything).Return(1, nil)
 	mp.mbi.On("AddFireflySubscription", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("test", nil)
-	mp.multipartyManager.config.Contracts = contracts
+	mp.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil)
 
-	cf := &core.FireFlyContracts{
-		Active: core.FireFlyContractInfo{Index: 0},
+	mp.multipartyManager.namespace.Contracts = core.MultipartyContracts{
+		Active: core.MultipartyContract{
+			Index:    0,
+			Location: location,
+		},
 	}
+	mp.multipartyManager.config.Contracts = []Contract{{
+		FirstEvent: "0",
+		Location:   location2,
+	}}
 
-	err := mp.ConfigureContract(context.Background(), cf)
+	err := mp.ConfigureContract(context.Background())
 	assert.NoError(t, err)
 }
 
 func TestConfigureContractTerminateSystem(t *testing.T) {
-	contracts := make([]Contract, 1)
 	location := fftypes.JSONAnyPtr(fftypes.JSONObject{
 		"address": "0x123",
 	}.String())
-	contract := Contract{
-		FirstEvent: "0",
-		Location:   location,
-	}
 
-	contracts[0] = contract
 	mp := newTestMultipartyManager()
 	defer mp.cleanup(t)
 	stopped := false
@@ -139,13 +158,16 @@ func TestConfigureContractTerminateSystem(t *testing.T) {
 	mp.namespace.LocalName = core.LegacySystemNamespace
 
 	mp.mbi.On("GetNetworkVersion", mock.Anything, mock.Anything).Return(2, nil)
-	mp.multipartyManager.config.Contracts = contracts
 
-	cf := &core.FireFlyContracts{
-		Active: core.FireFlyContractInfo{Index: 0},
+	mp.multipartyManager.namespace.Contracts = core.MultipartyContracts{
+		Active: core.MultipartyContract{Index: 0},
 	}
+	mp.multipartyManager.config.Contracts = []Contract{{
+		FirstEvent: "0",
+		Location:   location,
+	}}
 
-	err := mp.ConfigureContract(context.Background(), cf)
+	err := mp.ConfigureContract(context.Background())
 	assert.NoError(t, err)
 	assert.True(t, stopped)
 }
@@ -191,73 +213,67 @@ func TestResolveContractDeprecatedConfigNewestBlock(t *testing.T) {
 }
 
 func TestConfigureContractBadIndex(t *testing.T) {
-	contracts := make([]Contract, 1)
 	location := fftypes.JSONAnyPtr(fftypes.JSONObject{
 		"address": "0x123",
 	}.String())
-	contract := Contract{
-		FirstEvent: "0",
-		Location:   location,
-	}
 
-	contracts[0] = contract
 	mp := newTestMultipartyManager()
 	defer mp.cleanup(t)
 
-	mp.multipartyManager.config.Contracts = contracts
-
-	cf := &core.FireFlyContracts{
-		Active: core.FireFlyContractInfo{Index: 1},
+	mp.multipartyManager.namespace.Contracts = core.MultipartyContracts{
+		Active: core.MultipartyContract{Index: 1},
 	}
+	mp.multipartyManager.config.Contracts = []Contract{{
+		FirstEvent: "0",
+		Location:   location,
+	}}
 
-	err := mp.ConfigureContract(context.Background(), cf)
+	err := mp.ConfigureContract(context.Background())
 	assert.Regexp(t, "FF10396", err)
 }
 
 func TestConfigureContractNetworkVersionFail(t *testing.T) {
-	contracts := make([]Contract, 1)
 	location := fftypes.JSONAnyPtr(fftypes.JSONObject{
 		"address": "0x123",
 	}.String())
-	contract := Contract{
-		FirstEvent: "0",
-		Location:   location,
-	}
 
-	contracts[0] = contract
 	mp := newTestMultipartyManager()
 	defer mp.cleanup(t)
 
 	mp.mbi.On("GetNetworkVersion", mock.Anything, mock.Anything).Return(0, fmt.Errorf("pop"))
-	mp.multipartyManager.config.Contracts = contracts
 
-	cf := &core.FireFlyContracts{
-		Active: core.FireFlyContractInfo{Index: 0},
+	mp.multipartyManager.namespace.Contracts = core.MultipartyContracts{
+		Active: core.MultipartyContract{Index: 0},
 	}
+	mp.multipartyManager.config.Contracts = []Contract{{
+		FirstEvent: "0",
+		Location:   location,
+	}}
 
-	err := mp.ConfigureContract(context.Background(), cf)
+	err := mp.ConfigureContract(context.Background())
 	assert.Regexp(t, "pop", err)
 }
 
 func TestSubmitNetworkAction(t *testing.T) {
-	contracts := make([]Contract, 1)
 	location := fftypes.JSONAnyPtr(fftypes.JSONObject{
 		"address": "0x123",
 	}.String())
-	contract := Contract{
-		FirstEvent: "0",
-		Location:   location,
-	}
 	txid := fftypes.NewUUID()
 
-	contracts[0] = contract
 	mp := newTestMultipartyManager()
 	defer mp.cleanup(t)
 
-	mp.multipartyManager.config.Contracts = contracts
+	mp.multipartyManager.namespace.Contracts = core.MultipartyContracts{
+		Active: core.MultipartyContract{Index: 0},
+	}
+	mp.multipartyManager.config.Contracts = []Contract{{
+		FirstEvent: "0",
+		Location:   location,
+	}}
 
 	mp.mbi.On("GetNetworkVersion", mock.Anything, mock.Anything).Return(1, nil)
 	mp.mbi.On("AddFireflySubscription", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("test", nil)
+	mp.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil)
 	mp.mth.On("SubmitNewTransaction", mock.Anything, core.TransactionTypeNetworkAction).Return(txid, nil)
 	mp.mbi.On("Name").Return("ut")
 	mp.mom.On("AddOrReuseOperation", context.Background(), mock.MatchedBy(func(op *core.Operation) bool {
@@ -272,11 +288,7 @@ func TestSubmitNetworkAction(t *testing.T) {
 		return op.Type == core.OpTypeBlockchainNetworkAction && data.Type == core.NetworkActionTerminate
 	})).Return(nil, nil)
 
-	cf := &core.FireFlyContracts{
-		Active: core.FireFlyContractInfo{Index: 0},
-	}
-
-	err := mp.ConfigureContract(context.Background(), cf)
+	err := mp.ConfigureContract(context.Background())
 	assert.NoError(t, err)
 	err = mp.SubmitNetworkAction(context.Background(), "0x123", &core.NetworkAction{Type: core.NetworkActionTerminate})
 	assert.Nil(t, err)
@@ -287,30 +299,27 @@ func TestSubmitNetworkAction(t *testing.T) {
 }
 
 func TestSubmitNetworkActionTXFail(t *testing.T) {
-	contracts := make([]Contract, 1)
 	location := fftypes.JSONAnyPtr(fftypes.JSONObject{
 		"address": "0x123",
 	}.String())
-	contract := Contract{
-		FirstEvent: "0",
-		Location:   location,
-	}
 
-	contracts[0] = contract
 	mp := newTestMultipartyManager()
 	defer mp.cleanup(t)
 
-	mp.multipartyManager.config.Contracts = contracts
+	mp.multipartyManager.namespace.Contracts = core.MultipartyContracts{
+		Active: core.MultipartyContract{Index: 0},
+	}
+	mp.multipartyManager.config.Contracts = []Contract{{
+		FirstEvent: "0",
+		Location:   location,
+	}}
 
 	mp.mbi.On("GetNetworkVersion", mock.Anything, mock.Anything).Return(1, nil)
 	mp.mbi.On("AddFireflySubscription", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("test", nil)
+	mp.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil)
 	mp.mth.On("SubmitNewTransaction", mock.Anything, core.TransactionTypeNetworkAction).Return(nil, fmt.Errorf("pop"))
 
-	cf := &core.FireFlyContracts{
-		Active: core.FireFlyContractInfo{Index: 0},
-	}
-
-	err := mp.ConfigureContract(context.Background(), cf)
+	err := mp.ConfigureContract(context.Background())
 	assert.NoError(t, err)
 	err = mp.SubmitNetworkAction(context.Background(), "0x123", &core.NetworkAction{Type: core.NetworkActionTerminate})
 	assert.EqualError(t, err, "pop")
@@ -320,33 +329,30 @@ func TestSubmitNetworkActionTXFail(t *testing.T) {
 }
 
 func TestSubmitNetworkActionOpFail(t *testing.T) {
-	contracts := make([]Contract, 1)
 	location := fftypes.JSONAnyPtr(fftypes.JSONObject{
 		"address": "0x123",
 	}.String())
-	contract := Contract{
-		FirstEvent: "0",
-		Location:   location,
-	}
 	txid := fftypes.NewUUID()
 
-	contracts[0] = contract
 	mp := newTestMultipartyManager()
 	defer mp.cleanup(t)
 
-	mp.multipartyManager.config.Contracts = contracts
+	mp.multipartyManager.namespace.Contracts = core.MultipartyContracts{
+		Active: core.MultipartyContract{Index: 0},
+	}
+	mp.multipartyManager.config.Contracts = []Contract{{
+		FirstEvent: "0",
+		Location:   location,
+	}}
 
 	mp.mbi.On("GetNetworkVersion", mock.Anything, mock.Anything).Return(1, nil)
 	mp.mbi.On("AddFireflySubscription", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("test", nil)
+	mp.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil)
 	mp.mth.On("SubmitNewTransaction", mock.Anything, core.TransactionTypeNetworkAction).Return(txid, nil)
 	mp.mbi.On("Name").Return("ut")
 	mp.mom.On("AddOrReuseOperation", context.Background(), mock.Anything).Return(fmt.Errorf("pop"))
 
-	cf := &core.FireFlyContracts{
-		Active: core.FireFlyContractInfo{Index: 0},
-	}
-
-	err := mp.ConfigureContract(context.Background(), cf)
+	err := mp.ConfigureContract(context.Background())
 	assert.NoError(t, err)
 	err = mp.SubmitNetworkAction(context.Background(), "0x123", &core.NetworkAction{Type: core.NetworkActionTerminate})
 	assert.EqualError(t, err, "pop")
@@ -357,28 +363,26 @@ func TestSubmitNetworkActionOpFail(t *testing.T) {
 }
 
 func TestSubmitNetworkActionBadType(t *testing.T) {
-	contracts := make([]Contract, 1)
 	location := fftypes.JSONAnyPtr(fftypes.JSONObject{
 		"address": "0x123",
 	}.String())
-	contract := Contract{
-		FirstEvent: "0",
-		Location:   location,
-	}
 
-	contracts[0] = contract
 	mp := newTestMultipartyManager()
 	defer mp.cleanup(t)
 
 	mp.mbi.On("GetNetworkVersion", mock.Anything, mock.Anything).Return(1, nil)
 	mp.mbi.On("AddFireflySubscription", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("test", nil)
-	mp.multipartyManager.config.Contracts = contracts
+	mp.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil)
 
-	cf := &core.FireFlyContracts{
-		Active: core.FireFlyContractInfo{Index: 0},
+	mp.multipartyManager.namespace.Contracts = core.MultipartyContracts{
+		Active: core.MultipartyContract{Index: 0},
 	}
+	mp.multipartyManager.config.Contracts = []Contract{{
+		FirstEvent: "0",
+		Location:   location,
+	}}
 
-	err := mp.ConfigureContract(context.Background(), cf)
+	err := mp.ConfigureContract(context.Background())
 	assert.NoError(t, err)
 	err = mp.SubmitNetworkAction(context.Background(), "0x123", &core.NetworkAction{Type: "BAD"})
 	assert.Regexp(t, "FF10397", err)
@@ -487,28 +491,26 @@ func TestSubmitPinnedBatchOpFail(t *testing.T) {
 }
 
 func TestGetNetworkVersion(t *testing.T) {
-	contracts := make([]Contract, 1)
 	location := fftypes.JSONAnyPtr(fftypes.JSONObject{
 		"address": "0x123",
 	}.String())
-	contract := Contract{
-		FirstEvent: "0",
-		Location:   location,
-	}
 
-	contracts[0] = contract
 	mp := newTestMultipartyManager()
 	defer mp.cleanup(t)
 
 	mp.mbi.On("GetNetworkVersion", mock.Anything, mock.Anything).Return(1, nil)
 	mp.mbi.On("AddFireflySubscription", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("test", nil)
-	mp.multipartyManager.config.Contracts = contracts
+	mp.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil)
 
-	cf := &core.FireFlyContracts{
-		Active: core.FireFlyContractInfo{Index: 0},
+	mp.multipartyManager.namespace.Contracts = core.MultipartyContracts{
+		Active: core.MultipartyContract{Index: 0},
 	}
+	mp.multipartyManager.config.Contracts = []Contract{{
+		FirstEvent: "0",
+		Location:   location,
+	}}
 
-	err := mp.ConfigureContract(context.Background(), cf)
+	err := mp.ConfigureContract(context.Background())
 	assert.NoError(t, err)
 
 	version := mp.GetNetworkVersion()
@@ -516,36 +518,33 @@ func TestGetNetworkVersion(t *testing.T) {
 }
 
 func TestConfgureAndTerminateContract(t *testing.T) {
-	contracts := make([]Contract, 2)
 	location := fftypes.JSONAnyPtr(fftypes.JSONObject{
 		"address": "0x123",
 	}.String())
-	contract := Contract{
-		FirstEvent: "0",
-		Location:   location,
-	}
-	contract2 := Contract{
-		FirstEvent: "0",
-		Location:   location,
-	}
 
-	contracts[0] = contract
-	contracts[1] = contract2
 	mp := newTestMultipartyManager()
 	defer mp.cleanup(t)
 
 	mp.mbi.On("GetNetworkVersion", mock.Anything, mock.Anything).Return(1, nil)
 	mp.mbi.On("AddFireflySubscription", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("test", nil)
+	mp.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil)
 	mp.mbi.On("RemoveFireflySubscription", mock.Anything, mock.Anything).Return(nil)
-	mp.multipartyManager.config.Contracts = contracts
 
-	cf := &core.FireFlyContracts{
-		Active: core.FireFlyContractInfo{Index: 0},
+	mp.multipartyManager.namespace.Contracts = core.MultipartyContracts{
+		Active: core.MultipartyContract{Index: 0},
 	}
-	err := mp.ConfigureContract(context.Background(), cf)
+	mp.multipartyManager.config.Contracts = []Contract{{
+		FirstEvent: "0",
+		Location:   location,
+	}, {
+		FirstEvent: "0",
+		Location:   location,
+	}}
+
+	err := mp.ConfigureContract(context.Background())
 	assert.NoError(t, err)
 
-	err = mp.TerminateContract(context.Background(), cf, location, &blockchain.Event{})
+	err = mp.TerminateContract(context.Background(), location, &blockchain.Event{})
 	assert.NoError(t, err)
 }
 
@@ -558,11 +557,12 @@ func TestTerminateContractError(t *testing.T) {
 	location := fftypes.JSONAnyPtr(fftypes.JSONObject{
 		"address": "0x123",
 	}.String())
-	cf := &core.FireFlyContracts{
-		Active: core.FireFlyContractInfo{Index: 0, Location: location},
+
+	mp.multipartyManager.namespace.Contracts = core.MultipartyContracts{
+		Active: core.MultipartyContract{Index: 0, Location: location},
 	}
 
-	err := mp.TerminateContract(context.Background(), cf, location, &blockchain.Event{})
+	err := mp.TerminateContract(context.Background(), location, &blockchain.Event{})
 	assert.Regexp(t, "FF10396", err)
 }
 
@@ -573,10 +573,11 @@ func TestTerminateContractWrongAddress(t *testing.T) {
 	location := fftypes.JSONAnyPtr(fftypes.JSONObject{
 		"address": "0x123",
 	}.String())
-	cf := &core.FireFlyContracts{
-		Active: core.FireFlyContractInfo{Index: 0, Location: location},
+
+	mp.multipartyManager.namespace.Contracts = core.MultipartyContracts{
+		Active: core.MultipartyContract{Index: 0, Location: location},
 	}
 
-	err := mp.TerminateContract(context.Background(), cf, fftypes.JSONAnyPtr("{}"), &blockchain.Event{})
+	err := mp.TerminateContract(context.Background(), fftypes.JSONAnyPtr("{}"), &blockchain.Event{})
 	assert.NoError(t, err)
 }
diff --git a/internal/multiparty/operations_test.go b/internal/multiparty/operations_test.go
index 406bec72e..185e712b4 100644
--- a/internal/multiparty/operations_test.go
+++ b/internal/multiparty/operations_test.go
@@ -178,7 +178,7 @@ func TestPrepareOperationBatchPinNotFound(t *testing.T) {
 func TestRunBatchPinV1(t *testing.T) {
 	mp := newTestMultipartyManager()
 	defer mp.cleanup(t)
-	mp.activeContract.networkVersion = 1
+	mp.networkVersion = 1
 
 	op := &core.Operation{
 		Type:      core.OpTypeBlockchainPinBatch,
diff --git a/internal/namespace/manager_test.go b/internal/namespace/manager_test.go
index 49854e736..95381ab2b 100644
--- a/internal/namespace/manager_test.go
+++ b/internal/namespace/manager_test.go
@@ -31,6 +31,7 @@ import (
 	"github.com/hyperledger/firefly/internal/database/difactory"
 	"github.com/hyperledger/firefly/internal/dataexchange/dxfactory"
 	"github.com/hyperledger/firefly/internal/identity/iifactory"
+	"github.com/hyperledger/firefly/internal/orchestrator"
 	"github.com/hyperledger/firefly/internal/sharedstorage/ssfactory"
 	"github.com/hyperledger/firefly/internal/tokens/tifactory"
 	"github.com/hyperledger/firefly/mocks/blockchainmocks"
@@ -152,6 +153,8 @@ func TestInit(t *testing.T) {
 	nm.mps.On("Init", mock.Anything, mock.Anything).Return(nil)
 	nm.mti.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil)
+	nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil)
+	nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil)
 	nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 
 	err := nm.Init(context.Background())
@@ -290,6 +293,8 @@ func TestInitOrchestratorFail(t *testing.T) {
 	nm.mps.On("SetHandler", "default", mock.Anything).Return()
 	nm.mti.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 	nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil)
+	nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil)
+	nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil)
 	nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 
 	err := nm.Init(context.Background())
@@ -319,6 +324,10 @@ func TestInitVersion1(t *testing.T) {
 	nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil)
 	nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 
+	nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil)
+	nm.mdi.On("GetNamespace", mock.Anything, core.LegacySystemNamespace).Return(nil, nil)
+	nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil)
+
 	err := nm.Init(context.Background())
 	assert.NoError(t, err)
 
@@ -352,12 +361,53 @@ func TestInitVersion1Fail(t *testing.T) {
 	nm.mev.On("Init", mock.Anything, mock.Anything).Return(nil)
 	nm.auth.On("Init", mock.Anything, mock.Anything, mock.Anything).Return(nil)
 
+	nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, nil)
+	nm.mdi.On("GetNamespace", mock.Anything, core.LegacySystemNamespace).Return(nil, nil)
+	nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(nil)
+
 	err := nm.Init(context.Background())
 	assert.EqualError(t, err, "pop")
 
 	mo.AssertExpectations(t)
 }
 
+func TestInitNamespaceQueryFail(t *testing.T) {
+	nm := newTestNamespaceManager(true)
+	defer nm.cleanup(t)
+
+	ns := &namespace{
+		plugins: orchestrator.Plugins{
+			Database: orchestrator.DatabasePlugin{
+				Plugin: nm.mdi,
+			},
+		},
+	}
+
+	nm.mdi.On("GetNamespace", mock.Anything, "default").Return(nil, fmt.Errorf("pop"))
+
+	err := nm.initNamespace("default", ns)
+	assert.EqualError(t, err, "pop")
+}
+
+func TestInitNamespaceExistingUpsertFail(t *testing.T) {
+	nm := newTestNamespaceManager(true)
+	defer nm.cleanup(t)
+
+	ns := &namespace{
+		plugins: orchestrator.Plugins{
+			Database: orchestrator.DatabasePlugin{
+				Plugin: nm.mdi,
+			},
+		},
+	}
+
+	nm.mdi.On("GetNamespace", mock.Anything, "default").Return(&core.Namespace{}, nil)
+	nm.mdi.On("UpsertNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true).Return(fmt.Errorf("pop"))
+
+	err := nm.initNamespace("default", ns)
+	assert.EqualError(t, err, "pop")
+}
+
 func TestDeprecatedDatabasePlugin(t *testing.T) {
 	nm := newTestNamespaceManager(true)
 	defer nm.cleanup(t)
@@ -1419,10 +1469,15 @@ func TestGetNamespaces(t *testing.T) {
 	nm := newTestNamespaceManager(true)
 	defer nm.cleanup(t)
 
+	mo := &orchestratormocks.Orchestrator{}
 	nm.namespaces = map[string]*namespace{
-		"default": {},
+		"default": {
+			orchestrator: mo,
+		},
 	}
 
+	mo.On("GetNamespace", context.Background()).Return(&core.Namespace{})
+
 	results, err := nm.GetNamespaces(context.Background())
 	assert.Nil(t, err)
 	assert.Len(t, results, 1)
diff --git a/internal/namespace/persistence_events_test.go b/internal/namespace/persistence_events_test.go
index 4746b38a1..081322eae 100644
--- a/internal/namespace/persistence_events_test.go
+++ b/internal/namespace/persistence_events_test.go
@@ -86,16 +86,6 @@ func TestSubscriptionDeleted(t *testing.T) {
 	mae.AssertExpectations(t)
 }
 
-func TestUUIDCollectionEventFull(t *testing.T) {
-	mae := &spieventsmocks.Manager{}
-	nm := &namespaceManager{
-		adminEvents: mae,
-	}
-	mae.On("Dispatch", mock.Anything).Return()
-	nm.UUIDCollectionEvent(database.CollectionNamespaces, core.ChangeEventTypeDeleted, fftypes.NewUUID())
-	mae.AssertExpectations(t)
-}
-
 func TestHashCollectionNSEventOk(t *testing.T) {
 	mae := &spieventsmocks.Manager{}
 	nm := &namespaceManager{
diff --git a/internal/orchestrator/data_query_test.go b/internal/orchestrator/data_query_test.go
index b1a257582..6a68ac775 100644
--- a/internal/orchestrator/data_query_test.go
+++ b/internal/orchestrator/data_query_test.go
@@ -30,13 +30,14 @@ import (
 
 func TestGetNamespace(t *testing.T) {
 	or := newTestOrchestrator()
-	or.mdi.On("GetNamespace", mock.Anything, "ns1").Return(nil, nil)
-	_, err := or.GetNamespace(context.Background(), "ns1")
-	assert.NoError(t, err)
+	defer or.cleanup(t)
+	ns := or.GetNamespace(context.Background())
+	assert.Equal(t, "ns", ns.LocalName)
 }
 
 func TestGetTransactionByID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetTransactionByID", mock.Anything, "ns", u).Return(nil, nil)
 	_, err := or.GetTransactionByID(context.Background(), u.String())
@@ -45,12 +46,14 @@ func TestGetTransactionByID(t *testing.T) {
 
 func TestGetTransactionByIDBadID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	_, err := or.GetTransactionByID(context.Background(), "")
 	assert.Regexp(t, "FF00138", err)
 }
 
 func TestGetTransactionOperationsOk(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.mdi.On("GetOperations", mock.Anything, "ns", mock.Anything).Return([]*core.Operation{}, nil, nil)
 	_, _, err := or.GetTransactionOperations(context.Background(), fftypes.NewUUID().String())
 	assert.NoError(t, err)
@@ -58,12 +61,14 @@ func TestGetTransactionOperationsOk(t *testing.T) {
 
 func TestGetTransactionOperationBadID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	_, _, err := or.GetTransactionOperations(context.Background(), "")
 	assert.Regexp(t, "FF00138", err)
 }
 
 func TestGetTransactions(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetTransactions", mock.Anything, "ns", mock.Anything).Return([]*core.Transaction{}, nil, nil)
 	fb := database.TransactionQueryFactory.NewFilter(context.Background())
@@ -74,12 +79,14 @@ func TestGetTransactions(t *testing.T) {
 
 func TestGetMessageByIDBadID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	_, err := or.GetMessageByID(context.Background(), "")
 	assert.Regexp(t, "FF00138", err)
 }
 
 func TestGetMessageByIDNoValuesOk(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	msgID := fftypes.NewUUID()
 	msg := &core.Message{
 		Header: core.MessageHeader{
@@ -103,6 +110,7 @@ func TestGetMessageByIDNoValuesOk(t *testing.T) {
 
 func TestGetMessageByIDWithDataOk(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	msgID := fftypes.NewUUID()
 	msg := &core.Message{
 		Header: core.MessageHeader{
@@ -132,6 +140,7 @@ func TestGetMessageByIDWithDataOk(t *testing.T) {
 
 func TestGetMessageByIDWithDataMsgFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	msgID := fftypes.NewUUID()
 	or.mdi.On("GetMessageByID", mock.Anything, "ns", mock.Anything).Return(nil, fmt.Errorf("pop"))
 
@@ -141,6 +150,7 @@ func TestGetMessageByIDWithDataMsgFail(t *testing.T) {
 
 func TestGetMessageByIDWithDataFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	msgID := fftypes.NewUUID()
 	msg := &core.Message{
 		Header: core.MessageHeader{
@@ -161,6 +171,7 @@ func TestGetMessageByIDWithDataFail(t *testing.T) {
 
 func TestGetMessages(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetMessages", mock.Anything, "ns", mock.Anything).Return([]*core.Message{}, nil, nil)
 	fb := database.MessageQueryFactory.NewFilter(context.Background())
@@ -171,6 +182,7 @@ func TestGetMessages(t *testing.T) {
 
 func TestGetMessagesWithDataFailMsg(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.mdi.On("GetMessages", mock.Anything, "ns", mock.Anything).Return(nil, nil, fmt.Errorf("pop"))
 	fb := database.MessageQueryFactory.NewFilter(context.Background())
 	f := fb.And(fb.Eq("id", fftypes.NewUUID()))
@@ -180,6 +192,7 @@ func TestGetMessagesWithDataFailMsg(t *testing.T) {
 
 func TestGetMessagesWithDataOk(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	msgID := fftypes.NewUUID()
 	msg := &core.Message{
@@ -198,6 +211,7 @@ func TestGetMessagesWithDataOk(t *testing.T) {
 
 func TestGetMessagesWithDataFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	msgID := fftypes.NewUUID()
 	msg := &core.Message{
@@ -216,6 +230,7 @@ func TestGetMessagesWithDataFail(t *testing.T) {
 
 func TestGetMessagesForData(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetMessagesForData", mock.Anything, "ns", u, mock.Anything).Return([]*core.Message{}, nil, nil)
 	fb := database.MessageQueryFactory.NewFilter(context.Background())
@@ -226,6 +241,7 @@ func TestGetMessagesForData(t *testing.T) {
 
 func TestGetMessagesForDataBadID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	f := database.MessageQueryFactory.NewFilter(context.Background()).And()
 	_, _, err := or.GetMessagesForData(context.Background(), "!bad", f)
 	assert.Regexp(t, "FF00138", err)
@@ -233,6 +249,7 @@ func TestGetMessagesForDataBadID(t *testing.T) {
 
 func TestGetMessageTransactionOk(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	msgID := fftypes.NewUUID()
 	batchID := fftypes.NewUUID()
 	txID := fftypes.NewUUID()
@@ -260,6 +277,7 @@ func TestGetMessageTransactionOk(t *testing.T) {
 
 func TestGetMessageTransactionNoBatchTX(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	msgID := fftypes.NewUUID()
 	batchID := fftypes.NewUUID()
 	or.mdi.On("GetMessageByID", mock.Anything, "ns", msgID).Return(&core.Message{
@@ -276,6 +294,7 @@ func TestGetMessageTransactionNoBatchTX(t *testing.T) {
 
 func TestGetMessageTransactionNoBatch(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	msgID := fftypes.NewUUID()
 	batchID := fftypes.NewUUID()
 	or.mdi.On("GetMessageByID", mock.Anything, "ns", msgID).Return(&core.Message{
@@ -292,6 +311,7 @@ func TestGetMessageTransactionNoBatch(t *testing.T) {
 
 func TestGetMessageTransactionBatchLookupErr(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	msgID := fftypes.NewUUID()
 	batchID := fftypes.NewUUID()
 	or.mdi.On("GetMessageByID", mock.Anything, "ns", msgID).Return(&core.Message{
@@ -308,6 +328,7 @@ func TestGetMessageTransactionBatchLookupErr(t *testing.T) {
 
 func TestGetMessageTransactionNoBatchID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	msgID := fftypes.NewUUID()
 	or.mdi.On("GetMessageByID", mock.Anything, "ns", msgID).Return(&core.Message{
 		Header: core.MessageHeader{
@@ -321,6 +342,7 @@ func TestGetMessageTransactionNoBatchID(t *testing.T) {
 
 func TestGetMessageTransactionNoTx(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	msgID := fftypes.NewUUID()
 	or.mdi.On("GetMessageByID", mock.Anything, "ns", msgID).Return(&core.Message{
 		Header: core.MessageHeader{
@@ -333,6 +355,7 @@ func TestGetMessageTransactionNoTx(t *testing.T) {
 
 func TestGetMessageTransactionMessageNotFound(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	msgID := fftypes.NewUUID()
 	or.mdi.On("GetMessageByID", mock.Anything, "ns", msgID).Return(nil, nil)
 	_, err := or.GetMessageTransaction(context.Background(), msgID.String())
@@ -341,6 +364,7 @@ func TestGetMessageTransactionMessageNotFound(t *testing.T) {
 
 func TestGetMessageData(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	msg := &core.Message{
 		Header: core.MessageHeader{
 			Namespace: "ns",
@@ -359,6 +383,7 @@ func TestGetMessageData(t *testing.T) {
 
 func TestGetMessageDataBadMsg(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.mdi.On("GetMessageByID", mock.Anything, "ns", mock.Anything).Return(nil, nil)
 	_, err := or.GetMessageData(context.Background(), fftypes.NewUUID().String())
 	assert.Regexp(t, "FF10109", err)
@@ -366,6 +391,7 @@ func TestGetMessageDataBadMsg(t *testing.T) {
 
 func TestGetMessageEventsOk(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	msg := &core.Message{
 		Header: core.MessageHeader{
 			Namespace: "ns",
@@ -393,6 +419,7 @@ func TestGetMessageEventsOk(t *testing.T) {
 
 func TestGetMessageEventsBadMsgID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	fb := database.EventQueryFactory.NewFilter(context.Background())
 	f := fb.And(fb.Eq("type", core.EventTypeMessageConfirmed))
 	or.mdi.On("GetMessageByID", mock.Anything, "ns", mock.Anything).Return(nil, nil)
@@ -403,6 +430,7 @@ func TestGetMessageEventsBadMsgID(t *testing.T) {
 
 func TestGetBatchByID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetBatchByID", mock.Anything, "ns", u).Return(&core.BatchPersisted{
 		BatchHeader: core.BatchHeader{
@@ -415,12 +443,14 @@ func TestGetBatchByID(t *testing.T) {
 
 func TestGetBatchByIDBadID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	_, err := or.GetBatchByID(context.Background(), "")
 	assert.Regexp(t, "FF00138", err)
 }
 
 func TestGetBatches(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetBatches", mock.Anything, "ns", mock.Anything).Return([]*core.BatchPersisted{}, nil, nil)
 	fb := database.BatchQueryFactory.NewFilter(context.Background())
@@ -431,6 +461,7 @@ func TestGetBatches(t *testing.T) {
 
 func TestGetDataByID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetDataByID", mock.Anything, "ns", u, true).Return(&core.Data{
 		Namespace: "ns",
@@ -441,12 +472,14 @@ func TestGetDataByID(t *testing.T) {
 
 func TestGetDataByIDBadID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	_, err := or.GetDataByID(context.Background(), "")
 	assert.Regexp(t, "FF00138", err)
 }
 
 func TestGetData(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetData", mock.Anything, "ns", mock.Anything).Return(core.DataArray{}, nil, nil)
 	fb := database.DataQueryFactory.NewFilter(context.Background())
@@ -457,6 +490,7 @@ func TestGetData(t *testing.T) {
 
 func TestGetDatatypeByID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetDatatypeByID", mock.Anything, "ns", u).Return(&core.Datatype{
 		Namespace: "ns1",
@@ -467,12 +501,14 @@ func TestGetDatatypeByID(t *testing.T) {
 
 func TestGetDatatypeByIDBadID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	_, err := or.GetDatatypeByID(context.Background(), "")
 	assert.Regexp(t, "FF00138", err)
 }
 
 func TestGetDatatypeByName(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.mdi.On("GetDatatypeByName", context.Background(), "ns", "dt", "1").Return(&core.Datatype{
 		Namespace: "ns1",
 	}, nil)
@@ -482,18 +518,21 @@ func TestGetDatatypeByName(t *testing.T) {
 
 func TestGetDatatypeByNameBadNamespace(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	_, err := or.GetDatatypeByName(context.Background(), "", "")
 	assert.Regexp(t, "FF00140", err)
 }
 
 func TestGetDatatypeByNameBadName(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	_, err := or.GetDatatypeByName(context.Background(), "", "")
 	assert.Regexp(t, "FF00140", err)
 }
 
 func TestGetOperationByID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetOperationByID", mock.Anything, "ns", u).Return(&core.Operation{
 		Namespace: "ns1",
@@ -504,12 +543,14 @@ func TestGetOperationByID(t *testing.T) {
 
 func TestGetOperationIDdBadID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	_, err := or.GetOperationByID(context.Background(), "")
 	assert.Regexp(t, "FF00138", err)
 }
 
 func TestGetEventByID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetEventByID", mock.Anything, "ns", u).Return(&core.Event{
 		Namespace: "ns",
@@ -520,12 +561,14 @@ func TestGetEventByID(t *testing.T) {
 
 func TestGetEventIDBadID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	_, err := or.GetEventByID(context.Background(), "")
 	assert.Regexp(t, "FF00138", err)
 }
 
 func TestGetDatatypes(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetDatatypes", mock.Anything, "ns", mock.Anything).Return([]*core.Datatype{}, nil, nil)
 	fb := database.DatatypeQueryFactory.NewFilter(context.Background())
@@ -536,6 +579,7 @@ func TestGetDatatypes(t *testing.T) {
 
 func TestGetOperations(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetOperations", mock.Anything, "ns", mock.Anything).Return([]*core.Operation{}, nil, nil)
 	fb := database.OperationQueryFactory.NewFilter(context.Background())
@@ -546,6 +590,7 @@ func TestGetOperations(t *testing.T) {
 
 func TestGetEvents(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetEvents", mock.Anything, "ns", mock.Anything).Return([]*core.Event{}, nil, nil)
 	fb := database.EventQueryFactory.NewFilter(context.Background())
@@ -556,6 +601,7 @@ func TestGetEvents(t *testing.T) {
 
 func TestGetEventsWithReferencesFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetEvents", mock.Anything, "ns", mock.Anything).Return(nil, nil, fmt.Errorf("pop"))
 	fb := database.EventQueryFactory.NewFilter(context.Background())
@@ -566,6 +612,7 @@ func TestGetEventsWithReferencesFail(t *testing.T) {
 
 func TestGetEventsWithReferences(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 
 	// Setup the IDs
@@ -633,6 +680,7 @@ func TestGetEventsWithReferences(t *testing.T) {
 
 func TestGetEventsWithReferencesEnrichFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 
 	or.mdi.On("GetEvents", mock.Anything, "ns", mock.Anything).Return([]*core.Event{{ID: fftypes.NewUUID()}}, nil, nil)
@@ -645,6 +693,7 @@ func TestGetEventsWithReferencesEnrichFail(t *testing.T) {
 
 func TestGetBlockchainEventByID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 
 	id := fftypes.NewUUID()
 	or.mdi.On("GetBlockchainEventByID", context.Background(), "ns", id).Return(&core.BlockchainEvent{
@@ -657,12 +706,14 @@ func TestGetBlockchainEventByID(t *testing.T) {
 
 func TestGetBlockchainEventByIDBadID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	_, err := or.GetBlockchainEventByID(context.Background(), "")
 	assert.Regexp(t, "FF00138", err)
 }
 
 func TestGetBlockchainEvents(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 
 	or.mdi.On("GetBlockchainEvents", context.Background(), "ns", mock.Anything).Return(nil, nil, nil)
 
@@ -673,6 +724,7 @@ func TestGetBlockchainEvents(t *testing.T) {
 
 func TestGetTransactionBlockchainEventsOk(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	or.mdi.On("GetBlockchainEvents", mock.Anything, "ns", mock.Anything).Return([]*core.BlockchainEvent{}, nil, nil)
 	_, _, err := or.GetTransactionBlockchainEvents(context.Background(), fftypes.NewUUID().String())
 	assert.NoError(t, err)
@@ -680,12 +732,14 @@ func TestGetTransactionBlockchainEventsOk(t *testing.T) {
 
 func TestGetTransactionBlockchainEventsBadID(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	_, _, err := or.GetTransactionBlockchainEvents(context.Background(), "")
 	assert.Regexp(t, "FF00138", err)
 }
 
 func TestGetPins(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 	u := fftypes.NewUUID()
 	or.mdi.On("GetPins", mock.Anything, "ns", mock.Anything).Return([]*core.Pin{}, nil, nil)
 	fb := database.PinQueryFactory.NewFilter(context.Background())
diff --git a/internal/orchestrator/orchestrator_test.go b/internal/orchestrator/orchestrator_test.go
index 4f4f668a1..46c03eff2 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.NamespaceRef{LocalName: "ns", RemoteName: "ns"},
+			namespace: &core.Namespace{LocalName: "ns", RemoteName: "ns"},
 		},
 		mdi: &databasemocks.Plugin{},
 		mdm: &datamocks.Manager{},
@@ -182,7 +182,7 @@ func newTestOrchestrator() *testOrchestrator {
 
 func TestNewOrchestrator(t *testing.T) {
 	or := NewOrchestrator(
-		core.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		&core.Namespace{LocalName: "ns1", RemoteName: "ns1"},
 		Config{},
 		Plugins{},
 		&metricsmocks.Manager{},
@@ -369,9 +369,8 @@ func TestStartBatchFail(t *testing.T) {
 	coreconfig.Reset()
 	or := newTestOrchestrator()
 	defer or.cleanup(t)
-	or.mdi.On("GetNamespace", mock.Anything, "ns").Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
-	or.mmp.On("ConfigureContract", mock.Anything, mock.Anything).Return(nil)
+	or.mdm.On("Start").Return(nil)
+	or.mmp.On("ConfigureContract", mock.Anything).Return(nil)
 	or.mba.On("Start").Return(fmt.Errorf("pop"))
 	err := or.Start(func() {})
 	assert.EqualError(t, err, "pop")
@@ -381,8 +380,8 @@ func TestStartBlockchainsConfigureFail(t *testing.T) {
 	coreconfig.Reset()
 	or := newTestOrchestrator()
 	defer or.cleanup(t)
-	or.mdi.On("GetNamespace", mock.Anything, "ns").Return(nil, nil)
-	or.mmp.On("ConfigureContract", mock.Anything, &core.FireFlyContracts{}).Return(fmt.Errorf("pop"))
+	or.mdm.On("Start").Return(nil)
+	or.mmp.On("ConfigureContract", mock.Anything).Return(fmt.Errorf("pop"))
 	err := or.Start(func() {})
 	assert.EqualError(t, err, "pop")
 }
@@ -391,9 +390,8 @@ func TestStartStopOk(t *testing.T) {
 	coreconfig.Reset()
 	or := newTestOrchestrator()
 	defer or.cleanup(t)
-	or.mdi.On("GetNamespace", mock.Anything, "ns").Return(nil, nil)
-	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
-	or.mmp.On("ConfigureContract", mock.Anything, &core.FireFlyContracts{}).Return(nil)
+	or.mmp.On("ConfigureContract", mock.Anything).Return(nil)
+	or.mdm.On("Start").Return(nil)
 	or.mba.On("Start").Return(nil)
 	or.mem.On("Start").Return(nil)
 	or.mbm.On("Start").Return(nil)
@@ -404,6 +402,7 @@ func TestStartStopOk(t *testing.T) {
 	or.mdm.On("WaitStop").Return(nil)
 	or.msd.On("WaitStop").Return(nil)
 	or.mom.On("WaitStop").Return(nil)
+	or.mem.On("WaitStop").Return(nil)
 	err := or.Start(func() {})
 	assert.NoError(t, err)
 	or.WaitStop()
diff --git a/internal/orchestrator/persistence_events_test.go b/internal/orchestrator/persistence_events_test.go
index d92ea7359..e7c32e81b 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.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "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.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "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.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "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.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "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.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "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.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "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.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "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.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "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.NamespaceRef{LocalName: "ns1", RemoteName: "ns1"},
+		namespace: &core.Namespace{LocalName: "ns1", RemoteName: "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 8c44cc4ec..79003917a 100644
--- a/internal/orchestrator/status_test.go
+++ b/internal/orchestrator/status_test.go
@@ -23,46 +23,44 @@ import (
 	"github.com/hyperledger/firefly-common/pkg/config"
 	"github.com/hyperledger/firefly-common/pkg/fftypes"
 	"github.com/hyperledger/firefly/internal/coreconfig"
-	"github.com/hyperledger/firefly/mocks/eventmocks"
-	"github.com/hyperledger/firefly/mocks/identitymanagermocks"
 	"github.com/hyperledger/firefly/pkg/core"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 )
 
 var (
-	pluginsResult = core.NodeStatusPlugins{
-		Blockchain: []*core.NodeStatusPlugin{
+	pluginsResult = core.NamespaceStatusPlugins{
+		Blockchain: []*core.NamespaceStatusPlugin{
 			{
 				PluginType: "mock-bi",
 			},
 		},
-		Database: []*core.NodeStatusPlugin{
+		Database: []*core.NamespaceStatusPlugin{
 			{
 				PluginType: "mock-di",
 			},
 		},
-		DataExchange: []*core.NodeStatusPlugin{
+		DataExchange: []*core.NamespaceStatusPlugin{
 			{
 				PluginType: "mock-dx",
 			},
 		},
-		Events: []*core.NodeStatusPlugin{
+		Events: []*core.NamespaceStatusPlugin{
 			{
 				PluginType: "mock-ei",
 			},
 		},
-		Identity: []*core.NodeStatusPlugin{
+		Identity: []*core.NamespaceStatusPlugin{
 			{
 				PluginType: "mock-ii",
 			},
 		},
-		SharedStorage: []*core.NodeStatusPlugin{
+		SharedStorage: []*core.NamespaceStatusPlugin{
 			{
 				PluginType: "mock-ps",
 			},
 		},
-		Tokens: []*core.NodeStatusPlugin{
+		Tokens: []*core.NamespaceStatusPlugin{
 			{
 				Name:       "token",
 				PluginType: "mock-tk",
@@ -70,7 +68,7 @@ var (
 		},
 	}
 
-	mockEventPlugins = []*core.NodeStatusPlugin{
+	mockEventPlugins = []*core.NamespaceStatusPlugin{
 		{
 			PluginType: "mock-ei",
 		},
@@ -79,6 +77,7 @@ var (
 
 func TestGetStatusRegistered(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 
 	coreconfig.Reset()
 	config.Set(coreconfig.NamespacesDefault, "default")
@@ -87,8 +86,7 @@ func TestGetStatusRegistered(t *testing.T) {
 	orgID := fftypes.NewUUID()
 	nodeID := fftypes.NewUUID()
 
-	mim := or.identity.(*identitymanagermocks.Manager)
-	mim.On("GetMultipartyRootOrg", or.ctx).Return(&core.Identity{
+	or.mim.On("GetMultipartyRootOrg", or.ctx).Return(&core.Identity{
 		IdentityBase: core.IdentityBase{
 			ID:        orgID,
 			Name:      "org1",
@@ -96,7 +94,7 @@ func TestGetStatusRegistered(t *testing.T) {
 			DID:       "did:firefly:org/org1",
 		},
 	}, nil)
-	mim.On("CachedIdentityLookupNilOK", or.ctx, "did:firefly:node/node1").Return(&core.Identity{
+	or.mim.On("CachedIdentityLookupNilOK", or.ctx, "did:firefly:node/node1").Return(&core.Identity{
 		IdentityBase: core.IdentityBase{
 			ID:     nodeID,
 			Name:   "node1",
@@ -112,13 +110,12 @@ func TestGetStatusRegistered(t *testing.T) {
 
 	or.config.Multiparty.Org.Name = "org1"
 
-	mem := or.events.(*eventmocks.EventManager)
-	mem.On("GetPlugins").Return(mockEventPlugins)
+	or.mem.On("GetPlugins").Return(mockEventPlugins)
 
 	status, err := or.GetStatus(or.ctx)
 	assert.NoError(t, err)
 
-	assert.Equal(t, "ns", status.Namespace)
+	assert.Equal(t, "ns", status.Namespace.LocalName)
 
 	assert.Equal(t, "org1", status.Org.Name)
 	assert.True(t, status.Org.Registered)
@@ -145,16 +142,15 @@ func TestGetStatusRegistered(t *testing.T) {
 
 func TestGetStatusVerifierLookupFail(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 
 	coreconfig.Reset()
 	config.Set(coreconfig.NamespacesDefault, "default")
 	config.Set(coreconfig.NodeName, "node1")
 
 	orgID := fftypes.NewUUID()
-	nodeID := fftypes.NewUUID()
 
-	mim := or.identity.(*identitymanagermocks.Manager)
-	mim.On("GetMultipartyRootOrg", or.ctx).Return(&core.Identity{
+	or.mim.On("GetMultipartyRootOrg", or.ctx).Return(&core.Identity{
 		IdentityBase: core.IdentityBase{
 			ID:        orgID,
 			Name:      "org1",
@@ -162,17 +158,9 @@ func TestGetStatusVerifierLookupFail(t *testing.T) {
 			DID:       "did:firefly:org/org1",
 		},
 	}, nil)
-	mim.On("CachedIdentityLookupNilOK", or.ctx, "did:firefly:node/node1").Return(&core.Identity{
-		IdentityBase: core.IdentityBase{
-			ID:     nodeID,
-			Name:   "node1",
-			Parent: fftypes.NewUUID(),
-		},
-	}, false, nil)
 	or.mdi.On("GetVerifiers", or.ctx, "ns", mock.Anything).Return(nil, nil, fmt.Errorf("pop"))
 
-	mem := or.events.(*eventmocks.EventManager)
-	mem.On("GetPlugins").Return(mockEventPlugins)
+	or.mem.On("GetPlugins").Return(mockEventPlugins)
 
 	_, err := or.GetStatus(or.ctx)
 	assert.Regexp(t, "pop", err)
@@ -181,6 +169,7 @@ func TestGetStatusVerifierLookupFail(t *testing.T) {
 
 func TestGetStatusWrongNodeOwner(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 
 	coreconfig.Reset()
 	config.Set(coreconfig.NamespacesDefault, "default")
@@ -189,8 +178,7 @@ func TestGetStatusWrongNodeOwner(t *testing.T) {
 	orgID := fftypes.NewUUID()
 	nodeID := fftypes.NewUUID()
 
-	mim := or.identity.(*identitymanagermocks.Manager)
-	mim.On("GetMultipartyRootOrg", or.ctx).Return(&core.Identity{
+	or.mim.On("GetMultipartyRootOrg", or.ctx).Return(&core.Identity{
 		IdentityBase: core.IdentityBase{
 			ID:        orgID,
 			Name:      "org1",
@@ -198,7 +186,7 @@ func TestGetStatusWrongNodeOwner(t *testing.T) {
 			DID:       "did:firefly:org/org1",
 		},
 	}, nil)
-	mim.On("CachedIdentityLookupNilOK", or.ctx, "did:firefly:node/node1").Return(&core.Identity{
+	or.mim.On("CachedIdentityLookupNilOK", or.ctx, "did:firefly:node/node1").Return(&core.Identity{
 		IdentityBase: core.IdentityBase{
 			ID:     nodeID,
 			Name:   "node1",
@@ -214,13 +202,12 @@ func TestGetStatusWrongNodeOwner(t *testing.T) {
 
 	or.config.Multiparty.Org.Name = "org1"
 
-	mem := or.events.(*eventmocks.EventManager)
-	mem.On("GetPlugins").Return(mockEventPlugins)
+	or.mem.On("GetPlugins").Return(mockEventPlugins)
 
 	status, err := or.GetStatus(or.ctx)
 	assert.NoError(t, err)
 
-	assert.Equal(t, "ns", status.Namespace)
+	assert.Equal(t, "ns", status.Namespace.LocalName)
 
 	assert.Equal(t, "org1", status.Org.Name)
 	assert.True(t, status.Org.Registered)
@@ -235,23 +222,22 @@ func TestGetStatusWrongNodeOwner(t *testing.T) {
 
 func TestGetStatusUnregistered(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 
 	coreconfig.Reset()
 	config.Set(coreconfig.NamespacesDefault, "default")
 	config.Set(coreconfig.NodeName, "node1")
 
-	mim := or.identity.(*identitymanagermocks.Manager)
-	mim.On("GetMultipartyRootOrg", or.ctx).Return(nil, fmt.Errorf("pop"))
+	or.mim.On("GetMultipartyRootOrg", or.ctx).Return(nil, fmt.Errorf("pop"))
 
 	or.config.Multiparty.Org.Name = "org1"
 
-	mem := or.events.(*eventmocks.EventManager)
-	mem.On("GetPlugins").Return(mockEventPlugins)
+	or.mem.On("GetPlugins").Return(mockEventPlugins)
 
 	status, err := or.GetStatus(or.ctx)
 	assert.NoError(t, err)
 
-	assert.Equal(t, "ns", status.Namespace)
+	assert.Equal(t, "ns", status.Namespace.LocalName)
 
 	assert.Equal(t, "org1", status.Org.Name)
 	assert.False(t, status.Org.Registered)
@@ -265,6 +251,7 @@ func TestGetStatusUnregistered(t *testing.T) {
 
 func TestGetStatusOrgOnlyRegistered(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 
 	coreconfig.Reset()
 	config.Set(coreconfig.NamespacesDefault, "default")
@@ -272,8 +259,7 @@ func TestGetStatusOrgOnlyRegistered(t *testing.T) {
 
 	orgID := fftypes.NewUUID()
 
-	mim := or.identity.(*identitymanagermocks.Manager)
-	mim.On("GetMultipartyRootOrg", or.ctx).Return(&core.Identity{
+	or.mim.On("GetMultipartyRootOrg", or.ctx).Return(&core.Identity{
 		IdentityBase: core.IdentityBase{
 			ID:        orgID,
 			Name:      "org1",
@@ -281,7 +267,7 @@ func TestGetStatusOrgOnlyRegistered(t *testing.T) {
 			DID:       "did:firefly:org/org1",
 		},
 	}, nil)
-	mim.On("CachedIdentityLookupNilOK", or.ctx, "did:firefly:node/node1").Return(nil, false, nil)
+	or.mim.On("CachedIdentityLookupNilOK", or.ctx, "did:firefly:node/node1").Return(nil, false, nil)
 	or.mdi.On("GetVerifiers", or.ctx, "ns", mock.Anything).Return([]*core.Verifier{
 		{Hash: fftypes.NewRandB32(), VerifierRef: core.VerifierRef{
 			Type:  core.VerifierTypeEthAddress,
@@ -291,13 +277,12 @@ func TestGetStatusOrgOnlyRegistered(t *testing.T) {
 
 	or.config.Multiparty.Org.Name = "org1"
 
-	mem := or.events.(*eventmocks.EventManager)
-	mem.On("GetPlugins").Return(mockEventPlugins)
+	or.mem.On("GetPlugins").Return(mockEventPlugins)
 
 	status, err := or.GetStatus(or.ctx)
 	assert.NoError(t, err)
 
-	assert.Equal(t, "ns", status.Namespace)
+	assert.Equal(t, "ns", status.Namespace.LocalName)
 
 	assert.Equal(t, "org1", status.Org.Name)
 	assert.True(t, status.Org.Registered)
@@ -320,6 +305,7 @@ func TestGetStatusOrgOnlyRegistered(t *testing.T) {
 
 func TestGetStatusNodeError(t *testing.T) {
 	or := newTestOrchestrator()
+	defer or.cleanup(t)
 
 	coreconfig.Reset()
 	config.Set(coreconfig.NamespacesDefault, "default")
@@ -327,8 +313,7 @@ func TestGetStatusNodeError(t *testing.T) {
 
 	orgID := fftypes.NewUUID()
 
-	mim := or.identity.(*identitymanagermocks.Manager)
-	mim.On("GetMultipartyRootOrg", or.ctx).Return(&core.Identity{
+	or.mim.On("GetMultipartyRootOrg", or.ctx).Return(&core.Identity{
 		IdentityBase: core.IdentityBase{
 			ID:        orgID,
 			Name:      "org1",
@@ -336,7 +321,7 @@ func TestGetStatusNodeError(t *testing.T) {
 			DID:       "did:firefly:org/org1",
 		},
 	}, nil)
-	mim.On("CachedIdentityLookupNilOK", or.ctx, "did:firefly:node/node1").Return(nil, false, fmt.Errorf("pop"))
+	or.mim.On("CachedIdentityLookupNilOK", or.ctx, "did:firefly:node/node1").Return(nil, false, fmt.Errorf("pop"))
 	or.mdi.On("GetVerifiers", or.ctx, "ns", mock.Anything).Return([]*core.Verifier{
 		{Hash: fftypes.NewRandB32(), VerifierRef: core.VerifierRef{
 			Type:  core.VerifierTypeEthAddress,
@@ -344,8 +329,7 @@ func TestGetStatusNodeError(t *testing.T) {
 		}},
 	}, nil, nil)
 
-	mem := or.events.(*eventmocks.EventManager)
-	mem.On("GetPlugins").Return(mockEventPlugins)
+	or.mem.On("GetPlugins").Return(mockEventPlugins)
 
 	_, err := or.GetStatus(or.ctx)
 	assert.EqualError(t, err, "pop")
diff --git a/internal/txcommon/event_enrich_test.go b/internal/txcommon/event_enrich_test.go
index e4f860e12..cd080ba29 100644
--- a/internal/txcommon/event_enrich_test.go
+++ b/internal/txcommon/event_enrich_test.go
@@ -400,55 +400,6 @@ func TestEnrichIdentityConfirmedFail(t *testing.T) {
 	assert.EqualError(t, err, "pop")
 }
 
-func TestEnrichNamespaceConfirmed(t *testing.T) {
-	mdi := &databasemocks.Plugin{}
-	mdm := &datamocks.Manager{}
-	txHelper := NewTransactionHelper("ns1", mdi, mdm)
-	ctx := context.Background()
-
-	// Setup the IDs
-	ref1 := fftypes.NewUUID()
-	ev1 := fftypes.NewUUID()
-
-	// Setup enrichment
-	mdi.On("GetNamespaceByID", mock.Anything, ref1).Return(&core.Namespace{
-		ID: ref1,
-	}, nil)
-
-	event := &core.Event{
-		ID:        ev1,
-		Type:      core.EventTypeNamespaceConfirmed,
-		Reference: ref1,
-	}
-
-	enriched, err := txHelper.EnrichEvent(ctx, event)
-	assert.NoError(t, err)
-	assert.Equal(t, ref1, enriched.NamespaceDetails.ID)
-}
-
-func TestEnrichNamespaceConfirmedFail(t *testing.T) {
-	mdi := &databasemocks.Plugin{}
-	mdm := &datamocks.Manager{}
-	txHelper := NewTransactionHelper("ns1", mdi, mdm)
-	ctx := context.Background()
-
-	// Setup the IDs
-	ref1 := fftypes.NewUUID()
-	ev1 := fftypes.NewUUID()
-
-	// Setup enrichment
-	mdi.On("GetNamespaceByID", mock.Anything, ref1).Return(nil, fmt.Errorf("pop"))
-
-	event := &core.Event{
-		ID:        ev1,
-		Type:      core.EventTypeNamespaceConfirmed,
-		Reference: ref1,
-	}
-
-	_, err := txHelper.EnrichEvent(ctx, event)
-	assert.EqualError(t, err, "pop")
-}
-
 func TestEnrichTokenPoolConfirmed(t *testing.T) {
 	mdi := &databasemocks.Plugin{}
 	mdm := &datamocks.Manager{}
diff --git a/pkg/core/namespace_test.go b/pkg/core/namespace_test.go
index 58efb163b..6b5ce5aeb 100644
--- a/pkg/core/namespace_test.go
+++ b/pkg/core/namespace_test.go
@@ -17,60 +17,45 @@
 package core
 
 import (
-	"context"
 	"testing"
 
 	"github.com/hyperledger/firefly-common/pkg/fftypes"
 	"github.com/stretchr/testify/assert"
 )
 
-func TestNamespaceValidation(t *testing.T) {
-
+func TestNamespace(t *testing.T) {
 	ns := &Namespace{
-		Name: "!wrong",
-	}
-	assert.Regexp(t, "FF00140.*name", ns.Validate(context.Background(), false))
-
-	ns = &Namespace{
-		Name:        "ok",
-		Description: string(make([]byte, 4097)),
-	}
-	assert.Regexp(t, "FF00135.*description", ns.Validate(context.Background(), false))
-
-	ns = &Namespace{
-		Name:        "ok",
-		Description: "ok",
+		LocalName:  "local",
+		RemoteName: "remote",
 	}
-	assert.NoError(t, ns.Validate(context.Background(), false))
-
-	assert.Regexp(t, "FF00114", ns.Validate(context.Background(), true))
-
-	var nsDef Definition = ns
-	assert.Equal(t, "358de1708c312f6b9eb4c44e0d9811c6f69bf389871d38dd7501992b2c00b557", nsDef.Topic())
-	nsDef.SetBroadcastMessage(fftypes.NewUUID())
-	assert.NotNil(t, ns.Message)
-
+	ref := ns.Ref()
+	assert.Equal(t, "local", ref.LocalName)
+	assert.Equal(t, "remote", ref.RemoteName)
 }
 
-func TestFireFlyContractsDatabaseSerialization(t *testing.T) {
-	contracts1 := &FireFlyContracts{
-		Active: FireFlyContractInfo{
-			Index:        1,
-			FirstEvent:   "oldest",
-			Subscription: "1234",
+func TestMultipartyContractsDatabaseSerialization(t *testing.T) {
+	contracts1 := &MultipartyContracts{
+		Active: MultipartyContract{
+			Index:      1,
+			FirstEvent: "oldest",
 			Location: fftypes.JSONAnyPtr(fftypes.JSONObject{
 				"address": "0x123",
 			}.String()),
+			Info: MultipartyContractInfo{
+				Subscription: "1234",
+			},
 		},
-		Terminated: []FireFlyContractInfo{
+		Terminated: []MultipartyContract{
 			{
-				Index:        0,
-				FinalEvent:   "50",
-				Subscription: "12345",
-				FirstEvent:   "oldest",
+				Index:      0,
+				FirstEvent: "oldest",
 				Location: fftypes.JSONAnyPtr(fftypes.JSONObject{
 					"address": "0x1234",
 				}.String()),
+				Info: MultipartyContractInfo{
+					Subscription: "12345",
+					FinalEvent:   "50",
+				},
 			},
 		},
 	}
@@ -78,10 +63,10 @@ func TestFireFlyContractsDatabaseSerialization(t *testing.T) {
 	// Verify it serializes as bytes to the database
 	val1, err := contracts1.Value()
 	assert.NoError(t, err)
-	assert.Equal(t, `{"active":{"index":1,"location":{"address":"0x123"},"firstEvent":"oldest","subscription":"1234"},"terminated":[{"index":0,"finalEvent":"50","location":{"address":"0x1234"},"firstEvent":"oldest","subscription":"12345"}]}`, string(val1.([]byte)))
+	assert.Equal(t, `{"active":{"index":1,"location":{"address":"0x123"},"firstEvent":"oldest","info":{"subscription":"1234"}},"terminated":[{"index":0,"location":{"address":"0x1234"},"firstEvent":"oldest","info":{"subscription":"12345","finalEvent":"50"}}]}`, string(val1.([]byte)))
 
 	// Verify it restores ok
-	contracts2 := &FireFlyContracts{}
+	contracts2 := &MultipartyContracts{}
 	err = contracts2.Scan(val1)
 	assert.NoError(t, err)
 	assert.Equal(t, 1, contracts2.Active.Index)