Skip to main content

JACS Python Library

Cryptographic identity, signing, and verification for AI agents from Python.

Registry status (observed 2026-07-11): PyPI serves jacs==0.11.3; this branch is source 0.13.0. Pin the package when exact cross-language contracts matter.

pip install jacs

Prebuilt native bindings are distributed via maturin. A normal install does not require compiling Rust.

First use of the jacs command

Installing the Python library does not download a CLI. The first invocation of the jacs command downloads the matching cli/vX.Y.Z GitHub Release into an exact-version and platform-specific user cache. It fetches sha256sums.txt first (with a validated per-asset checksum fallback), verifies SHA-256, rejects unsafe archive paths, and then executes the cached binary. Linux musl is not a published CLI target, so musl systems fail clearly instead of downloading the glibc build. Unsupported platforms or failed verification exit nonzero with the fallback command cargo install jacs-cli. In an active virtual environment the Python shim may remain first on PATH; invoke Cargo's installed binary directly as ~/.cargo/bin/jacs (or .cargo\\bin\\jacs.exe under your Windows user profile). If CARGO_HOME is set, use its bin/jacs executable instead.

Importing or using the Python library performs no CLI download.

Full documentation | Quick Start

Quick start

import jacs.simple as jacs

info = jacs.quickstart(name="my-agent", domain="my-agent.example.com")
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 exists, it loads it; otherwise it creates a new agent.

Core operations

Operation Description
quickstart(name, domain) Create or load a persistent agent
load() Load an existing agent from config
sign_message() Sign JSON-serializable data
sign_file() Sign a file with optional embedding
verify() Verify a signed document
verify_standalone() Verify without loading an agent
create_agreement_v2() Create a standalone Agreement v2 document
sign_agreement_v2() Sign as signer, witness, or notary
verify_agreement_v2() Verify Agreement v2 hash, policy, transcript, and status
export_agent() Export agent JSON for sharing
audit() Run a security audit

Public human-approved document verification

The wheel build profile enables human-approval-vendored: the existing native WebAuthn verifier with OpenSSL compiled into the extension, without a separate OpenSSL installation. maturin develop uses this same profile. This configures new builds; it does not claim that an older published wheel has the method. Release gates check the installed artifact and reject external OpenSSL linkage. Browser WASM does not provide this verifier.

Rust defaults remain unchanged. A minimal custom extension can omit the feature using cargo build -p jacspy --features extension-module,a2a,agreements,attestation; it does not expose this method. human-approval alone remains available for custom builds intentionally using a system OpenSSL installation.

from jacs import SimpleAgent

report = SimpleAgent.verify_human_approved_document(
    bundle_json, expected_json, authority_json, provenance_json
)

This static method needs no agent, signing key, configuration, or network lookup. All four arguments are JSON strings. Read stored public evidence into bundle_json; select the expected human/action/credential and the two public key pins from your application's trusted state, not from the bundle. The complete report is a dict. Success confirms retained proof and document integrity, not permission to execute now: current remains not_evaluated. Live authorization, revocation and one-use checks stay with the relying application. Feature-enabled tests require JACS_TEST_HUMAN_APPROVAL=1 pytest tests/test_human_approved_document.py.

Request authentication and signed events

The instance-based SimpleAgent exposes the transport protocol helpers. Build the header from the exact request values that will be sent:

import json
from jacs import SimpleAgent

agent, _ = SimpleAgent.ephemeral(algorithm="ed25519")
body = '{"action":"approve"}'
authorization = agent.build_request_auth_header(
    "POST",
    "https://api.example.com/v1/jobs?mode=strict",
    body,
    "jobs-api",
)
assert authorization.startswith("JACS v2.")

envelope = agent.sign_response(json.dumps({"decision": "allow"}))
signer_id = json.loads(envelope)["jacsSignature"]["agentID"]
server_keys = json.dumps({signer_id: agent.get_public_key_pem()})
verified = json.loads(agent.unwrap_signed_event(envelope, server_keys))
assert verified["verified"] is True
assert verified["data"]["decision"] == "allow"

build_request_auth_header() accepts bytes for arbitrary request bodies or str (encoded as UTF-8), and binds the exact bytes with method, absolute URL including query, audience, signer/key, issue time, and nonce. Do not serialize the body again after building the header. sign_response() emits a 2.0.0 envelope whose jacs-response-v2 signature covers both payload and metadata. unwrap_signed_event() is fail-closed: plain events, legacy payload-only envelopes, unknown signers, and mutations raise instead of returning data.

Multi-replica consumers should release signed-event payloads through their own shared atomic replay store:

from jacs import unwrap_signed_event_with_replay_store

# store.scope must be "shared". consume(key, ttl_seconds) returns True only
# for the first atomic consumer and False for a duplicate.
verified = await unwrap_signed_event_with_replay_store(
    agent,
    envelope,
    server_keys,
    store,
)

Native verification and freshness checks run before the store is called and return no payload. The helper validates the exact-input digest, consumes the native replay key once, rechecks expiry, and only then returns data with its authenticated signer, timestamp, algorithm, and document ID. Store failures, timeouts, invalid results, process-local stores, duplicates, and expiry all fail closed with a stable ReplayError.code.

The server_keys object is a trust decision, not discovery: each key must be pinned or resolved under the application's configured policy. Successful verification proves possession of that key; it does not independently prove a person, organization, domain, or other real-world identity.

The old no-argument build_auth_header() remains available for source compatibility and emits a WARN because it does not bind the request. Migrate clients and servers together to JACS v2; strict deployments can reject the legacy method with JACS_REJECT_UNBOUND_AUTH_HEADER=true.

Text and image provenance

Python exposes the same inline text and image signing surface as the CLI:

import jacs.simple as jacs
from jacs import MissingSignatureError

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

# Markdown/text: append and verify an inline signature block.
jacs.sign_text("README.md")
text = jacs.verify_text("README.md")
print(text.status)  # 'signed' | 'missing_signature' | 'malformed'

try:
    jacs.verify_text("README.md", strict=True)
except MissingSignatureError:
    print("not signed")

jacs.verify_text("README.md", key_dir="./trusted-keys/")

# Images: embed and verify a signature in PNG, JPEG, or WebP metadata.
jacs.sign_image("photo.png", out="signed.png")
image = jacs.verify_image("signed.png")
print(image.status)  # 'valid'

payload = jacs.extract_media_signature("signed.png")

The same methods are available on the instance-based JacsClient for multi-agent processes. These signatures prove that an agent signed specific canonical bytes at its claimed time; they do not prove first creation or legal ownership.

Verify without an agent

result = jacs.verify_standalone(signed_json, key_directory="./keys")

Cross-language interop is tested on every commit. Documents signed in Rust or Node.js verify in Python, and Python-signed documents verify in the other bindings.

Agreement v2

Use Agreement v2 for new multi-agent consent workflows:

from jacs import SimpleAgent

agent, info = SimpleAgent.ephemeral(algorithm="ed25519")
agent_id = info["agent_id"]

agreement = agent.create_agreement_v2({
    "title": "Refund approval",
    "description": "Approval for a bounded refund.",
    "terms": "Refund up to $25 for order 123.",
    "status": "proposed",
    "parties": [{"agentId": agent_id, "agentType": "ai", "role": "signer"}],
    "signaturePolicy": {"partyQuorum": "all", "witnessRequired": 0, "notaryRequired": 0},
    "controllers": [agent_id],
})

signed = agent.sign_agreement_v2(agreement, "signer")
assert agent.verify_agreement_v2(signed)["valid"]

The same operations are available as module-level functions once an agent is loaded (quickstart() / create() / load()), matching Node's @hai.ai/jacs/simple surface:

import jacs.simple as jacs

jacs.quickstart(name="my-agent", domain="agent.example.com")
agent_id = jacs.get_agent_info().agent_id

agreement = jacs.create_agreement_v2({
    "title": "Refund approval",
    "description": "Approval for a bounded refund.",
    "terms": "Refund up to $25 for order 123.",
    "status": "proposed",
    "parties": [{"agentId": agent_id, "agentType": "ai", "role": "signer"}],
    "signaturePolicy": {"partyQuorum": "all", "witnessRequired": 0, "notaryRequired": 0},
    "controllers": [agent_id],
})

signed = jacs.sign_agreement_v2(agreement, "signer")
assert jacs.verify_agreement_v2(signed)["valid"]

Verifying another agent's agreement signature requires that agent's public key, so distinct agents must share a data_directory or exchange public keys; ephemeral agents verify only their own signatures.

The older create_agreement() / sign_agreement() / check_agreement() methods remain for simple jacsAgreement sidecars on existing documents.

Framework adapters

pip install jacs[langchain]    # LangChain / LangGraph
pip install jacs[fastapi]      # FastAPI / Starlette
pip install jacs[anthropic]    # Anthropic / Claude SDK
pip install jacs[a2a]          # A2A protocol
pip install jacs[all]          # Everything

jacs.a2a_server.jacs_a2a_routes() caches all six discovery documents together and refreshes them on the first request at six days after the binding's signed issuedAt. Concurrent FastAPI handlers share one locked refresh. Failed refreshes retry at most once per minute; the old snapshot is served only within its seven-day Strict lifetime and any earlier expiresAt. Unavailable discovery returns a non-cacheable 503. HTTP freshness ends by the six-day renewal boundary or earlier expiry. Still-valid snapshots awaiting renewal use no-store. Finite expiry is never extended: renew authorization and remount the router to serve a newly authorized binding after expiry. Separate resource requests can straddle renewal even though publication is atomic within the process. Clients must verify the card/JWKS/binding together and refetch on a mismatch; these endpoints are not a transactional bundle.

Instance-based API

For multiple agents in one process:

from jacs.client import JacsClient

client = JacsClient.quickstart(name="my-agent", domain="example.com")
signed = client.sign_message({"action": "approve"})

See DEVELOPMENT.md for the full API reference, advanced usage, framework adapter examples, and testing utilities.

Metadata

Release files for jacs 0.15.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jacs 0.15.0
File Size Uploaded Provenance Actions
jacs-0.15.0.tar.gz 1.7 MB With provenance Details for jacs-0.15.0.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for jacs 0.15.0
File Actions
jacs-0.15.0-cp310-abi3-musllinux_1_2_x86_64.whl CPython 3.10 abi3 Linux musl 1.2+ x86-64 Details for jacs-0.15.0-cp310-abi3-musllinux_1_2_x86_64.whl
jacs-0.15.0-cp310-abi3-manylinux_2_28_x86_64.whl CPython 3.10 abi3 Linux glibc 2.28+ x86-64 Details for jacs-0.15.0-cp310-abi3-manylinux_2_28_x86_64.whl
jacs-0.15.0-cp310-abi3-manylinux_2_28_aarch64.whl CPython 3.10 abi3 Linux glibc 2.28+ ARM64 Details for jacs-0.15.0-cp310-abi3-manylinux_2_28_aarch64.whl
jacs-0.15.0-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details for jacs-0.15.0-cp310-abi3-macosx_11_0_arm64.whl
jacs-0.15.0-cp310-abi3-macosx_10_12_x86_64.whl CPython 3.10 abi3 macOS 10.12+ x86-64 Details for jacs-0.15.0-cp310-abi3-macosx_10_12_x86_64.whl

Total release size: 61.5 MB

Release files / jacs-0.15.0.tar.gz

Download URL jacs-0.15.0.tar.gz
Size 1.7 MB
Tags Source
SHA-256 checksum
How to use checksums
56bfbf5d1e0b970718de7fa43e07a665ee90899ddbe731390e94ebe1b0be8e13
BLAKE2b-256 checksum
How to use checksums
f31d8cd06146f670c4eafda4fb42422334df46fb9d6d859418db29fb9166d94d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release files / jacs-0.15.0-cp310-abi3-musllinux_1_2_x86_64.whl

Download URL jacs-0.15.0-cp310-abi3-musllinux_1_2_x86_64.whl
Size 11.3 MB
Tags CPython 3.10 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
df4b4fb12c3321d04816926482051498904b942a9755fbf9a6f46574abe8736e
BLAKE2b-256 checksum
How to use checksums
e3979376de2aecca02051ed17607e3ca9f0ffe91d72c9ca45c0cae04631e9f59
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release files / jacs-0.15.0-cp310-abi3-manylinux_2_28_x86_64.whl

Download URL jacs-0.15.0-cp310-abi3-manylinux_2_28_x86_64.whl
Size 12.6 MB
Tags CPython 3.10 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
be44612351b6ef0ffe1a135f033bdb6f1befb7cdbf4e028756063433b731f15f
BLAKE2b-256 checksum
How to use checksums
117b42ffd181828e582445ea168f6ee6117aff61735c89c0076067118eb45057
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release files / jacs-0.15.0-cp310-abi3-manylinux_2_28_aarch64.whl

Download URL jacs-0.15.0-cp310-abi3-manylinux_2_28_aarch64.whl
Size 12.9 MB
Tags CPython 3.10 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
c39e31354c58dc342a2ef47bf4a0a0694cee2ff3dc171583b8a5895ca319850a
BLAKE2b-256 checksum
How to use checksums
e06b3f4c68e3c6e44de54b65e3c473f9f083e043bc66d9aa3064c76d522ace1a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release files / jacs-0.15.0-cp310-abi3-macosx_11_0_arm64.whl

Download URL jacs-0.15.0-cp310-abi3-macosx_11_0_arm64.whl
Size 11.4 MB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
4db57dfd8ec558e0f1068728020e7d04ffe32df2ee9986f21c64dbeb7cfa891d
BLAKE2b-256 checksum
How to use checksums
14f98f3ba1decc00124d05248c4e53d33ea3ce27fe24d5d3cb666481cf6c3c40
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release files / jacs-0.15.0-cp310-abi3-macosx_10_12_x86_64.whl

Download URL jacs-0.15.0-cp310-abi3-macosx_10_12_x86_64.whl
Size 11.6 MB
Tags CPython 3.10 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
3bd730951275a09de59adb130e54e72ab2dcb7f3242b603172d884a06e2bac48
BLAKE2b-256 checksum
How to use checksums
c185d1db5bf28ff8de76b06b38146409fc986fb733ab04a7b039cdf4fd2e5362
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Security for jacs 0.15.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 6 files in this release have been uploaded with provenance metadata.

Select from 6 files

Attestation for jacs-0.15.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page