Skip to content

Run agents without a leader.

kslate gives your agents a trusted, audited shared record.

For two or more agents working the same task at the same time.

HTTP/JSON, gRPC and MCP · Kubernetes or standalone

quickstart
$ git clone https://github.com/kslate-dev/kslate
$ cd kslate
$ make run

Trusted

Every write is checked against what the agent read:

  • the field versions
  • what it was based on
  • its lease
  • its role
  • an identity kslate verified, not one the agent claims
# a write whose premise moved
$ python remediator.py
rejected: planned_action moved v1 -> v2 by apikey:diagnoser

Audited

Every write and every rejection is kept, with its author, its evidence and what it was based on. kslate blame reads it back.

$ kslate blame --server http://127.0.0.1:8080 incidents/inc-1 actions_taken
actions_taken  (append, evidence required)
v1  23:04:44  apikey:remediator     "executed: roll back api deployment"
      evidence: kubectl output
      based_on: planned_action@2
      identity: apikey:remediator (shared-secret)
--  23:04:20  apikey:remediator     REJECTED STALE_PREMISE
      based_on: planned_action@1

How it works

Each agent runs its own loop: read the record, claim work, act, write back. Like Kubernetes controllers coordinating through the API server, agents never message each other or wait on a supervisor. kslate makes the shared record safe to coordinate through.

  1. 01read the record
  2. 02claim work
  3. 03act
  4. 04write back

Four agents, one record

Four agents handle one incident through one record. No agent calls another. The struck-through edge is a write kslate rejected because the plan it was based on had moved; the write below it, based on the current plan, was accepted. What is kslate? walks through this exchange.

    flowchart LR
  detector([detector])
  diagnoser([diagnoser])
  remediator([remediator])
  communicator([communicator])
  subgraph rec["record  incidents/inc-4471"]
    direction TB
    symptoms["symptoms  ·  append  ·  entries 1–3"]
    hypotheses["hypotheses  ·  append  ·  entries 1–6"]
    planned_action["planned_action  ·  versioned  ·  v5"]
    actions_taken["actions_taken  ·  append"]
    customer_updates["customer_updates  ·  append"]
  end
  detector -->|"append · entries 2, 3"| symptoms
  diagnoser -->|"append · based_on symptoms@3"| hypotheses
  diagnoser -->|"put · v4 → v5 · evidence hypotheses@6"| planned_action
  remediator rejected@-->|"<s>append · based_on planned_action@4</s><br>REJECTED STALE_PREMISE · planned_action moved 4 → 5 by diagnoser"| actions_taken
  remediator -->|"append · based_on planned_action@5 · accepted"| actions_taken
  communicator -->|"append · when actions_taken moves"| customer_updates
  

The rejected write stays in the record's history with who sent it and what had changed. kslate blame shows it. kslate does not merge writes, does not retry for the agent, and does not decide which agent was right.