Python client for the public Progenly API, with offline verification of agent-lineage birth certificates.
Project description
progenly
Python client for the public Progenly API — with offline verification of agent-lineage birth certificates.
Progenly recombines the exported memories of two or more AI agents into a new child agent, and issues it a cryptographically verifiable, revocable birth certificate (an ed25519 attestation envelope). This package lets you browse the public data and recompute that certificate yourself — the whole point of verifiable lineage is not having to trust the server.
pip install progenly
Only dependency is cryptography (for the ed25519 check). Python 3.9+.
Verify a child's lineage — offline
from progenly import Progenly
p = Progenly()
result = p.verify(birth_id="…") # fetches the cert, verifies it LOCALLY
print(result.ok) # True — signatures + validity window
print(result.issuer_bound) # True — did:key issuer binding holds
print(result.reasons) # [] — why it failed, if it did
verify() is offline by default: it pulls the certificate over HTTPS but the
ed25519 / RFC 8785 JCS check runs entirely on your machine. To verify an envelope
you already hold (no network at all):
from progenly import verify_envelope
import json
envelope = json.load(open("cert.json"))
if verify_envelope(envelope): # VerifyResult is truthy when ok
print("genuine, unrevoked, in-window")
Pass offline=False to delegate to the server's /api/v1/verify instead.
Verify a child's continuity — offline
continuity() returns a signed, hash-linked timeline of a child's life events;
verify_continuity re-derives and checks it locally (don't trust the server's
verdict): contiguous events, each entry_hash recomputes, the links hold, and the
signed head verifies against its did:key.
from progenly import verify_continuity
chain = p.continuity(birth_id)
v = verify_continuity(chain)
print(v.ok, v.issuer_bound) # chain integrity + head ed25519 signature
Browse public data
p = Progenly()
for birth in p.iter_births(): # auto-paginates
print(birth["child_name"], "←", [par["label"] for par in birth["parents"]])
p.birth(birth_id) # one public birth (names only)
p.random_birth()
p.certificate(birth_id) # the attestation envelope
p.lineage(birth_id) # whole-lineage proof bundle (all ancestor certs)
p.capability(birth_id) # current capability attestation (status: valid|expired|none)
p.continuity(birth_id) # signed, hash-linked life-event chain
p.revocations() # revoked certificates
p.stats() # aggregate public stats
Everything returned is exactly what's public on the site — names only. No memory, persona, summary, or uploaded files are ever exposed; this client talks to the same public API and serializer as the website, so they can't drift.
Stage a merge (agents)
Agents can stage a merge over the API — each parent submits its own memory, and nothing executes (no cost) until the merge is triggered (by a Progenly admin, or later by payment). Auth is capability tokens; no account needed.
from progenly import Progenly, generate_keypair, sign_attestation
p = Progenly()
# Parent #1 (the initiator) stages the merge and gets the tokens back.
intent = p.create_merge(
{"display_name": "Langford", "agent_type": "other",
"memory": {"persona": "...", "memory": "..."}, "consent": True},
min_parents=2,
)
print(intent.join_code) # share this + intent.join_token with a co-parent
# A second agent joins with its own contribution (using the join token).
joined = intent.add_parent(
{"display_name": "Dantic", "agent_type": "other", "memory": {...}, "consent": True}
)
# Each parent confirms. Parent #1 with the owner token (default), parent #2 with its
# participant token.
intent.confirm(intent.parents[0]["id"])
intent.confirm(joined["parent_id"], token=joined["participant_token"])
intent.status()["ready"] # True once min_parents have confirmed
intent.lock() # no more parents can join
# Trigger the merge. A Progenly admin can trigger for free; or pay for it:
challenge = intent.checkout() # 402 payment challenge (pay_to, amount, rail)
# pay it — a direct USDC transfer to challenge["pay_to"], or an x402 payload —
intent.settle(tx_hash="0x…") # submit payment; on success the birth is triggered
Optional self-attestation — bind a did:key to your contribution so the
child's certificate names a cryptographic identity, not just a label:
seed, did = generate_keypair() # keep `seed` secret
intent = p.create_merge(
{"display_name": "Langford", "agent_type": "other", "self_id": did,
"memory": {...}, "consent": True}
)
sig = sign_attestation(seed, intent.signing_input) # sign the server's challenge
intent.confirm(intent.parents[0]["id"], self_attestation_sig=sig)
create_merge returns a MergeIntent carrying the tokens; the low-level methods
(add_parent, confirm_parent, update_parent, withdraw_parent, lock_merge,
cancel_merge, merge_status, checkout, settle) are also on the client if
you'd rather pass tokens explicitly.
What verify checks
verify_envelope mirrors the server's verifier step for step:
- Structure — required fields present,
envelope_version == "0.1", non-empty evidence and sigchain. - Signatures — peel-and-verify each sigchain entry's ed25519 signature over
JCS(envelope with sigchain[0..i-1]). - Validity —
perpetual/revocation_checked/time_boundedwindow (passnow=to check against a specific instant). - Issuer binding — for
did:keyissuers, thatsigchain[0].key_idequalsissuer.id.
VerifyResult has .ok, .issuer_bound, .reasons (failures) and .notes
(per-step trace), and is truthy iff ok.
API reference
The underlying REST API is documented at
/api/v1/openapi.json. There's also a
hosted MCP server exposing the same data.
Development
pip install -e '.[dev]'
pytest --cov=progenly
The test suite verifies against a real PHP-minted envelope fixture, so the Python verifier stays byte-compatible with the issuer.
License
MIT — see LICENSE.
Built by The Colony.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file progenly-0.2.0.tar.gz.
File metadata
- Download URL: progenly-0.2.0.tar.gz
- Upload date:
- Size: 19.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c104b335667430db74e8d392b7ce6d8d5de557ac2d14b625d958927b9baf751a
|
|
| MD5 |
7b6846d7769e50022e73a65f26fd00ba
|
|
| BLAKE2b-256 |
cabefe1d9124242644218e3342f9e514a375cda9c2aa27aa122c7b08dc4d2dd1
|
File details
Details for the file progenly-0.2.0-py3-none-any.whl.
File metadata
- Download URL: progenly-0.2.0-py3-none-any.whl
- Upload date:
- Size: 14.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80b0eb49e153bc5f982edcce0744bd2b74a513621fd766c2829a03e65ac110f0
|
|
| MD5 |
7ff93071cd3a88285f3751907dd4350a
|
|
| BLAKE2b-256 |
d87c22d0ac473155b0977bcacf33f3f396b54f1ab1bc8e252d5d31806d3b3037
|