Skip to content

Consensus Mission Service

Status & scope

  • Stage: DRAFT — service layer (implementation target: REST per CL-06, NOT the parallax library)
  • Depends on: multi-contributor-combination (combiner), consensus-session, dissent, CL-06 (REST-only), CL-09 (evidence chain), CL-10 (multi-tenant)
  • Milestone: P1 — gates Q3 2026 GA; required for defense / coalition deployments

Purpose

SPEC-28 / SPEC-36 / SPEC-30 define combiner math, session state machine, and machine dissent. They need a home — a named service that consumes contributions, drives the state machine, calls the combiner, emits dissent, and manages cross-tenant federation handoff.

This spec names that service: Consensus Mission Service. It is the JICD §4.4.5 Mission Service Node Axonis registers with a CS Hub when peering with a JICD deployment, and it is the REST-callable consensus surface in any non-JICD deployment.

The service is REST-only (per CL-01: no MCP outside Profile A). It implements the consensus session endpoints from CL-06 and is the reference implementation of the SPEC-36 state machine.

Relationship to SPEC-PLATFORM-04

This service is net-new relative to the six services enumerated in SPEC-PLATFORM-04 (Parallax, Prism, Cortex, Unified Dataspace, Oracle, Sentinel). It is NOT a subset of Parallax or Prism, despite consuming their outputs.

Rationale for separate service rather than Parallax extension:

  1. Parallax's domain (per PLATFORM-04) is fusion entity resolution — two-record pair scoring, blocking, clustering. The 19 listed Parallax tools are CRUD + workflow + introspection over Lens, LensBinding, EntityMatch, EntityCluster, FusionRun. Adding session-shaped multi-contributor consensus to that surface conflates pair-level fusion with N-party combination.
  2. Consensus has its own object model (ConsensusSession, MachineDissent) and its own ES index — clean separation from Parallax's fusion index.
  3. Profile-aware deployment: Consensus Mission Service runs in T1/T2/T4/T5 (where N-party combination happens); does NOT run in T3 Lookup-Light Edge Nodes (where there's only one matching node and no N-party combination). Parallax has different profile semantics.
  4. JICD §4.4.5 Mission Service registration is a distinct Capability advertisement — separate component is the cleaner JICD composition.

Action required: SPEC-PLATFORM-04 amend to add this service as Service 7 alongside Sentinel:

Service 7: Consensus Mission Service Port 8400. /api/v2. Domain: multi-contributor consensus combiner, M-of-N quorum, machine dissent. See SPEC-31 for full specification.

Until that amendment lands, Consensus Mission Service operates as a peer-named service following SPEC-PLATFORM-02's contract — same Helm/CI/auth/health pattern as the six existing services, just not yet listed in PLATFORM-04's enumeration.

Scope

The Consensus Mission Service is responsible for:

  1. Hosting the consensus session lifecycle (PROPOSED → PENDING_QUORUM → RATIFIED | IN_CONFLICT | WITHDRAWN)
  2. Invoking the SPEC-28 combiner on every contribution
  3. Emitting MachineDissent records (SPEC-30) on IN_CONFLICT entry
  4. Emitting Evidence Blocks (CL-09) on RATIFIED entry
  5. Emitting lens emissions (CL-06) downstream when ratified results should propagate to consumers
  6. Federation handoff — accepting cross-deployment contributions per CL-07 transport adapters
  7. Tenant-scoping all sessions per CL-10
  8. Surfacing observability (metrics, audit views) via REST

It is NOT responsible for:

  • Pair-level scoring (Parallax service does this)
  • Lens execution (Parallax / Prism services do this)
  • Vulnerability code lookup or domain-specific result enrichment (cartridge / customer adapter handles this)
  • Human attestation UX (ADI/DES handles this)
  • Anything that runs in Profile B Edge Nodes (the Lookup-Light pattern doesn't use this service)

Service Anatomy (per SPEC-PLATFORM-02 contract)

consensus-service/
  server/
    __init__.py
    __main__.py                 # Starlette: /api/v2, /health, /service-info
    api/
      __init__.py
      routes.py                 # FastAPI REST endpoints
      schema/
        consensus_objects.yml   # OpenAPI component schemas
    commands.py                 # Command layer (REST + MCP if Profile A)
    mcp/                        # Profile A only (per CL-01); skipped otherwise
      server.py
      tools.py                  # Mirror of REST surface as MCP tools
  charts/consensus-service/
  pyproject.toml                # Depends on axonis-core, parallax (for combiner)

Profile-aware: in Profile A, the MCP surface is mounted at /agentspace. In Profiles B/C/D, only /api/v2 is exposed.

REST Surface (matches CL-06)

Method Path Purpose
POST /api/v2/consensus/session Create session
GET /api/v2/consensus/session/{id} Get session state
POST /api/v2/consensus/session/{id}/contribution Add contribution
POST /api/v2/consensus/session/{id}/resolve Force resolve attempt
POST /api/v2/consensus/session/{id}/cancel Cancel session → WITHDRAWN
GET /api/v2/consensus/session/{id}/events SSE stream (optional)
GET /api/v2/dissent List dissent records
GET /api/v2/dissent/{id} Get specific dissent
GET /api/v2/dissent/by-session/{session_id} All dissent for a session
GET /api/v2/dissent/{id}/explain Human-readable narrative
POST /api/v2/federation/contribution Accept contribution from a federation peer (signed, per CL-07)
GET /api/v2/health Liveness
GET /api/v2/service-info Service metadata per SPEC-PLATFORM-02

Command Layer

The command layer is the canonical implementation of the SPEC-36 state machine and is shared by REST endpoints (and by MCP tools in Profile A).

# consensus-service/server/commands.py

from parallax.ops.consensus_session import (
    create_session as _create, add_contribution as _add,
    SessionState, QuorumPolicy,
)
from parallax.ops.dissent import emit_machine_dissent
from parallax.ops.fusion.combination import Contribution
from axonis_core.userspace.fusion import ConsensusSession, MachineDissent
from axonis_core.gateway.transport import get_transport


def create_session_cmd(lens_id, lens_version, policy_dict, classification, token_payload):
    policy = QuorumPolicy(**policy_dict)
    return _create(
        lens_id=lens_id,
        lens_version=lens_version,
        policy=policy,
        tenant_id=token_payload.tenant_id,
        classification=classification,
    )


def add_contribution_cmd(session_id, contribution_dict, token_payload):
    # Tenant boundary check (CL-10)
    session = ConsensusSession().read(uid=session_id)
    if session["tenant_id"] != token_payload.tenant_id:
        raise PermissionError("session belongs to different tenant")

    contrib = Contribution(**contribution_dict)
    return _add(session_id, contrib)


def federation_contribution_cmd(payload, transport_message):
    """Called when a federation peer pushes a contribution via CL-07 transport.
    The transport adapter validates the signature; this command verifies tenant
    routing and delegates to the same _add() the local REST path uses."""

    # Verify cross-tenant routing per CL-10 federation-as-tenant
    sender_tenant = transport_message.sender_tenant_id
    target_tenant = transport_message.receiver_tenant_id
    session_id = payload["session_id"]

    session = ConsensusSession().read(uid=session_id)
    if session["tenant_id"] != target_tenant:
        raise PermissionError("federation contribution targets wrong tenant")

    # Federate-as-tenant: the sending peer is a contributor identified by sender_tenant
    contrib = Contribution(
        contributor_id=sender_tenant,
        pair_score=payload["pair_score"],
        accuracy=payload["accuracy"],
        credibility=payload["credibility"],
        signer_key_id=transport_message.signer_key_id,
        classification=payload["classification"],
    )
    return _add(session_id, contrib)

REST endpoints in api/routes.py are thin wrappers that authenticate via CL-02 provider, delegate to commands, return JSON.

Federation Handoff (CL-07)

When a deployment runs the Consensus Mission Service and federates with peers (other Axonis deployments, JICD CS hubs, partner systems), contributions can arrive two ways:

Local contribution

A local service (Parallax pair-scorer, Prism lens executor, Edge Node submission) calls POST /api/v2/consensus/session/{id}/contribution directly. Authenticates via the local CL-02 provider.

Federation contribution

A peer pushes a contribution via the configured CL-07 transport (mTLS, SILKWAVE, AMQP, file-drop CDS). The transport adapter: 1. Validates the message signature against the peer's pinned key 2. Validates classification per CL-08 routing rules 3. Hands off to federation_contribution_cmd() which adds to the session

Federate-as-tenant (CL-10) means the sending peer's sender_tenant_id becomes the contributor_id in the session. Per-federate lineage is preserved via the signer_key_id and the parent block IDs.

JICD CS Composition

When deployed alongside JICD CS:

  1. The Consensus Mission Service registers with the local CS Hub as a Mission Service Node (JICD §4.4.5)
  2. Capability message declares: "I host consensus sessions; I accept contributions of type X over lens Y"
  3. Other CS Nodes (Processor Nodes running lens execution, Tasker Nodes coordinating mission) discover the service via standard JICD RCS
  4. Contributions arrive via SILKWAVE-transported FusionMessages (CL-07 SilkwaveAdapter); the same federation_contribution_cmd path handles them

This is how Axonis composes into JICD as a Mission Service. The Capability message Mike's team sees in their JICD dashboard names this service explicitly, and any Tasker Node can subscribe to its Status feed for ratification events.

Service-Info Response

{
  "name": "consensus-service",
  "version": "1.0.0",
  "description": "Multi-contributor consensus combiner + quorum + machine dissent",
  "mcp_path": "/agentspace",       
  "health_path": "/health",
  "api_path": "/api/v2",
  "tools_count": 0,                 
  "resources_count": 0,
  "capabilities": [
    "consensus-session",
    "machine-dissent",
    "federation-contribution",
    "spec-28-combiner",
    "spec-29-quorum",
    "spec-30-dissent"
  ],
  "deployment_profile": "<from CL-01>",
  "supported_combination_methods": ["weighted_average", "dempster_shafer"],
  "supported_transports": ["<from CL-07 configuration>"]
}

In Profile A, tools_count reflects MCP tools mirroring the REST surface. In Profiles B/C/D, tools_count: 0 and mcp_path is omitted.

Observability

Endpoint Purpose
GET /api/v2/metrics Prometheus-formatted: session counts by state, dissent emissions, combiner latencies, federation contribution rates
GET /api/v2/health Liveness + dependency check (ES, transport adapter)
GET /api/v2/audit Recent session lifecycle events for human / SIEM consumption

Metrics taxonomy: - consensus_session_total{state} — counter - consensus_session_active{state} — gauge - consensus_combiner_seconds{method} — histogram - consensus_dissent_total{lens_id} — counter - federation_contribution_total{transport, peer} — counter

In Profile C/D, metrics endpoints stay inside the deployment boundary per CL-01 invariant 6.

Helm + CI (per SPEC-PLATFORM-02)

Standard Bitnami chart at charts/consensus-service/: - Port 8400 (next available in the platform's port assignments) - Resources: 500m–2 CPU, 1–4 GB RAM (combiner is mostly arithmetic; ES queries dominate) - HPA on request rate - ServiceAccount + ClusterRoleBinding for ES access

CI pipeline matches the standard template: qa, package, deploy, release, security.

Invariants

  1. Service is REST-first. All business logic reachable via REST in every profile.
  2. Profile-aware MCP. MCP surface mounts only in Profile A; non-existent in Profiles B/C/D.
  3. All sessions are tenant-scoped. Cross-tenant operations route through federation, never direct.
  4. State machine logic lives in parallax/ops/consensus_session.py (per SPEC-36). This service is a thin REST layer over that logic.
  5. Combiner logic lives in parallax/ops/fusion/combination.py (per the multi-contributor-combination spec). This service does not duplicate.
  6. Federation contributions verify signatures before mutation. No unsigned cross-deployment contribution is accepted.
  7. Service registers with JICD CS Hub when configured to compose (JICD-deployed sites only). Capability + Status messages match JICD §4.4.5 specification.
  8. No agentic behavior. This service does not autonomously decide to call tools or make decisions. It executes the state machine on inputs and emits results.

Deployment Topologies

Topology (CL-03) Where this service runs
T1 single cluster (Profile A) Yes, alongside other services; MCP active
T2 multi-tenant cloud (Profile A) Yes, single instance with tenant-scoped sessions
T3 customer on-prem (Profile B) Optional — needed only if customer hosts a federated multi-INT use case (rare for VRS-style deployments)
T4 coalition federated (Profile C) Yes, one per partner; peers contribute via CL-07 transport
T5 classified air-gapped (Profile D) Yes; federation via accredited cross-domain interfaces only

For Lookup-Light Edge Node deployments (VRS / commercial centralized matching), this service runs at the central matching node, not at the customer edge. Customer Edge Nodes don't host consensus sessions because there's no N-party combination on the customer side.

Test Expectations

  • Service-contract conformance: /health, /service-info, /api/v2/* all responsive; auth via CL-02 provider; profile-aware MCP mount.
  • Local + federation contribution parity: same lens, same contributions; one session sees both arrive locally and ratifies; another sees one arrive locally, one via federation; results match.
  • Tenant isolation: session for tenant A cannot accept contribution authenticated as tenant B.
  • JICD registration test: start service with JICD CS Hub URL configured; verify Capability and Status messages appear on the hub.
  • State machine integration test: create session → 3 contributions over time → SSE stream shows PROPOSED → PENDING_QUORUM → RATIFIED with combiner result.
  • Conflict path test: contributions trigger IN_CONFLICT → MachineDissent record appears → human attestation resolves → audit chain shows full lineage.
  • Federation transport test: swap transport adapters (mTLS / AMQP / SILKWAVE); behavior identical at the application layer.
  • Profile B refusal test: start service with AXONIS_DEPLOYMENT_PROFILE=customer_onprem and Lookup-Light cartridge declared; service refuses to start with clear error (consensus service requires Full Lens profile).

Cross-Reference

  • SPEC-28: combiner math invoked here
  • SPEC-36: state machine implemented here
  • SPEC-30: dissent emission triggered here
  • CL-01: deployment profile gates MCP availability
  • CL-02: pluggable auth on every endpoint
  • CL-03: topology determines where this service runs
  • CL-06: REST-only consensus session primitive
  • CL-07: federation transport adapter feeds contributions in
  • CL-08: classification routing
  • CL-09: evidence Blocks emitted on RATIFIED
  • CL-10: tenant scoping on every operation

Depends on: component.parallax.consensus-session, component.parallax.dissent, component.parallax.multi-contributor-combination

Realizes: product.fusion