Raft Membership Reconciliation
NOOSChain separates validator governance from runtime Raft membership.
Governance validator state is consensus state. It is changed only by signed, protocol-versioned governance transactions such as registering, suspending, reactivating, retiring, or updating a validator.
Runtime Raft membership is deployment state. It is the set of voters currently configured in Noosraft or the HashiCorp Raft sidecar. Governance transactions do not automatically mutate runtime Raft membership. Operators must review a reconciliation plan and explicitly execute approved runtime changes.
Purpose
The reconciliation tooling compares:
- active validators from consensus governance state
- currently running Raft voters from runtime consensus status
- local
trusted_peersused for HTTP sync, gossip, payload backfill, and operator transport
It then produces a safe operator plan. The plan is guidance by default. For the HashiCorp Raft sidecar, an operator can explicitly execute approved plan steps through the protected TypeScript governance API, which calls HashiCorp Raft membership APIs.
APIs
Protected chain-admin APIs:
GET /governance/runtime-membershipGET /governance/runtime-membership/reconciliation-planPOST /governance/runtime-membership/execute
Operator-token APIs:
GET /operator/membership/reconciliation-planPOST /operator/membership/execute
The operator membership plan is the preferred operational view because it checks all three planes together:
governance active validators
runtime Raft membership
trusted_peersThe execute endpoint accepts explicit operator-approved actions such as:
add_trusted_peerupdate_trusted_peerdisable_trusted_peeradd_runtime_voteradd_runtime_nonvoterremove_runtime_voter
It does not submit governance transactions. If a node should become an active validator, the operator must first submit and finalize the appropriate signed governance transaction.
The execute endpoint accepts only explicit operator-approved actions. It does not infer approval from governance state:
{
"actions": [
{
"action": "add_runtime_voter",
"nodeId": "validator-d",
"raftAddress": "127.0.0.1:12004",
"operatorApproved": true
}
]
}For the HashiCorp Raft sidecar, the executor uses the sidecar membership control endpoints:
GET /raft/membershipPOST /raft/membership/add-voterPOST /raft/membership/add-nonvoterPOST /raft/membership/remove-voter
Operators should normally call the protected TypeScript governance API rather than the sidecar directly. The sidecar control API should be bound to a local or private management interface and protected with the existing TLS/mTLS deployment settings where exposed outside localhost. If NOOS_OPERATOR_TOKEN is set, the sidecar membership endpoints require Authorization: Bearer <token>, and the TypeScript executor forwards the configured token.
Operator observability:
GET /node/observability/overviewGET /node/observability/consensus
The observability response includes:
governanceValidatorSetHashruntimeValidatorSetHashmembershipInSyncreconciliationWarningsmissingRuntimeValidatorsunexpectedRuntimeValidatorsendpointMismatches
Plan Contents
The reconciliation plan reports:
missingRuntimeValidators: active governance validators that are not runtime Raft voters.unexpectedRuntimeValidators: runtime Raft voters that are not active in governance state.endpointMismatches: validators where governance endpoint metadata differs from runtime peer endpoint configuration.runtimeConfigMismatches: duplicate runtime node IDs or incomplete governance endpoint metadata.votingPowerMismatches: Raft does not use governance voting power in this MVP; future BFT backends may.safeOperations: manual-only operator recommendations.
The operator membership plan additionally reports:
missingTrustedPeers: active governance validators missing fromtrusted_peers.trustedPeerEndpointMismatches: trusted peer HTTP endpoints that differ from governance endpoint metadata.trustedPeerModeMismatches: active validators configured as observer peers.unexpectedTrustedPeers: trusted peers that are not active governance validators. These are not automatically disabled because they may be intentional observers, bootstrap peers, or repair peers.governanceActionsRequired: guidance for cases that require signed governance transactions rather than operator-only transport changes.
Example recommendation:
{
"action": "add_runtime_voter",
"nodeId": "validator-d",
"raftAddress": "127.0.0.1:12004",
"requiresRuntimeChange": true,
"requiresOperatorApproval": true,
"requiresQuorum": true,
"requiresJointConsensus": true,
"manualOnly": true,
"safeSequenceStep": 1
}Runtime Execution
Runtime execution is currently available for the HashiCorp Raft engine. It uses HashiCorp Raft's built-in membership APIs:
AddVoterAddNonvoterRemoveServer
The executor applies one approved operation at a time. Before each operation it checks that a leader exists, the cluster is reachable through the sidecar, and the requested action is safe enough for this MVP. After each operation it waits for the sidecar to report a stable leader and then refreshes the runtime membership view.
For a replacement such as [a,b,c] -> [a,c,d], the safe plan is:
- add
das a voter - verify the cluster is stable as
[a,b,c,d] - remove
b - verify the cluster is stable as
[a,c,d]
The executor deliberately refuses direct quorum-shrinking operations such as removing a voter from a three-voter cluster without first adding a replacement.
Safe Workflow
- Commit governance transactions first.
- Query
GET /operator/membership/reconciliation-planor usenpm run noos -- governance operator-membership plan --json. - Review the manual plan across governance, Raft runtime membership, and
trusted_peers. - Execute only approved trusted-peer/runtime actions with
POST /operator/membership/executeornpm run noos -- governance operator-membership execute --plan-file actions.json --yes. - Verify cluster convergence and chain/replay health.
- Re-run reconciliation until the operator plan reports
inSync=true.
Tutorials
These examples use the operator CLI. The same workflow can be performed through the HTTP APIs listed above.
Set the operator target first:
$env:NOOS_CLI_BASE_URL="https://127.0.0.1:7001"
$env:NOOS_OPERATOR_TOKEN="<operator-token>"export NOOS_CLI_BASE_URL="https://127.0.0.1:7001"
export NOOS_OPERATOR_TOKEN="<operator-token>"1. Add A Validator Node
Adding a validator is a three-plane operation:
governance validator state
trusted_peers HTTP transport
runtime Raft voter membershipStep 1: submit and finalize a signed governance transaction.
Use the normal transaction flow to submit REGISTER_VALIDATOR_NODE with the new node metadata:
{
"nodeId": "validator-d",
"organizationId": "org-validator-d",
"publicKey": "<validator-d-node-public-key>",
"endpoint": "https://validator-d.example.com:7001",
"raftHttpUrl": "https://validator-d.example.com:9101",
"raftTcpAddress": "validator-d.example.com:12001",
"role": "validator",
"votingPower": 1,
"metadata": {}
}This must be signed by a chain:admin actor. The operator membership CLI does not create governance validators directly.
Step 2: inspect the operator reconciliation plan.
npm run noos -- governance operator-membership plan --jsonExpected findings:
missingTrustedPeerscontainsvalidator-d.raftMembershipPlan.missingRuntimeValidatorscontainsvalidator-d.safeOperationsincludesadd_trusted_peer.safeOperationsincludesadd_runtime_voter.
Step 3: create an explicit action file.
Example add-validator-d-actions.json:
{
"actions": [
{
"action": "add_trusted_peer",
"nodeId": "validator-d",
"baseUrl": "https://validator-d.example.com:7001",
"operatorApproved": true
},
{
"action": "add_runtime_voter",
"nodeId": "validator-d",
"raftAddress": "validator-d.example.com:12001",
"operatorApproved": true
}
]
}Step 4: execute the approved operator actions.
npm run noos -- governance operator-membership execute --plan-file add-validator-d-actions.json --yes --jsonStep 5: verify alignment.
npm run noos -- governance operator-membership plan --json
npm run noos -- chain verify
npm run noos -- chain replay-verifyThe desired result is:
- governance active validators include
validator-d trusted_peersincludesvalidator-dastrusted- runtime Raft voters include
validator-d - no membership warnings remain for
validator-d
2. Add An Observer Node
Observers are transport peers, not governance validators and not Raft voters. They sync blocks and may serve/read allowed availability-layer payloads, but they do not vote in Raft.
For a sync-only observer, do not submit REGISTER_VALIDATOR_NODE and do not add the node as a Raft voter.
Add the observer as a trusted peer with trustMode: observer:
npm run noos -- peers list --jsonThen call the operator peer API, or use the CLI if peer creation is exposed in your current build. HTTP example:
POST /peers
Authorization: Bearer <operator-token>
Content-Type: application/json
{
"id": "observer-a",
"nodeId": "observer-a",
"baseUrl": "https://observer-a.example.com:7001",
"trustMode": "observer"
}After adding it:
npm run noos -- peers list --json
npm run noos -- governance operator-membership plan --jsonAn observer may appear in trusted_peers, but it should not appear in the governance active validator set or runtime Raft voters. If the observer is configured with trustMode: observer, the operator plan should not recommend adding it as a Raft voter.
If an observer is accidentally added as trustMode: trusted, the plan may flag it as an unexpected trusted peer. Fix that by updating/re-adding it as an observer peer or disabling it if it is not intended.
3. Remove A Validator Or Observer Node
Removal depends on the node role.
Remove A Validator
Validator removal should be staged so the cluster never loses quorum unexpectedly.
Step 1: submit and finalize a governance transaction.
Use one of:
SUSPEND_VALIDATORfor temporary removal from the active governance set.RETIRE_VALIDATORfor permanent removal. Retired validators cannot be reactivated.
This must be signed by a chain:admin actor.
Step 2: inspect the operator plan.
npm run noos -- governance operator-membership plan --jsonExpected findings:
- runtime Raft may still list the validator as a voter.
- the plan may include
remove_runtime_voter. trusted_peersmay still contain the node.
Step 3: execute runtime removal only when quorum-safe.
Example remove-validator-b-actions.json:
{
"actions": [
{
"action": "remove_runtime_voter",
"nodeId": "validator-b",
"operatorApproved": true
}
]
}npm run noos -- governance operator-membership execute --plan-file remove-validator-b-actions.json --yes --jsonThe executor refuses unsafe quorum-shrinking operations in the MVP. For example, removing a voter from a three-voter cluster without first adding a replacement is rejected.
Step 4: disable or reclassify the trusted peer.
If the retired/suspended node should not be used for sync, gossip, or backfill:
npm run noos -- peers quarantine validator-b --reason "validator suspended"
npm run noos -- peers ban validator-b --reason "validator retired" --yesOr disable the trusted peer through the operator API:
POST /peers/validator-b/disable
Authorization: Bearer <operator-token>If the node should continue as a sync-only observer, keep it in trusted_peers, but configure it as trustMode: observer rather than a runtime Raft voter.
Step 5: verify.
npm run noos -- governance operator-membership plan --json
npm run noos -- chain verify
npm run noos -- chain replay-verifyRemove An Observer
Observers are local transport peers. Removing an observer does not require a governance transaction and does not require Raft membership changes.
Disable the peer:
POST /peers/observer-a/disable
Authorization: Bearer <operator-token>Or ban/quarantine it if the removal is due to operational risk:
npm run noos -- peers quarantine observer-a --reason "operator removal"
npm run noos -- peers ban observer-a --reason "operator removal" --yesThen verify:
npm run noos -- peers list --json
npm run noos -- governance operator-membership plan --jsonThe observer should be absent or disabled in trusted_peers, absent from runtime Raft voters, and absent from governance active validators.
Current Limits
- No automatic Raft membership mutation from governance transactions.
- No automatic trusted-peer mutation from governance transactions.
- Runtime mutation is explicit and operator-approved.
- No fully autonomous orchestration yet.
- No Byzantine slashing or staking.
- Peer scoring remains local operational defense and never changes validator governance state.
Future work can add richer autonomous orchestration, backend-specific joint consensus workflows, or BFT validator-set update machinery while preserving the same governance/runtime boundary.