Doors open. Funds move. Firmware overwrites. A single compromised channel can fabricate any instruction — unless the instruction never exists as a complete artifact in any single channel. XorIDA splits every payload across independent providers. Fewer than k shares reveal zero information. Not "hard to break." Mathematically impossible.
AI agent security incidents in the past year
¹ HelpNetSecurity, Feb 2026of teams use shared keys for agent auth today
² Gravitee, 2026enterprise multi-agent — 340% YoY growth
³ TechFundingNews, 2026XorIDA known-answer vectors, fuzz-tested, statistically verified for share independence.
⁴ packages/crypto/ — 100% line coverageThese are not features on a roadmap. They are properties of the cryptographic architecture — enforced by mathematics, not by policy configuration.
A leaked API key is unbounded — full access, all endpoints, forever, until someone notices and rotates. A leaked Xail signing key is bounded by three independent constraints:
finance.invoice.approve cannot call firmware.update.deploy. Scope is signed into every individual envelope and checked against the trust graph on every request. No god-mode keys. No lateral movement.Compare: a standard API key gives the attacker both authentication and access to encrypted payloads. Xail separates these concerns at the cryptographic layer.
The trust registry resolves DIDs to public keys at first contact. After that, public keys are cached locally. Your agents authenticate each other directly — the registry is not in the message delivery path.
This matters for IoT, edge deployments, and any environment where network reliability cannot be assumed. The registry is a phone book, not a gatekeeper.
Every envelope carries a 128-bit cryptographic nonce and a 30-second timestamp window. This is per-request, not per-credential — each individual request is unique and cannot be replayed.
Nonce + timestamp + Ed25519 signature + scope — all four are verified before your handler executes. The middleware enforces this automatically.
When Agent A tells Agent B unlock door 4 over a single transport channel, anyone who compromises that channel can substitute unlock all doors. TLS doesn't help — the attacker is often the transport operator, a compromised endpoint, or a man-in-the-middle at the provider. Every content-based defense (input filtering, classifier detection, LLM firewalls) can be bypassed by a sufficiently adaptive payload. The structural defense cannot.
"Unlock unit 42" becomes "unlock all units.""Dispense 10mg" becomes "dispense 100mg.""Approve $1,400" becomes "approve $140,000."splitChannel: true. XorIDA splits the ciphertext across n independent provider endpoints. No single provider can reconstruct or modify the instruction. Zero information below threshold. This is the structural prompt injection defense.agent.middleware() on any Express or Fastify route. Timestamp, nonce, signature, scope — all checked. req.agentMessage is verified or the request never reaches your handler.import { Agent } from '@xail/agent-sdk'; // 1. Create identity (once, on provisioning) const agent = await Agent.create({ name: 'invoice-processor', registry: 'https://atelier.xail.io', }); // 2. Send — signed + encrypted await agent.send({ to: 'did:xail:service-accounts-payable', payload: { invoiceId: 'INV-2847', amount: 14200 }, scope: 'finance.invoice.approve', }); // 3. Split-channel — no provider sees full content await agent.send({ to: 'did:xail:service-accounts-payable', payload: { invoiceId: 'INV-2847', amount: 14200 }, scope: 'finance.invoice.approve', splitChannel: true, // ← the feature nobody else has }); // 4. Receive — verified middleware app.post('/agent/inbox', agent.middleware(), (req, res) => { const { sender, payload, scope } = req.agentMessage; // sender verified · payload decrypted · scope checked });
No new infrastructure required for the identity layer. Uses your existing Postgres and Redis. Split-channel delivery requires @xail/crypto as a peer dependency — the XorIDA implementation.
The SDK ships with built-in adapters for development (in-memory nonce store, in-memory trust registry) and production (Redis nonce store, HttpTrustRegistry for registry.xail.io). Replace the adapters, not the SDK.
# Identity + signing layer npm install @xail/agent-sdk # Split-channel delivery (peer dep) npm install @xail/crypto # Production: Redis nonce store # (included in @xail/agent-sdk) import { RedisNonceStore } from '@xail/agent-sdk';
{
"v": 1,
"alg": "Ed25519",
"sender": "did:xail:agent-invoice-processor",
"recipient": "did:xail:service-ap",
"timestamp": 1742300000000,
"nonce": "a3f7b2c1d4e8f9a0...",
"scope": "finance.invoice.approve",
"payload": "base64(AES-256-GCM ciphertext)",
"signature": "base64(Ed25519 — covers ciphertext)"
}The trust registry resolves DIDs at first contact. After that, public keys are cached locally. The registry is not in the message delivery path — your agents keep working even if the registry is temporarily unreachable.
// Direct: signed + encrypted point-to-point Agent A → create envelope (sign + encrypt + scope) → deliver over HTTPS / MQTT / any transport → Agent B verify signature (Ed25519) check timestamp (30s window) check nonce (replay protection) check scope (trust graph) decrypt payload (AES-256-GCM) // Registry involvement: ZERO at runtime // DID → public key cached after first resolution
// Split-channel: no single provider sees content Agent A → create envelope (sign + encrypt + scope) → XorIDA split ciphertext into n shares → share 1 → Provider A (zero knowledge) → share 2 → Provider B (zero knowledge) → Agent B collect shares from providers reconstruct ciphertext (XorIDA) verify + decrypt (same as direct) // Each provider holds random noise. // No single breach exposes content.
atelier.xail.io maps DIDs to public keys — like DNS maps domains to IPs. Once resolved, the key is cached. Your agents authenticate each other directly using cached keys. The registry is consulted at provisioning and periodic refresh, never in the critical message path.
The envelope format is a JSON object with a signature. It rides on any transport that can carry bytes. Here is a concrete example over MQTT — the same envelope, the same verification, different wire.
{
"id": "did:xail:sensor-north-07",
"verificationMethod": [{
"id": "did:xail:sensor-north-07#key-1",
"type": "Ed25519VerificationKey2020",
"publicKeyMultibase": "z6Mkf5rG..."
}],
"service": [{
"id": "#mqtt",
"type": "AgentTransport",
"serviceEndpoint": {
"transport": "mqtt",
"broker": "mqtts://broker.example.com:8883",
"topic": "xail/agents/sensor-north-07/inbox",
"qos": 1
}
}]
}// Sender: publish signed envelope to MQTT topic const envelope = await agent.send({ to: 'did:xail:sensor-north-07', payload: { action: 'telemetry.report', zone: 'B4' }, scope: 'telemetry.sensor-north-07.publish', }); // The envelope is standard JSON — publish it mqtt.publish( 'xail/agents/sensor-north-07/inbox', JSON.stringify(envelope), { qos: 1 } ); // Receiver: subscribe + verify with same middleware mqtt.on('message', async (topic, msg) => { const envelope = JSON.parse(msg.toString()); const result = await agent.receive(envelope); if (!result.ok) return; // rejected // result.value.payload.action === 'telemetry.report' // result.value.scope === 'telemetry.sensor-north-07.publish' });
| Threat | Traditional response | Atelier guarantee |
|---|---|---|
| Compromised transport channel | HOPE "We use TLS." Endpoint operator still reads content. No defense once TLS terminates. |
MATHEMATICAL Fewer than k shares = 0 bits of instruction content. Provider holds random noise. Unconditional. |
| Agent instruction injection | PROBABILISTIC Content filtering, LLM firewalls, classifier detection. Bypassed by adaptive payloads. |
STRUCTURAL Instruction never exists as a complete artifact in any single channel. No content to filter. Transport-layer defense. |
| Signing key compromise | UNBOUNDED Full impersonation, indefinitely, until someone notices and rotates. |
BOUNDED 30-second window. Scope-limited. Zero content exposure. Instant revocation. |
| Quantum adversary | MIGRATE Algorithm swap (10–20 year timeline). Captured ciphertext decryptable later. |
IMMUNE XorIDA makes zero computational assumptions. Captured shares are permanent noise. No migration needed. |
| Provider subpoena | COMPLY Encrypt and hope. Provider holds complete ciphertext — breakable with court-ordered key disclosure. |
IMPOSSIBLE Provider holds one share. Mathematical impossibility of reconstruction. Nothing to disclose. |
| Replay attack | VARIES Nonce + timestamp, if implemented. Often absent in API key systems. |
ENFORCED Cryptographic nonce + 30s timestamp + scope + Ed25519 signature. All verified before handler executes. |
| Player | What they provide | Content security? |
|---|---|---|
| Keycard $38M — a16z |
TRANSPORT Scoped tokens, revocation, audit trail. Strong identity layer. TLS for delivery. |
BETTER Still TLS only. Endpoint operator reads message. No split-channel. |
| Google A2A Linux Foundation |
TRANSPORT Agent interoperability, discovery, JWT/OIDC auth. De facto standard. |
COMPATIBLE Xail split-channel works over A2A channels. We complement, not compete. |
| API Keys 45.6% of teams² |
NONE Bearer token. No identity, no replay protection, no audit, no scope. |
COMPLETE @xail/agent-sdk replaces this entirely. |
| @xail/agent-sdk | Ed25519 identity + signed envelopes + per-request scope + trust graph | XorIDA split-channel — zero-knowledge below threshold. Not encryption. Mathematics. |
In any standard PKI system, a leaked private key is a catastrophic event — indefinite impersonation, potential access to historical messages, unbounded damage. In the Xail agent architecture, it is a bounded, auditable incident. Not by policy. By mathematics.
In a standard PKI or API key model, a credential leak is unbounded — full access, forever, until someone notices. Xail’s architecture makes that impossible by design.
Built on the same XOR-IDA cryptographic foundation as Xecret.io — production-deployed since 2021 for cryptocurrency seed phrase protection. The same algorithm that protects agent messages can also protect the agent’s own signing key via threshold-split key custody.
We built Atelier so your system never depends on ours to function. The SDK is a convenience layer. The envelope format is the real product — and it’s yours.
@xail/agent-sdk and @xail/crypto. For infrastructure that controls physical access, financial transactions, or healthcare systems, vendor lock-in with no escape hatch is a non-starter. We agree.Every computational encryption scheme — AES, RSA, ECDSA, lattice-based PQC — is breakable by a sufficiently powerful computer. The question is when. XorIDA makes no computational assumption at all. The security of fewer-than-k shares is not "hard to break." It is information-theoretically impossible to extract any information — regardless of adversary resources, including quantum computers. This is Shannon’s perfect secrecy applied to agent communication.
A senior engineer can implement signed, encrypted, replay-protected agent envelopes in ~450 lines. We respect that. Here is what those 450 lines do not give you.
This gets you signed, encrypted, replay-protected agent envelopes. Solid. Start here if that's all you need.
For embedded systems, IoT devices, or any non-Node.js runtime. This is the complete verification flow — ~80 lines of Python using only the standard library and cryptography. Your device just needs the sender's public key, pinned at provisioning.
import json, time, base64, hashlib from cryptography.hazmat.primitives.asymmetric.ed25519 \ import Ed25519PublicKey from cryptography.hazmat.primitives.ciphers.aead \ import AESGCM # Pinned at provisioning (like a TLS cert) SENDER_PUBKEY = bytes.fromhex("ab12...ef") DEVICE_PUBKEY = bytes.fromhex("cd34...gh") NONCES_SEEN = set() def verify_envelope(raw_json: str): env = json.loads(raw_json) # 1. Version check assert env["v"] == 1 assert env["alg"] == "Ed25519" # 2. Timestamp (30s window) age = abs(time.time() * 1000 - env["timestamp"]) if age > 30_000: raise ValueError("Timestamp expired") # 3. Nonce (replay prevention) nonce_key = f'{env["sender"]}:{env["nonce"]}' if nonce_key in NONCES_SEEN: raise ValueError("Replay detected") NONCES_SEEN.add(nonce_key) # 4. Verify Ed25519 signature payload = base64.b64decode(env["payload"]) sig = base64.b64decode(env["signature"]) pub = Ed25519PublicKey.from_public_bytes(SENDER_PUBKEY) pub.verify(sig, payload) # raises on failure # 5. Derive shared key: SHA-256(sort(pubA,pubB)) pair = sorted([SENDER_PUBKEY, DEVICE_PUBKEY]) shared = hashlib.sha256(pair[0] + pair[1]).digest() # 6. Decrypt AES-256-GCM (12-byte IV prepended) iv, ct = payload[:12], payload[12:] plaintext = AESGCM(shared).decrypt(iv, ct, None) return json.loads(plaintext)
// C equivalent for embedded / QuickJS // Dependencies: libsodium (Ed25519), // OpenSSL or mbedTLS (AES-256-GCM) int verify_envelope( const char* json_str, const uint8_t sender_pub[32], const uint8_t device_pub[32] ) { // 1. Parse JSON (cJSON / jsmn) // 2. Check v == 1, alg == "Ed25519" // 3. Verify |now - timestamp| < 30000ms // 4. Check nonce not in seen-set // 5. Verify signature crypto_sign_ed25519_verify_detached( sig, payload, payload_len, sender_pub ); // 6. Derive shared key // sort(pubA, pubB) -> SHA-256 uint8_t shared[32]; sort_and_concat(sender_pub, device_pub, buf); SHA256(buf, 64, shared); // 7. Decrypt AES-256-GCM // iv = payload[0:12] // ciphertext = payload[12:] EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, shared, iv); // 8. Parse decrypted JSON payload return 0; // success }
These are the same test vectors used in the @xail/crypto test suite — 136 tests, 100% line coverage, fuzz-tested, statistically verified for share independence. Use them to validate any independent implementation of XorIDA 2-of-3 sharing.
{
"description": "2-of-3 XorIDA — 'Hello!' with PKCS#7 padding",
"config": { "n": 3, "k": 2 },
"input": "[0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0x02, 0x02]",
"random_r": "[0xa3, 0x7f, 0x12, 0xe8, 0x5c, 0xb1, 0x9d, 0x44]",
"shares": {
"0": "[0xeb, 0x1a, 0x7e, 0x84, 0x33, 0x90, 0x9f, 0x46]",
"1": "[0xc6, 0x7f, 0x7e, 0xe8, 0x7d, 0xb1, 0x9f, 0x44]",
"2": "[0xa3, 0x37, 0x12, 0x84, 0x5c, 0xde, 0x9d, 0x46]"
},
"reconstruction": {
"[0,1]": "reconstructs to input ✓",
"[0,2]": "reconstructs to input ✓",
"[1,2]": "reconstructs to input ✓"
},
"single_share_entropy": "each share is indistinguishable from random — zero information about input"
}{
"description": "2-of-3 XorIDA — edge case, zero random",
"config": { "n": 3, "k": 2 },
"input": "[0xff, 0x01]",
"random_r": "[0x00, 0x00]",
"shares": {
"0": "[0xff, 0x01]",
"1": "[0x01, 0x00]",
"2": "[0x00, 0xff]"
},
"reconstruction": {
"[0,1]": "reconstructs to input ✓",
"[0,2]": "reconstructs to input ✓",
"[1,2]": "reconstructs to input ✓"
},
"note": "Zero random exposes structure — production MUST use crypto.getRandomValues(). This vector verifies the algebraic identity holds even in degenerate cases."
}
// Reconstruction algorithm (any 2 of 3):
// Given shares[i] and shares[j] where i < j:
// row_i XOR row_j → intermediate
// Apply GF(2) inverse of (M[i] XOR M[j])
// to recover the original padded message.
//
// The generator matrix M is derived from
// the Cauchy matrix over GF(2^8), projected
// to binary — XorIDA construction.For law firms, healthcare organizations, and financial services firms — where "we use TLS" is not a sufficient answer.
We'd rather you know the trade-offs now than discover them in production. Atelier adds a cryptographic layer — that's power, but it's also complexity. Here's when it's worth it.
The envelope format and verify-only implementation are available independently of the SDK. Start with the spec. Add the convenience layer when the complexity is justified.
@xail/agent-atelier is available for approved early access. Download the SDK, read the technical architecture, or share the executive brief with your leadership team.