Skip to main content

citesig

CiteSig is an open protocol for embedding cryptographic verification signatures inside factual claims — a trust layer for the AI era.

citesig is the reference Python implementation of CiteSig v0.1. It provides sign(), verify(), canonical serialization, and the compact form defined in the specification.

Install

pip install citesig

Requires Python 3.9 or later. Depends on cryptography for Ed25519 primitives.

Quick example

import os
from datetime import datetime, timezone

from citesig import (
    sign,
    verify,
    ed25519,
    did_key_from_ed25519_public_key,
)

# Generate a signing key (32-byte seed).
seed = os.urandom(32)
public_key = ed25519.public_key_from_seed(seed)
signer = did_key_from_ed25519_public_key(public_key)

# Sign a claim.
attestation = sign(
    {
        "claim": "The Great Barrier Reef is approximately 2,300 km long.",
        "signer": signer,
        "sources": [
            {"url": "https://barrierreef.org/the-reef/facts"},
        ],
        "issued_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
    },
    seed,
)

# Verify it.
result = verify(attestation)
if result.ok:
    print("Signature verified.")
else:
    print(f"Rejected: {result.reason} — {result.detail}")

API

sign(claim, private_key_seed) -> dict

Produces a signed CiteSig attestation.

  • claim — partial attestation mapping. claim, signer, sources, issued_at are required; v defaults to citesig/0.1. Extension fields with names containing a colon (e.g. myapp:field) are included in the signed content per spec §3.2.
  • private_key_seed — 32-byte Ed25519 seed.
  • Returns — a new dict with all input fields plus a populated sig (base64url-encoded 64-byte Ed25519 signature).

verify(attestation, *, resolve_signer=None) -> VerifyResult

Verifies a CiteSig attestation per spec §5.3.

  • attestation — a signed attestation mapping.
  • resolve_signer — optional callable (signer: str) -> Iterable[bytes]. Called for https:// signers. Not called for did:key: signers, which resolve offline.
  • Returns — a VerifyResult(ok, reason=None, detail=None). On failure, reason is one of:
    • version-unknown — v is not citesig/0.1
    • field-missing — a REQUIRED field is absent
    • field-malformed — a field has the wrong type or shape
    • signature-invalid — signature does not match the signed content
    • signer-unresolvable — signer identifier could not be resolved to a public key

Constants live on RejectionReason (e.g. RejectionReason.SIGNATURE_INVALID).

Use verify_async(...) if you need an async resolve_signer.

canonicalize(value) -> bytes

Returns the JCS (RFC 8785) canonicalization of value as UTF-8 bytes. This is the signing input format per spec §4.

to_compact(attestation) -> str

Encodes a signed attestation as its compact form per spec §6:

citesig:0.1:<b64u(claim)>:<b64u(signer)>:<b64u(sourcedigest)>:<b64u(issued_at)>:<b64u(sig)>

where sourcedigest is SHA-256(JCS(sources)). Compact form is lossy for sources — recipients can verify the signature but must obtain the full attestation to enumerate sources.

parse_compact(compact) -> CompactAttestation

Parses a compact-form string into a CompactAttestation dataclass with v, claim, signer, source_digest (32 bytes), issued_at, sig. The original sources list cannot be recovered from a compact-form string.

Utility exports

  • ed25519.public_key_from_seed(seed) -> bytes — derive the public key from a 32-byte seed.
  • ed25519.sign(message, seed) -> bytes — raw Ed25519 signing (64-byte signature).
  • ed25519.verify(message, signature, public_key) -> bool — raw Ed25519 verification.
  • ed25519_public_key_from_did_key(did) -> bytes — decode a did:key:z... to its 32-byte public key.
  • did_key_from_ed25519_public_key(public_key) -> str — encode a 32-byte public key as did:key:z....
  • base64url.encode(bytes) -> str / base64url.decode(str) -> bytes — RFC 4648 §5 (no padding).

Conformance

This implementation passes every v0.1 conformance vector, which ship in the source distribution:

  • 20 canonicalization vectors (tests/vectors/v0.1/canonical.json)
  • 17 signing vectors, 6 accept and 11 reject — one for every rejection reason in spec §5.3 (tests/vectors/v0.1/signatures.json)

The signing vectors use a published, deliberately trivial test seed (the bytes 0x00 through 0x1f). It is not a key and must never be used as one.

Run the test suite from the source distribution:

pip download citesig==0.1.1 --no-binary :all: --no-deps
tar xzf citesig-0.1.1.tar.gz && cd citesig-0.1.1
pip install -e ".[test]"
pytest

The JavaScript reference implementation (@citesig/core on npm) and this Python implementation are byte-for-byte compatible — signatures produced by one verify with the other.

Specification

Changes in 0.1.1

  • Number canonicalization now follows RFC 8785 exactly (ECMA-262 Number::toString): whole-valued floats, exponent thresholds, exponent padding and negative zero now serialize byte-identically to the JavaScript implementation. Integers outside the exactly representable double range are rejected rather than rounded.
  • sign() now fills in the default version when v is passed explicitly as None, matching the JavaScript implementation. Previously this produced an attestation nothing could verify.
  • The conformance vectors now ship with the source distribution.
  • Package metadata points to https://citesig.org/spec/.

License

MIT for code and test vectors (this package). CiteSig is maintained by Machine Accountability Corporation.

Security

A valid CiteSig signature does not mean the claim is true. It means the signer identified by signer produced the attestation, and the claim + sources + timestamp + extension fields have not been altered since. Consumers building trust decisions on top of CiteSig must layer signer reputation, source quality, and independent verification on top.

Report vulnerabilities and other issues to security@deepinquiry.ai.

Metadata

Release files for citesig 0.1.1

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

Source distribution (sdist)

Source distribution for citesig 0.1.1
File Size Uploaded Provenance
citesig-0.1.1.tar.gz 20.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for citesig 0.1.1
File Interpreter ABI Platform
citesig-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 35.2 kB

Release files / citesig-0.1.1.tar.gz

Download URL citesig-0.1.1.tar.gz
Size 20.1 kB
Tags Source
SHA-256 checksum
How to use checksums
676a951c26ab91471fcb3d6b8d979141a294206d3ff3df8e64ebaac4b3ad7b3f
BLAKE2b-256 checksum
How to use checksums
44156ca9d3d15a471a2ed966c314dda62c48f1bb8007b594cad3a75bea6c0b51
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release files / citesig-0.1.1-py3-none-any.whl

Download URL citesig-0.1.1-py3-none-any.whl
Size 15.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
eaffa70cfa86e747c879be2a1fc76c5e778cf7b9c9999c7b014e3edb39532c36
BLAKE2b-256 checksum
How to use checksums
2f4e78d1cf6442bf40ae5070f2b5cc7bf4c0a05c7f08357901f0e4c770967984
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Security for citesig 0.1.1

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

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