{"id":50776495,"url":"https://github.com/cardanowall/label-309-py","last_synced_at":"2026-06-12T00:02:12.420Z","repository":{"id":362058769,"uuid":"1256469264","full_name":"cardanowall/label-309-py","owner":"cardanowall","description":"Python SDK for Label 309 — a byte-parity twin of the TypeScript reference implementation.","archived":false,"fork":false,"pushed_at":"2026-06-06T17:27:58.000Z","size":701,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-06T19:13:28.971Z","etag":null,"topics":["blockchain","cardano","cip-309","proof-of-existence","python","sdk","timestamping"],"latest_commit_sha":null,"homepage":"https://label309.org","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cardanowall.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-01T20:06:50.000Z","updated_at":"2026-06-06T17:28:01.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cardanowall/label-309-py","commit_stats":null,"previous_names":["cardanowall/cip309-py","cardanowall/label-309-py"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/cardanowall/label-309-py","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cardanowall%2Flabel-309-py","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cardanowall%2Flabel-309-py/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cardanowall%2Flabel-309-py/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cardanowall%2Flabel-309-py/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cardanowall","download_url":"https://codeload.github.com/cardanowall/label-309-py/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cardanowall%2Flabel-309-py/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34222709,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blockchain","cardano","cip-309","proof-of-existence","python","sdk","timestamping"],"created_at":"2026-06-12T00:02:12.160Z","updated_at":"2026-06-12T00:02:12.392Z","avatar_url":"https://github.com/cardanowall.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cardanowall-sdk — the Python SDK for Label 309 Proof-of-Existence\n\nA byte-identical parity twin of the TypeScript `@cardanowall/sdk-ts`: a\nstandalone Label 309 verifier, a gateway-agnostic HTTP client, off-host signing,\nthe structural validator, and the raw-seed identity surface — all in Pythonic,\nmypy-strict form.\n\n## What it is\n\nLabel 309 is an open standard for anchoring a content hash on the Cardano\nblockchain under transaction metadata label 309, so anyone with the transaction\nreference can later prove \"this content existed on or before block time T\" —\nwithout trusting any server, domain, or issuer identity.\n\n`cardanowall-sdk` is the Python member of a five-package family. It bundles the\n**standalone verifier** (the three verifier roles), the **gateway-agnostic HTTP\nclient** for publishing and reading records against any Label 309 service, the\n**structural validator** over canonical-CBOR record bytes, the **sealed-PoE**\nwrap/unwrap primitives, and **raw-seed identity** helpers. Its cryptographic\ncore under `cardanowall._crypto` is a byte-for-byte parity twin of the\nTypeScript `@cardanowall/crypto-core` and Rust references: every encoder,\ndigest, signature, and KEM is validated against the same shared known-answer\nvectors, so a record produced or verified here is bit-identical to one produced\nor verified by any sibling SDK. The whole package is type-checked under\n`mypy --strict`.\n\n## Install\n\nThe package is not yet published to PyPI (it is pre-1.0, version `0.0.0`).\nBuild it from the workspace or vendor it from source. Requires **Python 3.11+**.\n\n```sh\n# From the package directory, build a wheel:\npython -m build        # or: uv build\npip install dist/cardanowall_sdk-*.whl\n```\n\nOnce published, the intended install form will be:\n\n```sh\npip install cardanowall-sdk   # forthcoming\n```\n\nThe SDK is async-canonical (built on `httpx.AsyncClient`); every client method\nreturns a coroutine. For synchronous use, wrap calls in `asyncio.run(...)`.\n\n## Quick start\n\n### Verify any Label 309 record — standalone, no service dependency\n\n`verify_tx` is the full public/recipient verifier. Given a Cardano transaction\nhash it fetches the metadata from a public explorer, runs structural validation,\nverifies record signatures, recomputes Merkle roots, and returns a discriminated\nreport. No issuer server is contacted.\n\n```python\nimport asyncio\nfrom cardanowall import verify_tx, VerifyTxInput\n\nreport = asyncio.run(verify_tx(VerifyTxInput(tx_hash=\"\u003c64-char hex tx hash\u003e\")))\nprint(report.verdict)  # \"valid\" | \"pending\" | \"failed\"\n```\n\n### Validate raw record bytes — pure, no I/O\n\n`validate_poe_record` (re-exported from `poe_standard`) is a pure function over\ncanonical-CBOR bytes. It returns a discriminated result: `ValidateOk` carries the\ntyped record, `ValidateFail` carries the structured issue list.\n\n```python\nfrom cardanowall import validate_poe_record\nfrom cardanowall.poe_standard import ValidateOk\n\nresult = validate_poe_record(record_bytes)  # bytes\nif isinstance(result, ValidateOk):\n    print(result.record[\"v\"])               # the parsed record\nelse:\n    for issue in result.issues:\n        print(issue.code, issue.path)        # e.g. \"SCHEMA_MISSING_REQUIRED\"\n```\n\n### Publish with the gateway-agnostic client\n\nThe client targets **any** Label 309 gateway. `base_url` is **required** and used\nverbatim; `api_key` is an **opaque bearer token** forwarded as\n`Authorization: Bearer \u003ckey\u003e` with no format assumptions. `cardanowall.com` below\nis only one example deployment — substitute any conformant gateway, including a\nself-hosted one.\n\n```python\nimport asyncio\nfrom cardanowall import Label309Client, signer_from_seed\n\nasync def main() -\u003e None:\n    signer = signer_from_seed(seed=b\"\\x00\" * 32)  # 32-byte seed; SDK never sees the private key persisted\n    async with Label309Client(\n        base_url=\"https://gateway.example.com\",\n        api_key=\"\u003copaque-bearer\u003e\",\n    ) as client:\n        quote = await client.poe.quote(\n            record_bytes=200, recipient_count=0, file_bytes_total=0,\n        )\n        out = await client.poe.publish_content(\n            content=\"hello world\",            # also accepts bytes\n            quote_id=quote[\"quote_id\"],\n            signer=signer,\n        )\n        print(out[\"id\"], out[\"status\"])\n\nasyncio.run(main())\n```\n\nEvery PoE submission requires a `quote_id`. Request a quote first (it locks the\nUSD price for a 15-minute TTL); pass the returned `quote_id` to the publish call.\nThe quote is consumed atomically with the record insert.\n\n## API overview\n\n### Verifier (`cardanowall.verifier`, top-level re-exports)\n\nThe three verifier roles, all reachable through `verify_tx`:\n\n- **Structural validator** — `validate_poe_record(bytes)`: pure, no I/O, no\n  crypto. Returns `ValidateOk | ValidateFail`.\n- **Public verifier** — `verify_tx(VerifyTxInput(...))`: fetches metadata, runs\n  structural validation, verifies record signatures, recomputes Merkle roots.\n- **Recipient verifier** — `verify_tx` with a decryption input (an X25519 / X-Wing\n  secret): additionally decrypts a sealed PoE and recomputes plaintext hashes.\n\nOutbound HTTP is funnelled through a single `FetchOutbound` so a caller can\ninject a custom transport (`default_fetch_outbound` is the built-in); a\ndeny-host floor (`DENY_HOSTS_DEFAULT`) blocks single-implementer domains\n(including `cardanowall.com`) to prove service-independence. `fetch_item_ciphertext`\nfetches sealed-PoE ciphertext bounded by `DEFAULT_OUTBOUND_MAX_BYTES`.\n`detect_conformance_profile` reports a record's conformance profile.\n\n### Gateway-agnostic client (`cardanowall.client`)\n\n`Label309Client(base_url=..., api_key=..., http_client=...)` exposes four\nnamespaces:\n\n- `client.poe` — `quote(...)`, `publish_content(...)`, `publish_sealed(...)`,\n  `publish_merkle(...)` (one-call high-level flows) plus the low-level\n  `uploads(...)`, `publish(...)`, `publish_batch(...)` wire-shape methods.\n- `client.records` — `get(tx_hash)` to read a record by transaction hash.\n- `client.inbox` — sealed-PoE discovery for a recipient.\n- `client.account` — `balance()` and account-scoped reads.\n\nAll client failures raise typed errors inheriting from `Label309HttpError`:\n`RateLimitedError`, `InsufficientFundsError`, `QuoteExpiredError`,\n`QuoteAlreadyConsumedError`, `QuoteNotFoundError`, `FxStaleError`,\n`IdempotencyConflictError`, `UnauthenticatedError`, `InsufficientScopeError`,\n`RecordNotFoundError`, `MalformedCborError`, `InvalidBodyError`,\n`PartialUploadError`, and others. `InvalidClientConfigError` is raised eagerly if\n`base_url` is missing.\n\n### Wire format (`cardanowall.poe_standard`)\n\n- `encode_poe_record(record)` / `encode_record_body_for_signing(record)` —\n  canonical-CBOR encoders.\n- `validate(bytes)` (re-exported as `validate_poe_record`) — the structural\n  validator; `ValidateOk` / `ValidateFail` / `ValidationIssue` / `ErrorCode` /\n  `SEVERITY`.\n- `PoeRecord` and the schema TypedDicts (`Item`, `Slot`, `EncryptionEnvelope`,\n  `MerkleCommit`, …).\n- `chunk_bytes` / `chunk_text` / `bytes_chunk_array_concat` /\n  `reconstruct_chunked_uri` — the metadata-label-309 chunk codec.\n\n### Primitives, identity, and signing\n\n- `cardanowall.hash` — `sha2_256`, `blake2b_256`, `dual_hash`, `dual_hash_stream`.\n- `cardanowall.merkle` — `merkle_sha2_256_root`,\n  `merkle_sha2_256_inclusion_proof`, `merkle_sha2_256_verify_inclusion`,\n  `encode_leaves_list` / `decode_leaves_list`.\n- Sealed PoE — `ecies_sealed_poe_wrap` / `ecies_sealed_poe_unwrap`.\n- Recipients — `encode_age_x25519_recipient`, `encode_age_xwing_recipient`,\n  `parse_age_recipient`.\n- Seed derivation — `derive_ed25519_keypair_from_seed`,\n  `derive_x25519_keypair_from_seed`, `derive_mlkem768x25519_keypair_from_seed`.\n- Seed identity — `derive_keys_from_seed`, `recipients_from_seed`,\n  `signer_from_seed`, `recipient_secret_keys_from_seed`,\n  `decrypt_sealed_from_seed`.\n- Webhooks — `verify_webhook_signature`, `build_webhook_signature_header`,\n  `sign_webhook_payload`.\n- Stable identifiers — `encode_prefixed_id` / `decode_prefixed_id`,\n  Crockford base32 codecs, and the ID prefix/pattern constants.\n\nSee `src/cardanowall/__init__.py` for the exhaustive `__all__`.\n\n### Raw-seed identity, end to end\n\nA developer holding a 32-byte seed can sign, address recipients, and decrypt\nwithout any account envelope. The hybrid post-quantum KEM (X-Wing,\nML-KEM-768 + X25519) is exposed alongside the classical X25519 path.\n\n```python\nfrom cardanowall import (\n    derive_keys_from_seed,\n    recipients_from_seed,\n    signer_from_seed,\n)\n\nseed = b\"\\xff\" * 32\nkeys = derive_keys_from_seed(seed)            # ed25519 / x25519 / mlkem768x25519 keypairs\nrecipients = recipients_from_seed(seed)       # {\"age\": \"age1...\", \"age1pqc\": \"age1pqc...\"}\nsigner = signer_from_seed(seed)               # a path-1 Signer for client.poe.publish_*\n```\n\n### Off-host signing\n\nIf you build the canonical-CBOR record yourself and sign on a separate host\n(KMS, HSM, an air-gapped machine), `build_to_sign(record)` produces the exact\nbytes to sign; the SDK never needs the private key.\n\n```python\nfrom cardanowall import build_to_sign\nfrom cardanowall.poe_standard import PoeRecord\n\nrecord: PoeRecord = {\"v\": 1, \"items\": [{\"hashes\": {\"sha2-256\": digest}}]}\nto_sign = build_to_sign(record)   # hand these bytes to your external signer\n```\n\n## Cross-implementation parity\n\n`cardanowall-sdk` is a **byte-identical parity twin** of `@cardanowall/sdk-ts`\nand the `cardanowall` Rust crate. The canonical-CBOR encoder, the structural\nvalidator and its error codes, the COSE_Sign1 signing input, the sealed-PoE\nenvelope, the Merkle leaves-list codec, and the seed-derived recipient strings\nare all pinned to the **same shared known-answer vectors** mirrored byte-for-byte\nacross all three languages. This guarantees that:\n\n- a record encoded in Python produces the exact bytes that go on chain in TS/Rust;\n- a record that validates here validates identically everywhere (same verdict,\n  same error codes);\n- an `age1...` / `age1pqc...` recipient string derived from a seed is identical\n  across SDKs, so cross-SDK senders and recipients interoperate.\n\n## Standard and service independence\n\nThe verifier proves a PoE from three inputs only: the **transaction metadata**,\noptionally the **content bytes**, and a **public blockchain explorer**. No issuer\nserver is required at any step. The default deny-host list explicitly blocks\nsingle-implementer domains during verification, and the conformance suite runs\nwith that list active — so a record is provably verifiable without the gateway\nthat published it.\n\nA bundled conformance runner verifies a transaction from the command line:\n\n```sh\ncardanowall-sdk-conformance \u003ctx-hash\u003e\n# or:\npython -m cardanowall.conformance \u003ctx-hash\u003e\n```\n\nExit codes: `0` valid, `1` failed (integrity), `2` failed (network), `3` pending,\n`4` CLI input error.\n\n## Relation to the other packages\n\n- **`@cardanowall/crypto-core`** — closed-catalogue cryptographic primitives\n  (hash, KDF, signature, KEM, AEAD, CBOR, COSE, sealed-PoE, discovery, Merkle,\n  recipient encoding, seed derivation). The portable building blocks.\n- **`@cardanowall/poe-standard`** — the Label 309 wire-format library: record\n  schema, canonical-CBOR encoder, pure structural validator, and error-code\n  catalogue.\n- **`@cardanowall/sdk-ts`** — the browser + Node TypeScript SDK; the reference\n  this package mirrors.\n- **`cardanowall-sdk` (this package)** — the Python SDK; the byte-parity twin.\n- **`cardanowall` (Rust crate)** — the Rust SDK; the byte-parity twin in Rust.\n  (The `cardanowall` CLI binary is a separate crate built on it.)\n\n## License\n\nApache-2.0 — see [LICENSE](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcardanowall%2Flabel-309-py","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcardanowall%2Flabel-309-py","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcardanowall%2Flabel-309-py/lists"}