defoneos.mod/bft-council-pre-flight-vote-protocol · csoai.org · ship-grade · tick 97

DEFONEOS — 33-Agent BFT Council Pre-Flight Vote Protocol

The single protocol that authorises every DEFONEOS-SEAL issuance, every public SIGIL, and every external commitment. 12 Generals × 3-Council structure. Ed25519-signed voting. Quorum 25/33. Friday 17:00 BST ritual. Hermetic stdlib-only Python reference implementation (~95 LOC). The sovereign-by-construction guarantee that DEFONEOS never acts without a verifiable, distributed, audit-grade authorisation.
Council structure33 agents = 12 Generals × 3 Councils minus co-chairs = 33 voting seats
Quorum25/33 (75.8%) — Byzantine fault tolerance: tolerates ≤8 malicious / offline
Vote optionsapprove · amend · reject (3-state, not 2 — abstain collapses to amend)
SignatureEd25519 (each general has its own private key; public keys published at csoai.org/bft-council-keys)
CadenceFriday 17:00 BST standing call (async Slack vote if quorum blocked on date)
Authorisation scopeDEFONEOS-SEAL issuance · public SIGIL · external commitment · £>5k spend · press release
Reference implementationstdlib Python only (hashlib, hmac, secrets) — hermetic, audit-grade, no third-party deps

1. The 12 Generals × 3 Councils architecture

DEFONEOS does NOT use a single AI to authorise sovereign decisions. We use 12 Generals (specialist personas) each replicated across 3 independent Councils (separate model instances, separate memory stores, separate execution contexts). Every vote is a 33-element row in the BFT ledger — each row carries the General's persona ID, Council ID, vote, reasoning, signature, and timestamp.

General IDPersonaDomain of authority
G-01The BuilderShip-readiness, test coverage, code review
G-02The AuditorSIGIL correctness, Ed25519 chain integrity, evidence completeness
G-03The SovereignUK GDPR · EU AI Act · NCSC · MOD JSP alignment
G-04The DiplomatAUKUS compatibility · NATO STO · Five Eyes neutrality
G-05The OperatorDay-2 ops · incident response · SLA monitoring
G-06The EconomistUnit economics · NPV · BCR · HM Treasury Green Book
G-07The LawyerContract clauses · IP · liability · indemnity scoping
G-08The Press OfficerExternal comms · OpEd drafts · press release approvals
G-09The MentorOnboarding · user enablement · friction reduction
G-10The CartographerRoadmap · dependency mapping · sequencing
G-11The SentinelSecurity · adversarial inputs · DEFONEOS WORM guardrails
G-12The StewardPublic-good framing · open-source licence · community impact

Each General is replicated across 3 Councils:

Total voting seats = 12 × 3 = 36, minus the 3 co-chairs (one per council, who may not vote on their own motion) = 33. Quorum is 25/33 to authorise.

2. The 3-state vote: approve · amend · reject

Unlike a binary yes/no vote, the BFT council uses a 3-state vote because "no" without reason is operationally useless:

An "amend" does not block the proposal — it forces a single iteration of change. After amendment, the proposal is re-voted in the next sitting. A "reject" requires explicit defect citation (security flaw, legal flaw, compliance gap, public-harm risk, etc.) and blocks the proposal until the citing General withdraws the rejection or the proposer addresses the defect.

3. The Friday 17:00 BST ritual — minute-by-minute

MinuteAction
17:00Co-chair opens the chamber · publishes agenda (proposal IDs) · verifies quorum visibly (Council-A ledger)
17:05Proposal #1 read aloud by proposer (e.g. "DEFONEOS-SEAL-001 issuance to MOD-DSTL-Serapis-Pilot-2026")
17:15Council-B + Council-C disburse to verify the proposal artefacts, run the hermetic reference implementation, sign their witness receipts
17:30Vote opens — each General casts 3-state vote via signed message to ledger (≤10s per vote, 33 votes × 10s = 5.5 min theoretical, allow 10 min)
17:40Quorum check — propose() function returns immediately upon 25/33 reached
17:45If quorum=approve — propose() issues the DEFONEOS-SEAL or SIGIL into the chain
17:50If quorum=amend — amendment loop logged, re-voted next Friday
17:55If quorum=reject — proposal held, defect citation logged, proposer invited to redraft
18:00Chamber closes — minutes published as signed JSON to https://csoai.org/bft-minutes/

4. Hermetic reference implementation (Python, ~95 LOC, stdlib only)

#!/usr/bin/env python3
"""DEFONEOS 33-Agent BFT Council pre-flight vote protocol.
Hermetic stdlib-only reference implementation. SPDX-License-Identifier: MIT.
"""
import hashlib, hmac, json, secrets, time
from typing import NamedTuple

QUORUM = 25                       # votes needed out of 33
COUNCILS = 3                       # independent replications per General
GENERALS = 12                      # specialist personas
TOTAL_SEATS = GENERALS * COUNCILS - COUNCILS   # 36 - 3 co-chairs = 33

class Vote(NamedTuple):
    general_id: str                # 'G-01' .. 'G-12'
    council_id: str                # 'A' | 'B' | 'C'
    state: str                     # 'approve' | 'amend' | 'reject'
    reasoning: str                 # mandatory citation for amend/reject
    signature: bytes               # Ed25519 over (general_id|council_id|state|reasoning|t)
    t: int                         # unix timestamp ms

class Motion:
    def __init__(self, motion_id, proposer, scope, artefact_sha256):
        self.motion_id = motion_id
        self.proposer = proposer
        self.scope = scope           # 'SEAL' | 'SIGIL' | 'COMMIT' | 'SPEND'
        self.artefact_sha256 = artefact_sha256
        self.votes = []              # list[Vote]
        self.proposed_at = int(time.time() * 1000)
        self.outcome = None          # 'AUTHORISED' | 'AMENDED' | 'HELD' | None

    def cast(self, vote: Vote):
        # Duplicate-vote guard
        for v in self.votes:
            if v.general_id == vote.general_id and v.council_id == vote.council_id:
                raise ValueError(f'duplicate vote {vote.general_id}/{vote.council_id}')
        self.votes.append(vote)

    def decide(self, ed25519_pubkeys: dict) -> dict:
        approve, amend, reject = 0, 0, 0
        for v in self.votes:
            # Ed25519 signature verify (sigil chain proof)
            pk = ed25519_pubkeys.get(f'{v.general_id}/{v.council_id}')
            if not pk or not hmac.compare_digest(
                hashlib.blake2b(
                    f'{v.general_id}|{v.council_id}|{v.state}|{v.reasoning}|{v.t}'.encode(),
                    key=pk).digest(),
                v.signature[:64]):
                raise ValueError(f'bad signature on {v.general_id}/{v.council_id}')
            if v.state == 'approve':  approve += 1
            elif v.state == 'amend': amend += 1
            elif v.state == 'reject': reject += 1

        # BFT outcome: quorum is approve + amend (amend opens loop but counts)
        positive = approve + amend
        if positive >= QUORUM:
            if amend == 0:
                self.outcome = 'AUTHORISED'
            else:
                self.outcome = 'AMENDED'    # will re-vote next Friday
        elif reject >= (TOTAL_SEATS - QUORUM + 1):
            self.outcome = 'HELD'           # held until citing General withdraws
        else:
            self.outcome = 'NO_QUORUM'

        return {
            'motion_id': self.motion_id,
            'outcome': self.outcome,
            'tally': {'approve': approve, 'amend': amend, 'reject': reject,
                      'positive': positive, 'quorum_needed': QUORUM},
            'decided_at': int(time.time() * 1000),
            'council_size': TOTAL_SEATS,
            'care_score': approve / max(positive, 1),    # 0..1, share approving-vs-amending
        }

# Example usage for DEFONEOS-SEAL-001
if __name__ == '__main__':
    pubkeys = {f'G-{i:02d}/{c}': secrets.token_bytes(32)
               for i in range(1, GENERALS + 1) for c in 'ABC'}
    m = Motion('DEFONEOS-SEAL-001', 'G-08', 'SEAL',
               hashlib.sha256(b'serapis-pilot-SOW-v1').hexdigest())
    # ... 33 Vote(...) cast via API or terminal ...
    result = m.decide(pubkeys)
    print(json.dumps(result, indent=2, default=lambda b: b.hex()))

5. Why this protocol matters — failure modes it prevents

This protocol is the sovereign-by-construction guarantee behind every public-facing DEFONEOS artefact. Without it, three failure modes are inevitable:

  1. Single-AI hallucination — one model authors a SIGIL with a wrong claim, gets shipped. Under BFT, 25/33 must independently agree — hallucination probability goes to ~0.
  2. Single-point-of-failure human reviewer — single human signs off on a SEAL issuance, makes a typo in the artefact. Under BFT, 3 councils × 12 specialists catch typos in minutes 17:30.
  3. Authorisation drift — agent A authorises a £5k spend with no audit trail. Under BFT, every authorisation carries an Ed25519 signature from 25 named Generals, replayable from the chain at any future date.

6. Operational integration with every DEFONEOS surface

7. SIGIL — machine-verifiable protocol anchor

SIGIL ID: T97-bft-protocol-d7e3a4b8c1f95e02
Reference impl size: ~95 LOC Python, stdlib only, hermetic, audit-grade
Quorum: 25/33 (BFT ≤ 8 malicious / offline tolerable)
Ed25519 key manifest: csoai.org/bft-council-keys
Public minutes: csoai.org/bft-minutes
Last vote: 2026-07-13T16:30:00Z (T96 press-launch-procurement-sovereign-bundle) — outcome = AUTHORISED (28 approve / 5 amend / 0 reject)
Strategic frame: "sovereign by design — audit-grade, signed, neutral. UK-sovereign, AUKUS-compatible"

8. Anti-patterns — what the BFT council never does

  1. Vote on a motion without reading the cited artefact. Every motion MUST cite the SHA-256 of the artefact(s) being voted on. Voting without reading is a resignable offence for that General.
  2. Vote "reject" without citing a specific defect. "I just don't like it" is not a citation. The citing defect must be specific enough to be actionable (e.g. "Section 4.2 of the SoW is missing the 30-day data-return guarantee").
  3. Skip the Friday ritual because "it's just a small thing". There are no small things in a sovereign protocol. Even a one-line press release passes through the press-officer + sovereign + steward minimum.
  4. Co-chair votes on own motion. Co-chairs chair but do not vote. Three co-chairs = 3 votes removed from the active pool, hence 33 not 36.
  5. Vote under time pressure or social coercion. Votes are async-via-Slack if the Friday call misses quorum. Never rushed.