K9X.ai BETA
For developers and solution architects

Architecture

How k9x Origin fits into a solution built on K9-AIF, and how to build one: the architecture and class diagrams, the five configuration steps, your Kafka or Confluent, the security controls, and how it scales. K9X Streams is the reference implementation; your sources and projections change, everything else is the framework.

1 · What k9x Origin is2 · Architecture3 · Reference model 4 · Five steps5 · Your Kafka6 · Security controls 7 · Read model8 · Deploying at scale

What k9x Origin is

k9x Origin is a framework component, and MCP is one of the ways to reach it, the same way k9x Shield is a component, not a service.

What it is

A class in the framework (K9Origin, in k9_origin) that does the resolving: scope, Shield checks, live state, read-through, verify, flags. It runs inside your solution's own process, wherever you build it with OriginFactory.create(config).

Who asksHowExample
OrchestratorDirect, in-process, no networkBefore a squad runs, it fills live_context from origin.yaml
Agent (or any MCP client)Over MCP, through OriginMCPServerThe agent's last-moment get_current before acting

So "MCP service" describes how agents call it, not what it is. OriginMCPServer is the thin MCP wrapper that carries the calls; all the logic is in K9Origin.

How it is deployed

Origin is built into K9-AIF; agents talk to it over MCP; orchestrators call it directly.

Architecture

For solution architects: where k9x Origin sits, how it is filled, and its classes in the framework; then the K9X Streams application on top.

An agent's three paths: knowledge, current truth, actions

An agent's three paths: knowledge (retriever), current truth (k9x Origin), actions (systems of record), each screened by k9x Shield.

How data reaches k9x Origin

How data reaches k9x Origin: pushed by streams through the Shield gate, pulled from systems of record on demand.

One get_current call, step by step

One get_current call, step by step: scope, request screening, live state or read-through, verify, flags, answer screening.

k9x Origin classes in K9-AIF

k9x Origin classes in the framework (k9_origin), with the state backends, adapters and the rules agent.

The K9X Streams application

Your producers apps, CDC, devices, or the demo generator Kafka Apache Kafka, Redpanda, Confluent, MSK K9X Streams Shield gate k9x Shield Live state projections MCP server /mcp K9-AIF solution Orchestrator: live_context agents: MCP re-check k9x Shield on results HIL when it matters
K9X Streams classes on the K9-AIF framework

Classes: four projections, one orchestrator and one agent extend K9-AIF; everything else is the framework.

K9X Streams deployment view

Deployment view: producers, topics, the K9X Streams service and a K9-AIF solution.

The reference model

K9X Streams is how to build an agentic application that must act on the latest information. Copy the model, not the code: your sources and your projections change; the fabric, the governance and the way agents read live context stay the same.

1 · Data sources

  • Databases · change data capture
  • ERP, CRM, AP, claims apps · domain events
  • Application log files
  • IoT devices, telemetry
  • SaaS webhooks, REST APIs
  • Files, batch drops, S3
  • Other agents' results
→

2 · Kafka topics

  • Apache Kafka
  • Confluent Platform / Cloud
  • Redpanda
  • Amazon MSK
→

3 · K9-AIF context fabric

  • KafkaEventFabric
  • GovernanceEventGate · k9x Shield; refused → quarantined
  • your projections → live state · as_of, version, source
  • k9x Origin · one call for agents, over MCP
→

4 · Your K9-AIF solution

  • Orchestrator · hands squads live_context
  • Squad → agents
  • Agent re-checks over MCP · before it pays, approves, ships
  • Business rules → HIL on conflicts, quarantine, thresholds
SourceHow it reaches a Kafka topic
Relational databases (PostgreSQL, Oracle, SQL Server, MySQL)Change data capture (Debezium on Kafka Connect): every insert, update and delete becomes an event
Business applicationsThe application publishes domain events, or its own Kafka connector does
Application log filesA log shipper (Fluent Bit, Vector, Filebeat) sends each line to a topic
IoT devicesAn MQTT broker with a Kafka bridge
SaaS webhooks and REST APIsA small receiver or poller publishes to a topic
Files and batch dropsA Kafka Connect file or S3 source connector
Amazon Kinesis Data StreamsA bridge connector to Kafka (Kinesis Data Firehose only delivers to storage, so agents cannot consume from it)

Sources keep their own format: an envelope mapper per topic turns each message into an event (debezium for database change records, log_line for application logs, or your own via register_mapper); a webhook receiver can skip Kafka with ContextIngest.ingest_raw(). The five steps below build on it.

Five steps

k9x Origin is built into the K9-AIF framework (1.18), like k9x Shield. K9X Streams adds only its Accounts Payable projections, a generator, one orchestrator and one agent. A solution in any domain follows the same five steps.

Switch on k9x Origin

In config/config.yaml. Broker addresses and credentials stay in .env.

origin:
  enabled: true
  config: config/origin.yaml
  state: {provider: redis}        # in_memory | redis (shared, LRU-bounded)
streams:
  enabled: true                   # off: Origin answers from systems of record only
  provider: kafka
security:
  shield: {enabled: true}

Describe entities and sources

In config/origin.yaml: what agents may ask about, where each entity's system of record is, what each squad needs, and each agent's scope.

entities:
  claim:
    key: claim_id
    system_of_record: claims_api     # read-through on a miss, verify before acting
    owned_fields: [status, amount]
    conflicts: {status: {transitions: ["approved -> disapproved"]}}
sources:
  claims_api: {type: mcp, server: "${CLAIMS_MCP_URL}", tool: get_claim}
squads:
  payout: {live_context: ["claim:{claim_id}"], verify_before: [PayoutAgent]}
agents:
  PayoutAgent: {allowed_entities: [claim, policy]}

One projection per business event

Map source records to the entities your agents reason about. This is the only domain code Origin needs.

class ClaimProjection(BaseContextProjection):
    entity, id_field = "claim", "claim_id"
    def accepts(self, env): return env.event_type.startswith("claim.")
    def project(self, env): return {"status": env.payload["status"],
                                    "amount": env.payload["amount"]}

Wire it at the entry point

One factory call. Orchestrators get origin=; agents reach it over MCP with the framework's client. Production refuses an ungoverned Origin.

origin = OriginFactory.create(config, projections=[ClaimProjection()])
ContextIngest(StreamsFactory.create_fabric(config), origin.state,
              gate=StreamsFactory.create_gate(config)).attach(["claim.updated"])
orch = PayoutOrchestrator(config, governance=shield, origin=origin)
app.mount("/", OriginMCPServer(origin).http_app())      # MCP at /mcp

Decide with business rules

Origin states facts. A rules step in the squad decides, and raises a K9X HIL case when a rule says so. The agent itself makes one call: get_current("claim", "C-1").

rules_agent:
  adapter: k9_rules                  # or rest_rules (a decision service)
  hil_queue: claims-review
  rules:
    - {name: large claim,   when: "claim.amount > 5000",           then: hil}
    - {name: data conflict, when: "live_context.*.flags.conflict", then: hil}

Full recipe: Developer Guide 15.8 ↗ · design: k9x Origin design ↗

Your organization's Kafka or Confluent

K9X Streams speaks the Kafka protocol, so it connects to the broker you already run. Set it in .env; no code changes.

BrokerSettings
Apache Kafka, RedpandaKAFKA_BROKER=host:9092
Confluent PlatformKAFKA_BROKER; plus KAFKA_SECURITY_PROTOCOL and SASL settings if your cluster requires sign-in
Confluent CloudKAFKA_SECURITY_PROTOCOL=SASL_SSL, KAFKA_SASL_MECHANISM=PLAIN, API key and secret as KAFKA_SASL_USERNAME / KAFKA_SASL_PASSWORD
Amazon MSK and othersSASL_SSL with SCRAM-SHA-512 (or your cluster's mechanism)
Your topic namesSTREAMS_TOPIC_INVOICE, STREAMS_TOPIC_PAYMENT, STREAMS_TOPIC_VENDOR

Events are JSON today; Avro with Schema Registry is next. The adapter for a broker stays a configuration choice, never a dependency: the framework stays vendor-neutral.

Security controls

Stream data and agent requests are untrusted. Each line of defence is a K9-AIF building block you configure, and each is shown working in the demo's threat simulator.

ControlWhereStopsConfigure
Stream gateBefore live statePlanted instructions, payment diversion, PII values in eventsGovernanceEventGate with PromptInjectionCheck, PaymentDiversionCheck, PIIBoundaryCheck (block_on_match)
QuarantineLive stateA refused event's content ever being readAutomatic: the entity is marked quarantined; agents hold for human review
Entity scopeMCP server, before any readRequests for data outside the server's domainorigin.yaml: agents.<name>.allowed_entities
Request screeningMCP server, before any readInstructions in tool arguments, solicitations for PIIorigin.yaml: governance.request_checks (PromptInjectionCheck, PIIRequestCheck)
Answer screeningMCP server and agentPII or instructions in what an agent readsorigin.yaml: governance.answer_checks; screen_tool_result in the agent
Source orderLive stateA late event overwriting newer dataAutomatic: events apply by when they happened; late ones are kept in history only
Conflict flagsEvery answerActing on data that flipped, arrived out of order, or disagrees with its system of recordorigin.yaml: entities.<name>.conflicts; business rules decide (e.g. → HIL)

Read model, not system of record

Live state is a derived, read-only view of what is true now, rebuilt from Kafka at any time. Agents read it; they act through the systems of record, which emit events that flow back into it.

PieceRole
Systems of record (ERP, payments, vendor master)Own the truth and the transactions
Kafka topicsDurable, ordered log of every change; use log compaction keyed by entity id for entity state
Live stateDerived view with as_of; eventually consistent (milliseconds to seconds)
AgentsRead live state; act through the system of record's API

The agent's re-check shrinks the window for a duplicate action from hours to seconds; the final guarantee belongs in the system of record (an idempotency key or a unique constraint such as one payment per invoice).

Deploying at scale

K9-AIF scales by tier: router cluster, Kafka cluster, orchestrator clusters. K9X Streams adds three tiers that scale the same way.

TierHow it scalesState
Streams ingest workersOne consumer group; topics partitioned by entity keyStateless: writes to the shared live state
Shared live stateRedis cluster, PostgreSQL, or a stream processor's materialized viewThe only stateful piece; rebuildable from Kafka
Context MCP serversA pool behind a load balancerStateless: reads the shared live state
OrchestratorsExisting K9-AIF clusters, each given the shared state as context_stateStateless

This demo runs every tier in one process with in-memory state. Shared live state in Redis (RedisContextState, LRU-bounded, quarantine never evicted) is in K9-AIF 1.18; consumer groups with committed offsets and lag-based freshness are next.