Skip to content

HashiCorp Raft Engine Bridge

NOOSChain can now be configured for a pluggable external-process Raft engine path with RAFT_ENGINE=hashicorp-go.

This bridge keeps the architectural split strict:

  • NOOSChain TypeScript owns transactions, block construction, deterministic execution, SMT/state roots, permissions, encryption, snapshots, and replay verification.
  • The HashiCorp Raft sidecar owns the external Raft-process boundary, leader status, commit ordering API, and sidecar lifecycle.
  • Raft log entries are opaque NOOSChain block DTO envelopes, never raw database mutations.

Noosraft remains available with RAFT_ENGINE=noosraft, and single_node mode is unchanged.

Configuration

Use with:

bash
NOOS_CONSENSUS_MODE=raft
RAFT_ENGINE=hashicorp-go
HASHICORP_GO_RAFT_BINARY_PATH=external/raft-go/bin/hashicorp-raft
HASHICORP_GO_RAFT_HTTP_BIND=http://127.0.0.1:9001
HASHICORP_GO_RAFT_BIND=127.0.0.1:10001
HASHICORP_GO_RAFT_NODE_ID=node-a
HASHICORP_GO_RAFT_CLUSTER_ID=noos-dev-go-raft
HASHICORP_GO_RAFT_DATA_DIR=.noos-raft-go/node-a
HASHICORP_GO_RAFT_CALLBACK_BASE=http://127.0.0.1:7001
HASHICORP_GO_RAFT_PEERS='[{"nodeId":"node-b","httpUrl":"http://127.0.0.1:9002","raftAddress":"127.0.0.1:10002","callbackBaseUrl":"http://127.0.0.1:7002"}]'

Windows paths are supported because TypeScript starts the sidecar with child_process.spawn() and does not use shell interpolation.

Process Model

The TypeScript adapter creates a GoRaftProcessEngine, which starts the configured Go binary and communicates with it over localhost HTTP:

  • POST /raft/propose-block
  • GET /raft/status
  • GET /raft/leader
  • GET /raft/log-entries?from=<index>&to=<index>
  • POST /raft/shutdown

Committed entries are returned to NOOSChain via the normal Fastify app route:

  • POST /internal/raft/committed

The committed entry is signed with node-auth headers and then applied by the existing deterministic NOOSChain block execution path. If block hash or state root verification fails, the normal incident/conflict handling remains responsible for protecting finalized history.

The log-entry endpoint is a recovery/catch-up boundary for the TypeScript process. If a committed callback is missed while the TypeScript node is stopped or the sidecar is restarted, GoRaftProcessEngine fetches committed opaque block entries from the sidecar and replays them through the same deterministic commitRaftBlockEntry path. Replaying an already-applied entry is idempotent when the block hash and state root match; a same-height different hash still triggers incident handling.

Supervision And Restart

GoRaftProcessEngine supervises the sidecar process:

  • checks sidecar HTTP health periodically
  • detects unexpected child process exit
  • marks consensus status degraded/dead/restarting while unhealthy
  • records an incident when the sidecar exits unexpectedly
  • restarts the sidecar automatically with bounded exponential backoff
  • preserves HashiCorp Raft state because logs/stable store/snapshots live in HASHICORP_GO_RAFT_DATA_DIR
  • replays committed sidecar log entries on startup/restart so NOOSChain catches up after missed callbacks

Operator observability includes:

  • externalProcessStatus
  • externalProcessPid
  • externalProcessUptimeMs
  • externalProcessRestartCount
  • externalProcessLastExitAt
  • externalProcessLastExitCode
  • externalProcessLastExitSignal
  • externalProcessLastError
  • externalProcessNextRestartAt

Automatic restart is process supervision only. It does not change NOOSChain finalized history, validator membership, state roots, or Raft voter sets.

Deployment examples, port layout, build commands, backup guidance, and local cluster startup/shutdown steps are documented in HashiCorp Raft Deployment.

Runtime Membership Changes

Governance validator transactions define the intended validator set, but they do not automatically reconfigure HashiCorp Raft. Runtime membership changes are an explicit operator action:

  1. commit governance changes
  2. inspect GET /governance/runtime-membership/reconciliation-plan
  3. approve specific operations
  4. execute POST /governance/runtime-membership/execute
  5. verify convergence and replay health

The TypeScript executor calls the HashiCorp Raft sidecar membership API wrappers:

  • GET /raft/membership
  • POST /raft/membership/add-voter
  • POST /raft/membership/add-nonvoter
  • POST /raft/membership/remove-voter

The sidecar maps those calls to HashiCorp Raft AddVoter, AddNonvoter, and RemoveServer. The executor applies one operation at a time, waits for a stable leader after each operation, and stops on unsafe or failed states. For example, changing voters from [a,b,c] to [a,c,d] is sequenced as add d, verify stability, then remove b.

The HashiCorp Raft sidecar membership endpoints are local operational control endpoints. In normal deployments, call them through the protected TypeScript governance API and bind the sidecar to localhost or a private management network. If exposed across hosts, use the TLS/mTLS settings documented in TLS/mTLS. When NOOS_OPERATOR_TOKEN is configured, the sidecar membership endpoints also require Authorization: Bearer <token>; the TypeScript executor forwards that token when calling the sidecar.

Security Boundary

The Fastify internal callback route validates node-auth headers. The TypeScript process passes the local node private key to the Go child process through its process environment, not through shell interpolation or command-line arguments. The sidecar uses that key only to sign callback requests; NOOSChain user keys, bucket DEKs, plaintext payloads, and database credentials are never sent to the sidecar.

The HashiCorp Raft sidecar treats log entries as opaque JSON.

TLS Boundaries

HashiCorp Go Raft uses three distinct network surfaces:

  • TypeScript HTTP APIs for NOOSChain sync, callbacks, governance, and observability, controlled by NOOS_TLS_*.
  • HashiCorp sidecar HTTP control/callback traffic, controlled by GO_RAFT_TLS_*.
  • HashiCorp Raft TCP replication traffic, controlled by GO_RAFT_TCP_TLS_*.

Raft TCP TLS encrypts the HashiCorp replication channel and can require mTLS with GO_RAFT_TCP_MTLS_REQUIRED=true. The MVP verifies peer certificates against the configured CA. It does not yet perform Raft nodeId-to-certificate identity binding beyond normal TLS SAN/server-name checks.

HashiCorp Raft Status

The HashiCorp Raft sidecar uses github.com/hashicorp/raft with:

  • TCP Raft transport
  • BoltDB log/stable store through github.com/hashicorp/raft-boltdb/v2
  • file snapshot store
  • opaque FSM apply callback into NOOSChain

HashiCorp snapshots are sidecar log snapshots. They are not yet integrated with NOOSChain state snapshots/checkpoints.

Limitations

  • MVP/development bridge only.
  • No Byzantine protection.
  • Dynamic membership requires explicit operator approval; governance transactions never auto-mutate runtime voters.
  • No OpenBFT or CometBFT integration.
  • HashiCorp Raft transport/log-store wiring is implemented for the MVP bridge but not production-hardened yet.
  • NOOSChain snapshot/checkpoint integration with HashiCorp InstallSnapshot remains future work.

Audience-first NOOSChain documentation.