openglass-sdk
The neutral witness for agent-to-agent interactions — lookup, private attestations, and witnessed sessions whose signed record both owners can read. Official Python client for OpenGlass.
pip install openglass-sdk, import openglass — the PyPI distribution name has the -sdk
suffix (plain openglass was already taken), but the importable module stays openglass.
Works with your framework? Already-OpenTelemetry-instrumented agents get checked and
attested automatically via openglass-otel.
See openglass.glass/integrations for status on
other frameworks, or to request one.
Quickstart
Look up a counterparty before you act — no auth, no setup:
from openglass import OpenGlassClient
client = OpenGlassClient()
info = client.lookup(agent_id="agt_...")
print(info["registered"], info["claimed"], info["flags"])
Privately attest to one of your own actions — a payment, a tool call, a decision — with no counterparty needed:
result = client.register_agent(name="My Agent", description="...")
client.wait_until_claimed() # returns once the owner opens claim.url and accepts
attestation = client.open_attestation(purpose="Called payment-tool with $500.")["attestation"]
client.send_attestation_event(attestation["id"], {"text": "Payment confirmed."})
client.close_attestation(attestation["id"])
record_id = client.wait_for_attestation_record(attestation["id"])
Or run a full two-party, witnessed session with another agent:
session = client.offer_session(purpose="...", counterparty_agent_id="agt_...")["session"]
client.wait_for_active(session["id"])
client.send_message(session["id"], {"text": "Hello — let's get started."})
client.close_session(session["id"])
record_id2 = client.wait_for_record(session["id"])
bundle = client.get_record_bundle(record_id2)
print(client.verify(bundle).valid) # True — independently re-derivable by anyone
No SDK-specific server setup required — register_agent generates your Ed25519 keypair locally
the first time you call it.
Install
pip install openglass-sdk
Python >= 3.10. Dependencies: httpx (HTTP) and cryptography (Ed25519 + ECDSA).
What this package is for
If you're an AI agent (or the code behind one) that wants to check a counterparty before acting
on anything it says, log your own high-risk actions privately, or run a session with another
agent and have both sides' human owners get an independently verifiable record, use this. You
don't need to trust OpenGlass's word for what happened — every hash and signature in a returned
record can be re-derived and checked locally with client.verify(bundle), which never makes a
network call beyond fetching OpenGlass's current public keys.
If you'd rather not add a dependency, see
skill.md
for the same protocol as plain HTTP requests with no SDK at all — this package is a thin,
ergonomic wrapper around exactly that same flow.
Core concepts
- Lookup:
client.lookup(...)checks whether any agent — yours or someone else's — is registered, claimed, and domain-verified, before you offer, accept, or act on anything. Public, no auth, works before you've even registered yourself. - Identity: an Ed25519 keypair, generated locally (
OpenGlassClient.generate_identity(), or automatically insideregister_agent()the first time you call it with no identity set). The private key never leaves your process — only the public key and signatures are sent. - Claiming: an agent can't attest or run sessions until its human owner "claims" it by
opening
claim["url"]and confirming the key fingerprint matches. This is by design — it's what makes a later record mean something (it's tied to a real accountable owner). - Attestations: a one-party record of your own agent's action — private by default, no counterparty, no invite, activates immediately.
- Sessions: two agents exchange a signed
offer/accept(the "genesis" of a hash chain), then zero or more signed, hash-chained messages, then a signedclose. Shared by default: both owners can read the full record from the moment it's issued. OpenGlass countersigns every step, so the whole exchange is tamper-evident even to OpenGlass itself after the fact. - Records & verification: once closed, OpenGlass issues a signed
RecordBundle— the full evidence trail plus its own countersignatures.client.verify(bundle)(or the standaloneverify_bundle()function) re-derives every hash and checks every signature locally; it returns aVerifyResult(valid, errors)listing every check that failed, not just the first one.
API reference
OpenGlassClient(base_url=..., identity=None, http_client=None)
A context manager (with OpenGlassClient(...) as client:) that owns an httpx.Client unless
you pass your own. base_url defaults to the production API; pass your own for local
development (e.g. a docker-compose stack) or a different deployment.
Lookup & domain verification
| Method | Description |
|---|---|
lookup(agent_id=None, domain=None, agent_card_url=None, public_key=None) |
Check any agent, registered or not, before offering or accepting a session with it. Public, no auth. Pass exactly one. |
request_domain_verification() |
Starts domain verification for your own meta.homepage; returns instructions for all three proof methods. |
verify_domain() |
Checks whether verification now succeeds — call after publishing the token. |
Agents
| Method | Description |
|---|---|
register_agent(name, description, meta=None) |
Registers a new agent (generating an identity first if needed). Returns {"agent": ..., "claim": ...}. |
get_agent(agent_id) |
Public lookup of any agent — no signing needed. |
me() |
Your own agent, full view (requires an identity). |
wait_until_claimed(interval_s=2.0, timeout_s=None) |
Polls until your owner has claimed you. No timeout by default; pass timeout_s if you're running under a bounded task budget. |
Attestations — the one-party counterpart to a session
| Method | Description |
|---|---|
open_attestation(purpose, mode="relay", attestation_id=None, idle_timeout_sec=86400, visibility=None) |
Logs one of your own agent's actions, no counterparty. Private by default; activates immediately. |
get_attestation(attestation_id) |
Fetch its current state. |
send_attestation_event(attestation_id, payload, ...) |
Appends one hash-chained event. seq/prev_hash tracked automatically. |
close_attestation(attestation_id) |
Signs and submits a close statement. |
wait_for_attestation_record(attestation_id, ...) |
Polls until closed and a record has been issued; returns the record_id. |
witness_fetch(attestation_id, url, method="GET", body=None) |
Asks OpenGlass itself to fetch url (GET, or POST with a JSON body) and witness the raw exchange directly — for a counterparty with no key to sign anything. |
get_fetch_witnesses(attestation_id) |
Lists an attestation's witnessed fetches, seq order. |
attested_fetch(attestation_id, url, mode=None, method="GET", body=None, direct_fetch=None) |
Chooses how much a fetch's witnessing costs and guarantees — see below. |
Sessions & invites
| Method | Description |
|---|---|
offer_session(purpose, counterparty_agent_id=None, ..., visibility=None) |
Builds, signs, and submits a session offer. Omit counterparty_agent_id for an open (bearer-link) invite. Shared by default ("sealed" is deprecated). |
get_session(session_id) / wait_for_active(session_id, ...) |
Fetch or poll-until-active a session. |
list_invites() |
Direct invites addressed to you. |
accept_invite(invite_id, token=None) |
Accepts an invite (pass token for an open/bearer-link invite). |
decline_invite(invite_id, token=None, reason=None) |
Declines one. |
Messages, close, records
| Method | Description |
|---|---|
send_message(session_id, payload, ...) |
Sends one witnessed message. seq/prev_hash are tracked automatically per session. |
witness(send, client, session_id) |
Wraps an existing send function so every call is witnessed first, then delivered — see below. |
close_session(session_id) |
Signs and submits a close statement. |
wait_for_record(session_id, ...) |
Polls until the session is closed and a record has been issued; returns the record_id. |
get_record_bundle(record_id) |
Fetches the full evidence bundle (a receipt instead, for a legacy sealed record not yet unsealed). |
verify(bundle) / verify_bundle(bundle, trusted_keys) |
Offline, local verification (SPEC §7.6) — no trust in OpenGlass required. |
verify_remote(bundle) |
Same check run server-side via POST /v1/verify, for when you'd rather not implement local verification. |
Disputes and legacy sealed records
These routes are owner-authenticated (the dashboard's session cookie); the client signs them as an agent, so they currently return 401 from an agent identity.
| Method | Description |
|---|---|
dispute(record_id) |
Flags a two-party record as disputed. Opens nothing, except that it force-unseals a legacy sealed record. |
request_unseal(record_id) |
Requests the unseal ceremony for a legacy visibility: "sealed" record; counts your own approval. |
approve_unseal(record_id) |
Adds your approval; fully unseals once every participant owner has called this. |
guard(): a pre-flight counterparty check for risky actions
For a tool call your own policy layer has already flagged as risky, look up the counterparty and decide allow/warn/block from policy you configure locally — fails open if OpenGlass is unreachable, so an infrastructure hiccup never blocks real work:
decision = client.guard(
risk="high", # from your own policy evaluation, e.g. openglass-policy via openglass-core
counterparty_agent_id="agt_...",
on_unverified_domain="warn", # default
on_new_counterparty="allow", # default
)
if decision["action"] == "block":
raise RuntimeError(decision["reason"])
if decision["action"] == "warn":
print(decision["reason"], decision["lookup"])
witness(): wrap your existing send function
from openglass import witness
send = witness(raw_send_to_counterparty, client=client, session_id=session["id"])
send({"text": "hello"}) # witnessed, then delivered exactly like raw_send_to_counterparty did
attested_fetch(): choosing how a fetch gets witnessed
For a fetch to a third party that isn't an OpenGlass agent — an A2A agent-card URL, a
message/send probe — you choose how much the witnessing should cost and guarantee:
result = client.attested_fetch(
attestation["id"],
"https://example.com/a2a",
mode="primary", # the default: OpenGlass's own fetch is the one real request
method="POST",
body={"jsonrpc": "2.0", "id": 1, "method": "SendMessage", "params": {"message": message}},
)
if result["witnessed"]:
print(result["witness"]["response"]["status"])
else:
print("unwitnessed:", result["reason"], result["direct"] and result["direct"]["status"])
"primary"(default): OpenGlass's own fetch is the one real request. Falls back to your owndirect_fetchonly if OpenGlass itself — not the target — can't be reached; that fallback is alwaysresult["witnessed"] is False."shadow": your owndirect_fetchis the real request (act onresult["direct"]); OpenGlass separately, best-effort, witnesses the same URL afterward. A second real request to the target — fine for an idempotent read, worth weighing for anything with side effects."off": no independent witness; equivalent to callingdirect_fetchyourself.
Always check result["witnessed"] before treating a result as independently verified — it's
False for "off", a failed "shadow" witness attempt, or "primary"'s own fallback, and
the plain fetch that actually ran is in result["direct"], never silently merged with a true
witness. Pass your own direct_fetch ((url, method, body) -> {"status", "headers", "body"})
to reuse an existing HTTP client's headers, timeouts, or response caps instead of the default
plain httpx request.
Low-level crypto exports
For advanced use, the primitives are exported directly: canonicalize/canonicalize_to_bytes
(RFC 8785 JCS), sha256/to_hex/hex_to_bytes, sig_input, generate_ed25519_keypair/
sign_ed25519/verify_ed25519, verify_signature, and verify_bundle. These are the exact
algorithms packages/db uses server-side, hand-ported and checked against real server-generated
vectors in this package's own test suite (tests/crypto/test_vectors.py, loading the same
fixtures/vectors.json that sdk-js uses).
Error handling
Every failed API call raises OpenGlassApiError (err.status, err.body with the server's
error code/message, err.method/err.path). A wait_* call that exceeds its timeout_s raises
OpenGlassTimeoutError.
Upgrading
0.3.0 and 0.2.0 are both purely additive — every earlier method keeps its existing signature
and behavior unchanged; offer_session's visibility keyword is optional. (At 0.2.0's
release the server defaulted sessions to "sealed"; since Sept 30 2026 it defaults them to
"shared". See the changelog.)
Contributing
pip install -e ".[dev]"
pytest # tests/test_integration.py and tests/test_lookup_and_attestations.py need a local stack (docker compose up -d --wait); they skip themselves otherwise
mypy src
License
MIT
Metadata
Release files for openglass-sdk 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| openglass_sdk-0.3.0.tar.gz | 34.8 kB | With provenance | Details for openglass_sdk-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| openglass_sdk-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for openglass_sdk-0.3.0-py3-none-any.whl |
Total release size: 66.1 kB
Release files / openglass_sdk-0.3.0.tar.gz
| Download URL | openglass_sdk-0.3.0.tar.gz |
|---|---|
| Size | 34.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d7a0ccae9f651c6327eea48c73839920fcf101f519c2d0db0e6ca228ca5bb8d9
|
|
BLAKE2b-256 checksum How to use checksums |
5da344cc3e3a6e6323e0e68f353b0fcc3f105ab462a819c6c616103f3a995fbb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 5, 2026.
Transparency logRelease files / openglass_sdk-0.3.0-py3-none-any.whl
| Download URL | openglass_sdk-0.3.0-py3-none-any.whl |
|---|---|
| Size | 31.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0a5624656460a43205de44e37bbf1997182abaf2f2d2b5962e0367dce8bc3e54
|
|
BLAKE2b-256 checksum How to use checksums |
5447625322d64c0f0e86a17794e2e173831a33012d4470dd8d55c42655bd90f0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 5, 2026.
Transparency logSecurity for openglass-sdk 0.3.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
Both files in this release have been uploaded with provenance metadata.
Attestation for openglass_sdk-0.3.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 Oct 5, 2026.
Transparency logA 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.