Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c31cb30
Refactor self_healing_open to RDP and whole feature to sealing-recovery
cjen1-msft Feb 19, 2026
6e44eb0
snag
cjen1-msft Feb 19, 2026
2eca945
fixup config
cjen1-msft Feb 19, 2026
f0b292c
Fixup config of sealing_recovery
cjen1-msft Feb 19, 2026
1f5cfd3
Clean up testbed
cjen1-msft Feb 19, 2026
477e4e2
Fixup main
cjen1-msft Feb 19, 2026
2a5f868
Fix identity issue with published_address being stale
cjen1-msft Feb 19, 2026
a9c66df
Update docs
cjen1-msft Feb 19, 2026
701eaa3
fmt
cjen1-msft Feb 19, 2026
659c2da
Merge branch 'main' into sealing-recovery-codex
cjen1-msft Feb 19, 2026
f65b746
Make line longer lol
cjen1-msft Feb 20, 2026
a1326aa
Update docs
cjen1-msft Feb 24, 2026
4b52e60
Rename to remove impl
cjen1-msft Feb 24, 2026
8a13cd8
Update comment in network.py
cjen1-msft Feb 24, 2026
e4671f8
Simplify rdp config.jinja
cjen1-msft Feb 24, 2026
7a200f5
Refactor sealing_recovery per_node_overrides into network.py helper
cjen1-msft Feb 24, 2026
4beb6a8
Clean up per_node_args_override in recovery_decision_protocol
cjen1-msft Feb 24, 2026
6ec445e
Clean up and add disable failover if set to 0ms
cjen1-msft Feb 24, 2026
49a83d8
refmt
cjen1-msft Feb 24, 2026
cca1fda
remove test file
cjen1-msft Feb 24, 2026
4b9b532
Use intrinsic id rather than node_id throughout
cjen1-msft Feb 24, 2026
7689430
Ensure we are writing the right key
cjen1-msft Feb 24, 2026
d84faf9
doc new map
cjen1-msft Feb 24, 2026
a796dbe
fmt
cjen1-msft Feb 24, 2026
5978942
Write to sealed_shares using intrinsic_id
cjen1-msft Feb 24, 2026
3abbe83
fixup
cjen1-msft Feb 24, 2026
0c21a41
fixup
cjen1-msft Feb 24, 2026
90f1818
identity -> location
cjen1-msft Feb 25, 2026
d3303ce
store recovery_keys as node_id->key
cjen1-msft Feb 25, 2026
cee474f
Cache sealing info before it is overwritten
cjen1-msft Feb 25, 2026
838f45e
fmt
cjen1-msft Feb 25, 2026
3c9567c
Make node the single source of truth for sealing recovery locations
cjen1-msft Feb 25, 2026
bf16b77
Merge branch 'main' into sealing-recovery-codex
achamayou Feb 25, 2026
2b232f5
Add timeout = 0 test
cjen1-msft Feb 25, 2026
3cfcc49
cluster => expected_locations
cjen1-msft Feb 25, 2026
65993c5
Refmt
cjen1-msft Feb 26, 2026
b199c85
Revert "Add timeout = 0 test"
cjen1-msft Feb 26, 2026
b762824
fixup naming
cjen1-msft Feb 26, 2026
aa854d6
schennigans to minimise diff
cjen1-msft Feb 26, 2026
74e8e41
And move it back
cjen1-msft Feb 26, 2026
02bfec0
Revert rename such that the diff is minimal
cjen1-msft Feb 26, 2026
47e786b
revert improved error message
cjen1-msft Feb 26, 2026
b429c7b
fixup test
cjen1-msft Feb 26, 2026
369864e
snag docs
cjen1-msft Feb 26, 2026
4ba3e50
Add changelog entry
cjen1-msft Feb 26, 2026
cec710c
Update doc/audit/builtin_maps.rst
cjen1-msft Feb 26, 2026
dfe14dd
excise intrinsic
cjen1-msft Feb 26, 2026
df8798a
Merge branch 'main' into sealing-recovery-codex
cjen1-msft Feb 26, 2026
4ea8040
bump pyproject
cjen1-msft Feb 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [7.0.0-dev12]

[7.0.0-dev12]: https://github.com/microsoft/CCF/releases/tag/ccf-7.0.0-dev12

### Changed

- Refactored the user facing surface of self-healing-open and local sealing. The whole feature is now `sealing-recovery` with `self-healing-open` now referred to as the `recovery-decision-protocol`. (#7679)
- Local sealing is enabled by setting the `sealing-recovery` config field (for both the sealing node, and the unsealing recovery node)
- The local sealing identity is under `sealing-recovery.location.name`
- The recovery-decision-protocol is configured via `sealing-recovery.recovery_decision_protocol`

## [7.0.0-dev11]

[7.0.0-dev11]: https://github.com/microsoft/CCF/releases/tag/ccf-7.0.0-dev11
Expand Down
6 changes: 3 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ endif()
set(CCF_IMPL_SOURCE
${CCF_DIR}/src/enclave/main.cpp ${CCF_DIR}/src/enclave/thread_local.cpp
${CCF_DIR}/src/node/quote.cpp ${CCF_DIR}/src/node/uvm_endorsements.cpp
${CCF_DIR}/src/node/self_healing_open_impl.cpp
${CCF_DIR}/src/node/recovery_decision_protocol.cpp
)

add_ccf_static_library(
Expand Down Expand Up @@ -749,7 +749,7 @@ if(BUILD_TESTS)
${CMAKE_CURRENT_SOURCE_DIR}/src/node/rpc/test/frontend_test.cpp
${CCF_DIR}/src/node/quote.cpp
${CCF_DIR}/src/node/uvm_endorsements.cpp
${CCF_DIR}/src/node/self_healing_open_impl.cpp
${CCF_DIR}/src/node/recovery_decision_protocol.cpp
)
target_link_libraries(
frontend_test
Expand Down Expand Up @@ -789,7 +789,7 @@ if(BUILD_TESTS)
${CMAKE_CURRENT_SOURCE_DIR}/src/node/rpc/test/node_frontend_test.cpp
${CCF_DIR}/src/node/quote.cpp
${CCF_DIR}/src/node/uvm_endorsements.cpp
${CCF_DIR}/src/node/self_healing_open_impl.cpp
${CCF_DIR}/src/node/recovery_decision_protocol.cpp
)
target_link_libraries(
node_frontend_test
Expand Down
65 changes: 35 additions & 30 deletions doc/audit/builtin_maps.rst
Original file line number Diff line number Diff line change
Expand Up @@ -581,64 +581,69 @@ While the contents themselves are encrypted, the table is public so as to be acc
:project: CCF
:members:

``sealing_recovery_names``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Mapping from sealing recovery names to node IDs for nodes that support local sealing. This table is used alongside ``nodes.sealed_recovery_keys`` to fetch the sealed recovery key when a node is recovering.

**Key** Sealing recovery name of the node, represented as a string.

**Value** Node ID: SHA-256 digest of the node public key, represented as a hex-encoded string.

``last_recovery_type``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Value** The mechanism by which the ledger secret was recovered.

.. doxygenenum:: ccf::RecoveryType
:project: CCF

``self_healing_open.nodes``
~~~~~~~~~~~~~~~~~~~~~~~~~~~
``recovery_decision_protocol.nodes``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Key** Intrinsic node ID: A string which is unique to a particular node role within a cluster.
**Key** Location name: A string which is unique to the location of a particular node within a network.

**Value**

.. doxygenstruct:: ccf::self_healing_open::NodeInfo
.. doxygenstruct:: ccf::recovery_decision_protocol::NodeInfo
:project: CCF
:members:

``self_healing_open.gossip``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Key** Intrinsic node ID of the source of the gossip message.
``recovery_decision_protocol.gossip``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Value**
**Key** Location name of the source of the gossip message.

.. doxygenstruct:: ccf::self_healing_open::GossipRequest
:project: CCF
:members:
**Value** The TxID of the last recovered signed transaction known by the source node.

``self_healing_open.chosen_node``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``recovery_decision_protocol.chosen_node``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Value** The intrinsic node ID of the chosen node. This will either be the node this node voted for, or the node that is has received an `IAmOpen` message from.
**Value** The location name of the chosen node. This will either be the node this node voted for, or the node that it has received an `IAmOpen` message from.

``self_healing_open.votes``
~~~~~~~~~~~~~~~~~~~~~~~~~~~
``recovery_decision_protocol.votes``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Key** Intrinsic node ID of the node which has voted for this node to be opened.
**Key** Location name of the node which has voted for this node to be opened.

``self_healing_open.sm_state``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``recovery_decision_protocol.sm_state``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Value** State machine state of the self-healing open protocol.
**Value** State machine state of the recovery decision protocol.

.. doxygenenum:: ccf::self_healing_open::StateMachine
.. doxygenenum:: ccf::recovery_decision_protocol::StateMachine
:project: CCF

``self_healing_open.timeout_sm_state``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``recovery_decision_protocol.timeout_sm_state``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Value** Timeout state machine state of the self-healing open protocol. Ticks based on `failover_timeout` and advances `self_healing_open.sm_state` if it falls behind.
**Value** Timeout state machine state of the recovery decision protocol. Ticks based on `failover_timeout` and advances `recovery_decision_protocol.sm_state` if it falls behind.

See :cpp:enum:`ccf::self_healing_open::StateMachine` above.
See :cpp:enum:`ccf::recovery_decision_protocol::StateMachine` above.

``self_healing_open.open_kind``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``recovery_decision_protocol.open_kind``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Value** The kind of recovery that was performed, either `Quorum`-based which guarantees that there is at most one recovered service using this path, or `Failover`-based which could allow multiple services to recover.

.. doxygenenum:: ccf::self_healing_open::OpenKinds
:project: CCF
.. doxygenenum:: ccf::recovery_decision_protocol::OpenKinds
:project: CCF
106 changes: 53 additions & 53 deletions doc/host_config_schema/cchost_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -356,55 +356,6 @@
"previous_service_identity_file": {
"type": "string",
"description": "Path to the previous service certificate (PEM) file"
},
"previous_local_sealing_identity": {
"type": ["string", "null"],
"description": "The identity of the previous node which sealed the ledger secrets. Required if local sealing is enabled"
},
"self_healing_open": {
"type": "object",
"properties": {
"identity": {
"type": "object",
"properties": {
"intrinsic_id": {
"type": "string",
"description": "Intrinsic identifier of this node, used to identify it in the self-healing-open protocol"
},
"published_address": {
"type": "string",
"description": "Published address (host:port) of this node, used to identify it in the self-healing-open protocol"
}
}
},
"cluster_identities": {
"type": "array",
"items": {
"type": "object",
"properties": {
"intrinsic_id": {
"type": "string",
"description": "Intrinsic identifier of the node, used to identify it in the self-healing-open protocol"
},
"published_address": {
"type": "string",
"description": "Published address (host:port) of the node, used for communication during the self-healing-open protocol"
}
}
},
"description": "List of identities for all nodes in the cluster"
},
"retry_timeout": {
"type": "string",
"default": "100ms",
"description": "Interval (time string) at which the node re-sends self-healing-open messages. This should be significantly less than 'failover_timeout'"
},
"failover_timeout": {
"type": "string",
"default": "2000ms",
"description": "Interval (time string) after which the node forcibly advances to the next phase of the self-healing-open protocol"
}
}
}
},
"required": ["previous_service_identity_file"],
Expand Down Expand Up @@ -710,10 +661,59 @@
"default": "512MB",
"description": "Historical queries cache soft limit (as size string)"
},
"enable_local_sealing": {
"type": "boolean",
"default": false,
"description": "Enable sealing of ledger secrets using platform derived key capabilities (e.g. AMD SEV-SNP derived keys). This allows the node to unilaterally recover its ledger secrets on restart without needing to reconstruct them from recovery shares."
"sealing_recovery": {
"type": "object",
"description": "Optional. Controls the behaviour of sealing-based recovery. If set, enables sealing of ledger secrets using platform derived key capabilities (e.g. AMD SEV-SNP derived keys). This allows a future recovering node to unilaterally recover its ledger secrets on restart without needing to reconstruct them from recovery shares.",
"properties": {
"location": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"address": {
"type": "string"
}
},
"required": ["name", "address"],
"additionalProperties": false
},
"recovery_decision_protocol": {
"type": "object",
"properties": {
"expected_locations": {
"type": "array",
"description": "List of locations that the recovery_decision_protocol expects to be part of the previous network.",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"address": {
"type": "string"
}
},
"required": ["name", "address"],
"additionalProperties": false
}
},
"message_retry_timeout": {
"type": "string",
"default": "100ms"
},
"failover_timeout": {
"type": "string",
"default": "2000ms",
"description": "Timeout duration before failover forcibly advances the recovery_decision_protocol, allowing recovery to proceed even in the presence of unresponsive nodes. Set to 0 to disable failover."
}
},
"required": ["expected_locations"],
"additionalProperties": false
}
},
"required": ["location"],
"additionalProperties": false
}
},
"required": ["network", "command"],
Expand Down
Loading