PHALANXconsensus engine
v1.0.0

integration

embedding phalanx in your own application or using it as a service.

There are two ways to use Phalanx: as a standalone service you deploy and talk to over gRPC, or as a Go library you embed directly into your application.

option 1 — use as a service

Deploy a Phalanx cluster and interact with it via gRPC from any language. This is the simplest approach and works today.

from go

import (
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    "google.golang.org/grpc/encoding"
)

// Register the JSON codec (once, at init)
encoding.RegisterCodec(jsonCodec{})

// Connect to the cluster leader
conn, _ := grpc.Dial("leader:9000",
    grpc.WithTransportCredentials(insecure.NewCredentials()),
    grpc.WithDefaultCallOptions(grpc.CallContentSubtype("json")),
)

// Write
req := map[string]any{"data": encodeCommand("SET", "foo", "bar")}
resp := map[string]any{}
conn.Invoke(ctx, "/phalanx.KV/Propose", req, &resp)

// Read
readReq := map[string]any{"key": "foo"}
readResp := map[string]any{}
conn.Invoke(ctx, "/phalanx.KV/Read", readReq, &readResp)

from python

import grpc
import json

# Custom JSON codec channel
channel = grpc.insecure_channel('leader:9000')

# Use generic unary call with JSON serialization
method = '/phalanx.KV/Propose'
request = json.dumps({
    "data": base64.b64encode(json.dumps({
        "op": "SET", "key": "foo", "value": "bar"
    }).encode()).decode()
}).encode()

response = channel.unary_unary(method)(request)

from any http client (via grpc-web proxy)

If you front Phalanx with a gRPC-Web proxy like Envoy or grpcwebproxy, any HTTP client can interact with the KV service using JSON payloads.

option 2 — embed as a go library

Import Phalanx packages directly into your Go application. This gives you full control over the consensus engine — you can build distributed locks, replicated queues, or coordination services.

import the raft core

The raft/ package is a pure state machine with zero I/O dependencies. You drive it with Tick() and Step() calls:

import (
    "phalanx/raft"
    "phalanx/pb"
)

r := raft.NewRaft(raft.Config{
    ID:               "my-node",
    Peers:            []string{"peer-1", "peer-2"},
    ElectionTimeout:  10,
    HeartbeatTimeout: 3,
    Logger:           slog.Default(),
    Term:             &atomic.Uint64{},
})

// Drive the state machine
r.Tick()                        // advance logical clock
r.Step(incomingMessage)         // process a message
msgs := r.Messages()            // get outbound messages
entries := r.ApplicableEntries() // get committed entries

use the full node

import phalanx "phalanx"

node, _ := phalanx.NewNode(phalanx.NodeConfig{
    ID:               "my-node",
    Peers:            []string{"peer-1", "peer-2"},
    TickInterval:     100 * time.Millisecond,
    ElectionTimeout:  10,
    HeartbeatTimeout: 3,
    DataDir:          "/data/my-app",
    GRPCAddr:         "[::]:9000",
    DebugAddr:        "[::]:8080",
    Logger:           myLogger,
    Term:             &atomic.Uint64{},
})

// Wire peer addresses
node.SetPeerAddr("peer-1", "10.0.0.2:9000")
node.SetPeerAddr("peer-2", "10.0.0.3:9000")

// Run the event loop (blocks until ctx is cancelled)
ctx, cancel := signal.NotifyContext(context.Background(),
    syscall.SIGINT, syscall.SIGTERM)
defer cancel()
node.Run(ctx)

build a custom state machine

The current KV FSM (fsm/kv.go) implements SET and DELETE. To build your own state machine, follow the same pattern:

// Your custom state machine
type DistributedQueue struct {
    mu    sync.RWMutex
    items []string
}

func (q *DistributedQueue) Apply(data []byte) error {
    var cmd QueueCommand
    json.Unmarshal(data, &cmd)
    q.mu.Lock()
    defer q.mu.Unlock()
    switch cmd.Op {
    case "PUSH":
        q.items = append(q.items, cmd.Value)
    case "POP":
        if len(q.items) > 0 {
            q.items = q.items[1:]
        }
    }
    return nil
}

to fully replace the KV FSM with your own, you would modify node.go to accept a StateMachine interface instead of the hardcoded fsm.KV. this is a planned refactor — see the architecture notes on component boundaries.

package reference

packageimport pathwhat you get
raft/phalanx/raftpure consensus state machine — Tick, Step, Propose, HasLeaderQuorum
fsm/phalanx/fsmKV state machine — Apply, Get, Snapshot
storage/phalanx/storageBadgerDB persistence — SaveState, LoadState, AppendLog
network/phalanx/networkgRPC transport — send/receive RPCs, KV channels
discovery/phalanx/discoverySWIM gossip — automatic peer discovery, join/leave events
pb/phalanx/pbRPC types — LogEntry, request/response structs, service interfaces
observability/phalanx/observabilitymetrics + debug HTTP handler
logger/phalanx/loggerstructured slog with atomic term injection

design constraints for embedders