Skip to main content

JACS - JSON AI Communication Standard: Cryptographic signing and verification for AI agents.

Project description

JACS Python Library

Sign it. Prove it.

Cryptographic signatures for AI agent outputs -- so anyone can verify who said what and whether it was changed. No server. Three lines of code. Optionally register with HAI.ai for cross-organization key discovery.

Which integration should I use? | Full documentation

# Using uv (recommended)
uv pip install jacs

# Or with pip
pip install jacs

# With HAI.ai integration
uv pip install jacs[hai]

Packaging/build metadata is defined in pyproject.toml (maturin). setup.py is intentionally not used.

To check dependencies for known vulnerabilities when using optional extras, run pip audit (or safety check).

Quick Start

Zero-config -- one call to start signing:

import jacs.simple as jacs

jacs.quickstart()
signed = jacs.sign_message({"action": "approve", "amount": 100})
result = jacs.verify(signed.raw)
print(f"Valid: {result.valid}, Signer: {result.signer_id}")

quickstart() creates a persistent agent with keys on disk. If ./jacs.config.json already exists, it loads it; otherwise it creates a new agent. Agent, keys, and config are saved to ./jacs_data, ./jacs_keys, and ./jacs.config.json. If JACS_PRIVATE_KEY_PASSWORD is not set, a secure password is auto-generated and saved to ./jacs_keys/.jacs_password. Pass algorithm="ring-Ed25519" or algorithm="RSA-PSS" to override the default (pq2025).

Signed your first document? Next: Verify it standalone | Add framework adapters | Multi-agent agreements | Full docs

Advanced: Loading an existing agent

If you already have an agent (e.g., created by a previous quickstart() call), load it explicitly:

import jacs.simple as jacs

agent = jacs.load("./jacs.config.json")

# Sign a message (accepts dict, list, str, or any JSON-serializable data)
signed = jacs.sign_message({"action": "approve", "amount": 100})
print(f"Signed by: {signed.agent_id}")

# Verify it
result = jacs.verify(signed.raw)
print(f"Valid: {result.valid}")

# Sign a file
signed_file = jacs.sign_file("document.pdf", embed=True)

# Update agent metadata
agent_doc = json.loads(jacs.export_agent())
agent_doc["jacsAgentType"] = "updated-service"
updated = jacs.update_agent(agent_doc)

# Update a document
doc = json.loads(signed.raw)
doc["content"]["status"] = "approved"
updated_doc = jacs.update_document(signed.document_id, doc)

Core Operations

The simplified API provides these core operations:

Operation Description
quickstart() Create a persistent agent with keys on disk -- zero config, no manual setup
create() Create a new agent programmatically (non-interactive)
load() Load an existing agent from config
verify_self() Verify the loaded agent's integrity
update_agent() Update the agent document with new data
update_document() Update an existing document with new data
sign_message() Sign a text message or JSON data
sign_file() Sign a file with optional embedding
verify() Verify any signed document (JSON string)
verify_standalone() Verify without loading an agent (one-off)
verify_by_id() Verify a document by its storage ID (uuid:version)
get_dns_record() Get DNS TXT record line for the agent
get_well_known_json() Get well-known JSON for /.well-known/jacs-pubkey.json
reencrypt_key() Re-encrypt the private key with a new password
trust_agent() Add an agent to the local trust store
list_trusted_agents() List all trusted agent IDs
untrust_agent() Remove an agent from the trust store
is_trusted() Check if an agent is trusted
get_trusted_agent() Get a trusted agent's JSON document
audit() Run a read-only security audit (returns risks, health_checks, summary)
generate_verify_link() Generate a shareable hai.ai verification URL for a signed document

Programmatic Agent Creation

import jacs.simple as jacs

# Create an agent without interactive prompts
agent = jacs.create(
    name="my-agent",
    password="Str0ng-P@ssw0rd!",  # or set JACS_PRIVATE_KEY_PASSWORD env var
    algorithm="pq2025",            # default; also: "ring-Ed25519", "RSA-PSS"
    data_directory="./jacs_data",
    key_directory="./jacs_keys",
)
print(f"Created agent: {agent.agent_id}")

Standalone Verification (No Agent Required)

Verify a signed document without loading an agent. Useful for one-off verification, CI/CD pipelines, or services that only need to verify, not sign.

import jacs.simple as jacs

result = jacs.verify_standalone(
    signed_json,
    key_resolution="local",
    key_directory="./trusted-keys/"
)
if result.valid:
    print(f"Signed by: {result.signer_id}")

Generate a shareable verification link:

url = jacs.generate_verify_link(signed_doc.raw_json)
# https://hai.ai/jacs/verify?s=<base64url-encoded-document>

Documents signed by Rust or Node.js agents verify identically in Python -- cross-language interop is tested on every commit with Ed25519 and pq2025 (ML-DSA-87). See the full Verification Guide for CLI, DNS, and cross-language examples.

Verify by Document ID

# If you have a document ID instead of the full JSON
result = jacs.verify_by_id("550e8400-e29b-41d4-a716-446655440000:1")
print(f"Valid: {result.valid}")

Re-encrypt Private Key

jacs.reencrypt_key("old-password-123!", "new-Str0ng-P@ss!")

Password Requirements

Passwords must be at least 8 characters and include uppercase, lowercase, a digit, and a special character.

Algorithm Deprecation Notice

The pq-dilithium algorithm is deprecated. Use pq2025 (ML-DSA-87, FIPS-204) instead. pq-dilithium still works but emits deprecation warnings.

Type Definitions

from jacs import AgentInfo, SignedDocument, VerificationResult

# All return types are dataclasses with clear fields
agent: AgentInfo = jacs.load()
signed: SignedDocument = jacs.sign_message({"data": "hello"})
result: VerificationResult = jacs.verify(signed.raw)

JacsClient (Instance-Based API)

When you need multiple agents in one process, or want to avoid global state, use JacsClient. Each instance wraps its own JacsAgent with independent keys and config.

from jacs.client import JacsClient

# Load from config
client = JacsClient("./jacs.config.json")
signed = client.sign_message({"action": "approve"})
result = client.verify(signed.raw_json)
print(f"Valid: {result.valid}, Agent: {client.agent_id}")

# Or zero-config quickstart (creates keys on disk)
client = JacsClient.quickstart()

# Context manager for automatic cleanup
with JacsClient.quickstart() as client:
    signed = client.sign_message("hello")

Multi-Agent Example

from jacs.client import JacsClient

alice = JacsClient.ephemeral()
bob = JacsClient.ephemeral()

signed = alice.sign_message({"from": "alice"})
result = bob.verify(signed.raw_json)
print(f"Alice: {alice.agent_id}")
print(f"Bob verifies Alice's message: {result.valid}")

Agreements with Timeout and Quorum

create_agreement accepts flat keyword arguments for advanced options:

from datetime import datetime, timedelta, timezone

agreement = client.create_agreement(
    document={"proposal": "Deploy model v2"},
    agent_ids=[alice.agent_id, bob.agent_id, mediator.agent_id],
    question="Do you approve?",
    quorum=2,                    # 2-of-3 signatures required
    timeout=(datetime.now(timezone.utc) + timedelta(hours=1)).isoformat(),
    required_algorithms=None,    # optional: restrict signing algorithms
    minimum_strength=None,       # optional: "classical" or "post-quantum"
)

signed = alice.sign_agreement(agreement)
status = alice.check_agreement(signed)
print(f"Complete: {status.complete}, Pending: {status.pending}")

See examples/multi_agent_agreement.py for a full 3-agent agreement demo with crypto proof chain.

JacsClient API Reference

Method Description
JacsClient(config_path) Load from config
JacsClient.quickstart() Zero-config persistent agent
JacsClient.ephemeral() In-memory agent (no disk, for tests)
sign_message(data) Sign JSON-serializable data
verify(document) Verify a signed document
verify_self() Verify agent integrity
verify_by_id(doc_id) Verify by storage ID
sign_file(path, embed) Sign a file
create_agreement(...) Create multi-party agreement
sign_agreement(doc) Co-sign an agreement
check_agreement(doc) Check agreement status
trust_agent(json) Add agent to trust store
list_trusted_agents() List trusted agent IDs
update_agent(data) Update and re-sign agent
update_document(id, data) Update and re-sign document
export_agent() Export agent JSON for sharing
audit() Run security audit
reset() Clear internal state

Framework Adapters

Auto-sign AI framework outputs with zero infrastructure. Install the extra for your framework:

pip install jacs[langchain]   # LangChain / LangGraph
pip install jacs[fastapi]     # FastAPI / Starlette
pip install jacs[crewai]      # CrewAI
pip install jacs[anthropic]   # Anthropic / Claude SDK
pip install jacs[all]         # Everything

LangChain -- sign every tool result via middleware:

from jacs.adapters.langchain import jacs_signing_middleware
agent = create_agent(model="openai:gpt-4o", tools=tools, middleware=[jacs_signing_middleware()])

FastAPI -- sign all JSON responses:

from jacs.adapters.fastapi import JacsMiddleware
app.add_middleware(JacsMiddleware)

CrewAI -- sign task outputs via guardrail:

from jacs.adapters.crewai import jacs_guardrail
task = Task(description="Analyze data", agent=my_agent, guardrail=jacs_guardrail())

Anthropic / Claude SDK -- sign tool return values:

from jacs.adapters.anthropic import signed_tool

@signed_tool()
def get_weather(location: str) -> str:
    return f"Weather in {location}: sunny"

See the Framework Adapters guide for full documentation, custom adapters, and strict/permissive mode details.

Testing

The jacs.testing module provides a pytest fixture that creates an ephemeral client with no disk I/O or env vars required:

from jacs.testing import jacs_agent

def test_sign_and_verify(jacs_agent):
    signed = jacs_agent.sign_message({"test": True})
    result = jacs_agent.verify(signed.raw_json)
    assert result.valid

def test_agent_has_unique_id(jacs_agent):
    assert jacs_agent.agent_id

The fixture automatically resets after each test.

MCP Integration

For AI tool servers using the Model Context Protocol:

from fastmcp import FastMCP
import jacs.simple as jacs

mcp = FastMCP("My Server")
jacs.load("./jacs.config.json")

@mcp.tool()
def signed_hello(name: str) -> dict:
    signed = jacs.sign_message({"greeting": f"Hello, {name}!"})
    return {"response": signed.raw}

JacsAgent Class (Advanced)

For more control, use the JacsAgent class directly:

from jacs import JacsAgent

agent = JacsAgent()
agent.load("./jacs.config.json")

# Sign raw strings
signature = agent.sign_string("data to sign")

# Verify documents
is_valid = agent.verify_document(document_json)

# Create documents with schemas
doc = agent.create_document(json_string, schema=None)

A2A Protocol Support

JACS supports Google's Agent-to-Agent (A2A) protocol:

from jacs.a2a import JACSA2AIntegration

a2a = JACSA2AIntegration("jacs.config.json")
agent_card = a2a.export_agent_card(agent_data)
wrapped = a2a.wrap_artifact_with_provenance(artifact, "task")

HAI.ai Integration

HAI.ai is a platform for agent-to-agent agreements and conflict resolution, providing cryptographic attestation of agent capabilities.

Quick Registration

from jacs.hai import HaiClient
import jacs.simple as jacs

# Load your JACS agent
jacs.load("./jacs.config.json")

# Connect to HAI.ai
hai = HaiClient()

# Test connection
if hai.testconnection("https://hai.ai"):
    # Register your agent
    result = hai.register("https://hai.ai", api_key="your-api-key")
    print(f"Registered: {result.agent_id}")

Prerequisites

Available Methods

Method Description
testconnection() Test HAI.ai connectivity
register() Register agent with HAI.ai
verify_agent() Verify another agent's trust level
status() Check registration status
benchmark() Run benchmark suite
connect() Connect to SSE event stream

Agent Verification Levels

JACS agents can be verified at three trust levels:

Level Badge What it proves
1 Basic Agent holds a valid private key (self-signed)
2 Domain Agent owner controls a DNS domain
3 Attested HAI.ai has verified and co-signed the agent
from jacs.hai import verify_agent

# Verify another agent meets your trust requirements
result = verify_agent(sender_agent_doc, min_level=2)

if result.valid:
    print(f"Verified: {result.agent_id} (Level {result.level}: {result.level_name})")
else:
    print(f"Verification failed: {result.errors}")

Examples

  • examples/hai_quickstart.py - 5-minute quickstart
  • examples/register_with_hai.py - Complete registration example

Installation

# Basic installation
pip install jacs

# With framework adapters
pip install jacs[langchain]    # LangChain / LangGraph
pip install jacs[fastapi]      # FastAPI / Starlette
pip install jacs[crewai]       # CrewAI
pip install jacs[anthropic]    # Anthropic / Claude SDK
pip install jacs[all]          # All adapters + MCP + HAI

# With MCP support
pip install jacs[mcp]

# With HAI.ai integration
pip install jacs[hai]

Examples

See the examples/ directory:

  • quickstart.py - Basic signing and verification
  • sign_file.py - File signing with embeddings
  • mcp_server.py - Authenticated MCP server
  • p2p_exchange.py - Peer-to-peer trust establishment
  • multi_agent_agreement.py - Three-agent agreement with quorum, timeout, and crypto proof chain

Development

Using uv (recommended):

# Quick start with Makefile
make setup   # Install all dependencies
make dev     # Build for development
make test    # Run all tests

# Or manually:
uv venv && source .venv/bin/activate
uv pip install maturin pytest httpx httpx-sse
uv run maturin develop
uv run python -m pytest tests/ -v

Available Make Commands

Command Description
make setup Install dev dependencies with uv
make dev Build Rust extension for development
make test Run all tests (Python + HAI)
make test-hai Run HAI integration tests only
make check-imports Verify all imports work

Documentation

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jacs-0.8.0.tar.gz (609.8 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

jacs-0.8.0-cp310-abi3-musllinux_1_2_x86_64.whl (6.3 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ x86-64

jacs-0.8.0-cp310-abi3-manylinux_2_34_x86_64.whl (7.2 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.34+ x86-64

jacs-0.8.0-cp310-abi3-manylinux_2_34_aarch64.whl (7.2 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.34+ ARM64

jacs-0.8.0-cp310-abi3-macosx_11_0_arm64.whl (6.5 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

jacs-0.8.0-cp310-abi3-macosx_10_12_x86_64.whl (6.7 MB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

File details

Details for the file jacs-0.8.0.tar.gz.

File metadata

  • Download URL: jacs-0.8.0.tar.gz
  • Upload date:
  • Size: 609.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for jacs-0.8.0.tar.gz
Algorithm Hash digest
SHA256 1424378470094842b575f4172ad98039f8bb6c64625e6b3f55c9363f6cf465dd
MD5 3f193380120be44fce75a2fcccc3ec48
BLAKE2b-256 5f4c4ca80773ce1031cf889014459c1c253871c468cbb470f46acc6f0e088dc7

See more details on using hashes here.

Provenance

The following attestation bundles were made for jacs-0.8.0.tar.gz:

Publisher: release-pypi.yml on HumanAssisted/JACS

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jacs-0.8.0-cp310-abi3-musllinux_1_2_x86_64.whl.

File metadata

  • Download URL: jacs-0.8.0-cp310-abi3-musllinux_1_2_x86_64.whl
  • Upload date:
  • Size: 6.3 MB
  • Tags: CPython 3.10+, musllinux: musl 1.2+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for jacs-0.8.0-cp310-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 748943b126d22f2b255bec14c12e2756a665633557a37ca244866ba5f7f8020b
MD5 2f79fde0d0066e3f050cc31e6dc06c47
BLAKE2b-256 39b9061ce554a541bee7deb4a4e3a2e96c7395c9d828b6ab71543903b0cae0fd

See more details on using hashes here.

Provenance

The following attestation bundles were made for jacs-0.8.0-cp310-abi3-musllinux_1_2_x86_64.whl:

Publisher: release-pypi.yml on HumanAssisted/JACS

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jacs-0.8.0-cp310-abi3-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for jacs-0.8.0-cp310-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 98da01a1a9c1e72537518e827899cb9a16c4b67cd3b339c5155e8263192979ef
MD5 5eba1197e38fe2ef548e91edd51fa8ba
BLAKE2b-256 2f1c30940ad339e8fe47e588dfed995ce049f411c6831872da2b8f7115b8912c

See more details on using hashes here.

Provenance

The following attestation bundles were made for jacs-0.8.0-cp310-abi3-manylinux_2_34_x86_64.whl:

Publisher: release-pypi.yml on HumanAssisted/JACS

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jacs-0.8.0-cp310-abi3-manylinux_2_34_aarch64.whl.

File metadata

File hashes

Hashes for jacs-0.8.0-cp310-abi3-manylinux_2_34_aarch64.whl
Algorithm Hash digest
SHA256 2bde05fb76c37159a2e7fd580f7f876847acfe111564c124b28fec135bbd9861
MD5 ed865bd7db276f5fd274a189b1e7450a
BLAKE2b-256 9a3ad854fcd0c424ff911dd198fef06c8b81192baab5ea58082e86d3755edd4c

See more details on using hashes here.

Provenance

The following attestation bundles were made for jacs-0.8.0-cp310-abi3-manylinux_2_34_aarch64.whl:

Publisher: release-pypi.yml on HumanAssisted/JACS

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jacs-0.8.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

  • Download URL: jacs-0.8.0-cp310-abi3-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 6.5 MB
  • Tags: CPython 3.10+, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for jacs-0.8.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 dacc0d313f0703e76ce696b9a9cb84d7d30b79909bc342523c71e33c30e172c5
MD5 179ad409bd6eda8ee344f27c5976a720
BLAKE2b-256 269e957d8a6cd882c90a66cc33924e2ed1462b034fcc53439c6f95a6118f2cc8

See more details on using hashes here.

Provenance

The following attestation bundles were made for jacs-0.8.0-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: release-pypi.yml on HumanAssisted/JACS

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jacs-0.8.0-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for jacs-0.8.0-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 f992252798174c918b1d04907ecf6d84f1d16f5a1d5f4aa2ee77c732f9efa73b
MD5 684b2fb89bcbf36e28e4c9da14c41c34
BLAKE2b-256 848d81016c199735e4329c754ad69df0de04a599092accbbd44c7ddbcb39034e

See more details on using hashes here.

Provenance

The following attestation bundles were made for jacs-0.8.0-cp310-abi3-macosx_10_12_x86_64.whl:

Publisher: release-pypi.yml on HumanAssisted/JACS

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page