dyolo-kya 2.0.0

Know Your Agent (KYA): cryptographic chain-of-custody for recursive AI delegation with provable scope narrowing, namespace isolation, and enterprise-grade storage health
Documentation

dyolo-kya — Know Your Agent

Cryptographic chain-of-custody for recursive AI agent delegation.

Crates.io npm PyPI docs.rs CI License: MIT OR Apache-2.0


The Problem

When a human authorizes an AI agent — which may delegate to another agent, which may delegate again — the authorization chain breaks down at the first hop. There is no irrefutable proof that an action traces back to a human authorization, no guarantee that the delegated scope is a strict subset of the parent's scope, and no replay or revocation enforcement across hops.

This is the Recursive Delegation Gap. dyolo-kya closes it with a cryptographic chain-of-custody protocol using Ed25519 signatures and Blake3 Merkle trees over intent hashes.


What is new in v2.0.0

Gap from v1 v2.0 fix
Rust-only — no SDK for other languages REST gateway + Go SDK + Python SDK + TypeScript SDK
No ops tooling dyolo-kya-cli: keygen, issue, revoke, inspect, verify, decode
No identity bridge to existing IAM dyolo-kya-identity: JWT/OIDC binding + YAML policy-as-code
Redis-only storage dyolo-kya-pg: PostgreSQL adapter with multi-tenant namespacing
No typed extension fields CertExtensions committed into cert signatures
No AI framework examples LangChain, OpenAI Assistants, AutoGen, Go SDK
Global rate limiting Per-client-IP bucketing with governor::keyed
Two-step nonce protocol (TOCTOU) Atomic try_consume — single-roundtrip at DB level
No batch cert issuance POST /v1/cert/issue-batch + CertBundle type
No discovery document GET /.well-known/kya-configuration (OIDC-style)
No batch authorization POST /v1/authorize/batch + authorizeBatch in all SDKs
No policy-as-code PolicyDocument YAML/JSON compiled into DelegationPolicy

Architecture

┌───────────────────────────────────────────────────────────────────────┐
│  Human (Ed25519 root)                                                 │
│    │ signs DelegationCert (scope: trade.equity, max_depth: 8)         │
│    ▼                                                                  │
│  Orchestrator agent (Ed25519)                                         │
│    │ narrows scope → signs sub-cert (scope: trade.equity/NYSE)        │
│    ▼                                                                  │
│  Tool agent (Ed25519)                                                 │
│    │ calls execute_trade("AAPL", 10)                                  │
│    ▼                                                                  │
│  dyolo-kya-gateway  POST /v1/authorize                                │
│    verifies: signatures → scope ⊆ parent → expiry → nonce → revoked  │
│    returns: AuthorizeResponse { authorized: true, chain_depth: 2 }    │
└───────────────────────────────────────────────────────────────────────┘

Five-minute start (Docker + Python)

# 1. Run the gateway
docker run -p 8080:8080 ghcr.io/dyologician/dyolo-kya-gateway:2

# 2. Install the Python SDK
pip install dyolo-kya

# 3. Issue a cert and authorize an action
python - <<'EOF'
from dyolo_kya import KyaClient, IntentSpec

kya  = KyaClient("http://localhost:8080")
cert = kya.issue_cert(
    delegate_pk_hex="<agent-pk-hex>",
    intents=[IntentSpec("trade.equity", {"exchange": "NYSE"})],
    ttl_seconds=3600,
    extensions={"dyolo.cost_center": "ai-ops"},
)
print("Cert fingerprint:", cert.fingerprint_hex)
EOF

SDK quick-reference

Go

import "github.com/dyologician/dyolo-kya/sdk/go/kya"

client := kya.NewClient("http://localhost:8080")

cert, err := client.IssueCert(ctx, kya.IssueCertRequest{
    DelegatePkHex: agentPkHex,
    Intents:       []kya.IntentSpec{{Name: "trade.equity"}},
    TtlSeconds:    3600,
})

result, err := client.Authorize(ctx, kya.AuthorizeRequest{
    Chain:         signedChain,
    IntentName:    "trade.equity",
    ExecutorPkHex: agentPkHex,
})

// Batch: authorize multiple intents atomically
batch, err := client.AuthorizeBatch(ctx, kya.BatchAuthorizeRequest{
    Chain:         signedChain,
    ExecutorPkHex: agentPkHex,
    Intents:       []kya.BatchIntentSpec{{Name: "query.portfolio"}, {Name: "trade.equity"}},
})

Python

from dyolo_kya import KyaClient, IntentSpec

kya  = KyaClient("http://localhost:8080")
cert = kya.issue_cert(delegate_pk_hex="...", intents=[IntentSpec("trade.equity")])
res  = kya.authorize(chain=signed_chain, intent_name="trade.equity", executor_pk_hex="...")

# Batch
batch = kya.authorize_batch(
    chain=signed_chain,
    executor_pk_hex="...",
    intents=[IntentSpec("query.portfolio"), IntentSpec("trade.equity")],
)
assert batch.all_authorized

TypeScript / Node.js

import { KyaClient } from "dyolo-kya";
import { buildLangChainKyaBatchTool } from "dyolo-kya/integrations";

const kya  = new KyaClient("http://localhost:8080");
const cert = await kya.issueCert({ delegatePkHex: "...", intents: [{ name: "trade.equity" }] });

// Single-intent
const res = await kya.authorize({ chain, intentName: "trade.equity", executorPkHex: "..." });

// Multi-intent batch (atomic single round-trip)
const batch = await kya.authorizeBatch({
  chain,
  executorPkHex: "...",
  intents: [{ name: "query.portfolio" }, { name: "trade.equity" }],
});
if (!batch.allAuthorized) throw new Error("Batch denied");

// LangChain multi-intent tool guard
const tool = buildLangChainKyaBatchTool({
  name: "portfolio_rebalance",
  description: "Query and rebalance a portfolio",
  intentNames: ["query.portfolio", "trade.equity"],
  client: kya,
  resolveContext: (input) => ({ chain: agentChain, executorPkHex: agentPk }),
  run: async (input, auth) => `Rebalanced (${auth.authorizedCount} intents authorized)`,
});

Rust (native, no gateway)

use dyolo_kya::{
    DyoloIdentity, DyoloChain, Intent, CertBuilder, IntentTree,
    MerkleProof, MemoryRevocationStore, MemoryNonceStore, SystemClock,
    policy::{DelegationPolicy, CapabilitySet, PolicySet},
    NoopAuditSink,
};

let human  = DyoloIdentity::generate();
let agent  = DyoloIdentity::generate();
let intent = Intent::new("trade.equity").unwrap();
let tree   = IntentTree::build(vec![intent.hash()]).unwrap();
let cert   = CertBuilder::new(agent.verifying_key(), tree.root(), now, now + 3600)
                 .sign(&human);
let mut chain = DyoloChain::new(human.verifying_key(), tree.root());
chain.push(cert);

// Plain authorize
let action = chain.authorize(
    &agent.verifying_key(), &intent.hash(),
    &MerkleProof::default(), &SystemClock,
    &MemoryRevocationStore::new(), &MemoryNonceStore::new(),
).unwrap();

// Authorize with policy enforcement + audit log
let policy = PolicySet::new().add(
    DelegationPolicy::new("fintech-prod")
        .max_chain_depth(4)
        .max_ttl_secs(3600)
        .capabilities(CapabilitySet::new().allow("trade."))
        .forbid_sub_delegation(),
);
let action = chain.authorize_with_options(
    &agent.verifying_key(), &intent.hash(),
    &MerkleProof::default(), &SystemClock,
    &MemoryRevocationStore::new(), &MemoryNonceStore::new(),
    Some(&policy), &NoopAuditSink,
).unwrap();
assert_eq!(action.receipt.chain_depth, 1);

Policy-as-code (YAML)

Store your policies in source control and load them at startup:

# policies/fintech-trading.yaml
name: fintech-trading
max_chain_depth: 4
max_ttl_secs: 3600
forbid_sub_delegation: true
capabilities:
  - "trade.equity"
  - "query.portfolio"
required_extensions:
  - "dyolo.cost_center"
use dyolo_kya_identity::policy::PolicyDocument;

let doc    = PolicyDocument::from_yaml(include_str!("policies/fintech-trading.yaml"))?;
let policy = doc.into_policy(); // → DelegationPolicy

Batch cert issuance

Issue multiple delegation certificates in a single authenticated request:

POST /v1/cert/issue-batch
{
  "requests": [
    { "delegate_pk_hex": "<pk1>", "intents": [{"name":"trade.equity"}], "ttl_seconds": 3600 },
    { "delegate_pk_hex": "<pk2>", "intents": [{"name":"query.portfolio"}], "ttl_seconds": 7200 }
  ]
}

Returns a CertBundle alongside the individual issuance results.


Discovery document

GET /.well-known/kya-configuration

Returns an OIDC-style JSON document with the gateway's signing public key, all endpoint URLs, supported algorithms, and protocol version. Enterprise clients can bootstrap trust by fetching this document and pinning the gateway_signing_pk_hex field.


CLI

cargo install dyolo-kya-cli

dyolo-kya keygen
dyolo-kya issue --policy examples/trading_chain.yaml
dyolo-kya revoke <fingerprint-hex>
dyolo-kya inspect <fingerprint-hex>
dyolo-kya verify token.json
dyolo-kya decode cert.json

# PostgreSQL migration (one-time setup for dyolo-kya-pg)
dyolo-kya migrate --database-url postgres://user:pass@localhost/mydb
# Print the raw DDL for manual or CI-managed application
dyolo-kya migrate --print | psql "$DATABASE_URL"

# Generate shell completions
dyolo-kya completion bash   >> ~/.bash_completion
dyolo-kya completion zsh    >> ~/.zshrc
dyolo-kya completion fish   > ~/.config/fish/completions/dyolo-kya.fish

Gateway REST API

Method Path Description
GET /health Liveness + signing public key
GET /.well-known/kya-configuration OIDC-style discovery document
POST /v1/cert/issue Issue a single cert
POST /v1/cert/issue-batch Issue multiple certs (CertBundle)
POST /v1/cert/revoke Revoke a cert by fingerprint
POST /v1/cert/revoke-batch Revoke multiple certs in one call
GET /v1/cert/:fp Inspect revocation status
POST /v1/authorize Authorize a single agent intent
POST /v1/authorize/batch Authorize multiple intents atomically
POST /v1/token/verify Verify a VerifiedToken MAC

Environment variables

Variable Default Description
DYOLO_SIGNING_KEY_HEX ephemeral 32-byte Ed25519 signing key (hex). Set in production.
DYOLO_MAC_KEY_HEX ephemeral 32-byte BLAKE3 MAC key (hex). Set in production.
DYOLO_RATE_LIMIT_RPS 500 Per-IP request rate limit (requests/second).
GATEWAY_ADDR 0.0.0.0:8080 Bind address
RUST_LOG info Log filter

Framework integrations

Framework Language Reference
LangChain Python docs/integrations/langchain.md
OpenAI Assistants Python docs/integrations/openai-agents.md
AutoGen Python examples/integrations/autogen_example.py
OpenAI Agents SDK TypeScript examples/integrations/openai_agents_example.ts
LangChain.js (batch) TypeScript buildLangChainKyaBatchTool in dyolo-kya/integrations
OpenAI SDK (batch) TypeScript buildOpenAIKyaBatchFunction in dyolo-kya/integrations
AutoGen (batch) TypeScript withKyaBatchGuard in dyolo-kya/integrations

Crate / package layout

Package Description
dyolo-kya (Rust) Core library — certs, chains, intents, identity, policy, audit
dyolo-kya-gateway Axum REST sidecar with per-IP rate limiting
dyolo-kya-cli Operations CLI
dyolo-kya-redis Redis AsyncRevocationStore + AsyncNonceStore (atomic SET NX PX)
dyolo-kya-pg PostgreSQL adapter (atomic INSERT ON CONFLICT DO NOTHING)
dyolo-kya-identity JWT/OIDC binding + YAML policy-as-code (PolicyDocument)
dyolo-kya (npm) TypeScript/Node.js SDK
dyolo-kya (PyPI) Python SDK
dyolo-kya (Go module) Go SDK

How dyolo-kya compares to JWT delegation and SPIFFE

See docs/vs-jwt-spiffe.md.
SPIFFE answers "which workload is this?", JWT answers "which user triggered this?", and dyolo-kya answers "did the user authorize this exact action through this exact delegation chain, and is the scope still valid?"


Security

Primitives: ed25519-dalek 2 (signatures), blake3 1 (hashing + MAC), subtle 2 (constant-time comparisons). Nonce stores use single-operation atomic commits — SET NX PX for Redis, INSERT ON CONFLICT DO NOTHING for Postgres — eliminating the check-then-set TOCTOU race.

Report vulnerabilities to the address in SECURITY.md.


License

MIT OR Apache-2.0. MD_EOF echo "done"