aarondb/identity

identity — durable node trust, mTLS policy, and safe recovery reference surface

This is a policy model, not a socket implementation. A real transport presents peer certificate facts here before it delivers a Raft RPC. Durable adapters persist TrustStore and RecoveryState atomically with their authority files.

Types

pub type Certificate {
  Certificate(
    node: String,
    fingerprint: String,
    issuer: String,
    state: CertificateState,
  )
}

Constructors

  • Certificate(
      node: String,
      fingerprint: String,
      issuer: String,
      state: CertificateState,
    )
pub type CertificateState {
  Active(epoch: Int)
  Rotated(epoch: Int)
  Revoked
}

Constructors

  • Active(epoch: Int)
  • Rotated(epoch: Int)
  • Revoked
pub type PeerError {
  UnknownCertificate
  WrongCertificate(
    expected_node: String,
    certificate_node: String,
  )
  UntrustedIssuer(String)
  InactiveCertificate
  RevokedCertificate
  UnauthorizedMember(String)
  InvalidRpcSize(actual: Int, maximum: Int)
  InvalidReconnectLimit
  ReconnectExhausted(attempts: Int)
  BootstrapDenied
}

Constructors

  • UnknownCertificate
  • WrongCertificate(expected_node: String, certificate_node: String)
  • UntrustedIssuer(String)
  • InactiveCertificate
  • RevokedCertificate
  • UnauthorizedMember(String)
  • InvalidRpcSize(actual: Int, maximum: Int)
  • InvalidReconnectLimit
  • ReconnectExhausted(attempts: Int)
  • BootstrapDenied
pub type RecoveryAlarm {
  UnsafeRecoveryAcknowledged(reason: String)
  TrustMaterialMissing
  MembershipMismatch(
    expected: List(String),
    actual: List(String),
  )
}

Constructors

  • UnsafeRecoveryAcknowledged(reason: String)
  • TrustMaterialMissing
  • MembershipMismatch(expected: List(String), actual: List(String))
pub type RecoveryState {
  RecoveryState(
    alarms: List(RecoveryAlarm),
    force_recovery_used: Bool,
  )
}

Constructors

  • RecoveryState(
      alarms: List(RecoveryAlarm),
      force_recovery_used: Bool,
    )
pub type RpcLimits {
  RpcLimits(maximum_bytes: Int, maximum_attempts: Int)
}

Constructors

  • RpcLimits(maximum_bytes: Int, maximum_attempts: Int)
pub type TrustStore {
  TrustStore(
    cluster: String,
    trusted_issuers: List(String),
    certificates: List(Certificate),
    members: List(String),
  )
}

Constructors

  • TrustStore(
      cluster: String,
      trusted_issuers: List(String),
      certificates: List(Certificate),
      members: List(String),
    )

Values

pub fn admit_peer(
  store: TrustStore,
  expected_node: String,
  fingerprint: String,
  limits: RpcLimits,
  rpc_bytes: Int,
) -> Result(Nil, PeerError)

Mutual TLS admission: issuer, presented identity, active certificate, and committed membership must all agree. Nothing reaches Raft before this passes.

pub fn authorize_bootstrap(
  store: TrustStore,
  requested_members: List(String),
) -> Result(Nil, PeerError)

New-cluster bootstrap may install the initial member set only when the trust store is empty. Existing clusters require committed membership changes.

pub fn clean_recovery() -> RecoveryState
pub fn fingerprint_for(
  store: TrustStore,
  node: String,
) -> option.Option(String)
pub fn force_recovery(
  state: RecoveryState,
  reason: String,
) -> RecoveryState

A forced repair is an operator action, not an automatic healing path. Its durable alarm remains until an operator explicitly records the incident.

pub fn inspect_recovery(
  state: RecoveryState,
  expected_members: List(String),
  actual_members: List(String),
  trust_material_present: Bool,
) -> RecoveryState
pub fn new(
  cluster: String,
  trusted_issuers: List(String),
  members: List(String),
) -> TrustStore
pub fn put_certificate(
  store: TrustStore,
  certificate: Certificate,
) -> TrustStore

Replacing a fingerprint is an explicit rotation. Existing certificates are retained only as Rotated evidence, never as valid transport credentials.

pub fn reconnect(
  limits: RpcLimits,
  attempt: Int,
) -> Result(Nil, PeerError)

attempt is zero based. Callers must stop after this returns exhaustion; this deliberately refuses an unbounded retry loop disguised as resilience.

pub fn revoke(
  store: TrustStore,
  fingerprint: String,
) -> TrustStore
Search Document