Skip to main content

SDF (Smart Document Format) — producer, reader, validator, signer

Project description

sdf-python

SDF (Smart Document Format) Python reference implementation — producer, reader, validator, signer.

License: BUSL-1.1

SDF is an open file format that combines a human-readable PDF layer with a machine-readable JSON layer in a single file. etapsky-sdf is the Python reference implementation of the SDF specification.


Installation

pip install etapsky-sdf

Or from source (monorepo):

cd packages/sdf-python
pip install -e ".[dev]"

Requirements: Python 3.11+, jsonschema, reportlab, cryptography


Quick start

Produce a .sdf file

from sdf import build_sdf

schema = {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "required": ["document_type", "invoice_number"],
    "properties": {
        "document_type": {"type": "string"},
        "invoice_number": {"type": "string"},
        "total": {
            "type": "object",
            "properties": {
                "amount": {"type": "string"},
                "currency": {"type": "string"},
            },
        },
    },
}

data = {
    "document_type": "invoice",
    "invoice_number": "INV-2026-001",
    "total": {"amount": "1250.00", "currency": "EUR"},
}

buffer = build_sdf(
    data,
    schema,
    issuer="Acme Supplies GmbH",
    issuer_id="DE123456789",
    document_type="invoice",
    recipient="Global Logistics AG",
)

with open("invoice.sdf", "wb") as f:
    f.write(buffer)

Read and validate a .sdf file

from sdf import parse_sdf

with open("invoice.sdf", "rb") as f:
    buffer = f.read()

result = parse_sdf(buffer)

print(result.meta.document_id)    # UUID v4
print(result.meta.sdf_version)   # '0.1'
print(result.data["invoice_number"])  # 'INV-2026-001'
# result.pdf_bytes — serve to a PDF viewer for human review

Validate schema directly

from sdf import validate_schema, validate_schema_or_throw

result = validate_schema(data, schema)
if not result.valid:
    print(result.errors)
else:
    print("Valid")

# Or raise on invalid:
validate_schema_or_throw(data, schema)

Sign and verify

from sdf import (
    generate_key_pair,
    export_public_key,
    export_private_key,
    import_public_key,
    import_private_key,
    sign_sdf,
    verify_sig,
)

# Generate ECDSA P-256 key pair
priv, pub = generate_key_pair("ECDSA")

# Export / import (Base64 SPKI / PKCS#8)
pub_b64 = export_public_key(pub)
priv_b64 = export_private_key(priv)
pub = import_public_key(pub_b64)
priv = import_private_key(priv_b64)

# Sign
signed = sign_sdf(buffer, priv, include_pdf=True)

# Verify
ok = verify_sig(signed, pub)
assert ok

API

Producer — sdf.build_sdf

build_sdf(data, schema, issuer, *, issuer_id=None, document_type=None, recipient=None, recipient_id=None, schema_id=None, tags=None) -> bytes

Produces a .sdf file buffer. Validates data against schema before producing — raises SDFValidationError on failure.

Reader — sdf.parse_sdf

parse_sdf(buffer: bytes | bytearray) -> SDFParseResult

Reads and validates a .sdf buffer. Returns SDFParseResult(meta, data, schema, pdf_bytes).

Validator — sdf.validate_*

  • validate_meta(meta) -> SDFMeta — Validates meta.json against normative schema
  • validate_schema(data, schema) -> SDFValidationResult — Returns {valid, errors}
  • validate_schema_or_throw(data, schema) — Raises SDFValidationError on failure

Signer — sdf.signer

  • generate_key_pair(algorithm="ECDSA") — ECDSA P-256 or RSA 2048
  • export_public_key, export_private_key, import_public_key, import_private_key
  • sign_sdf(buffer, private_key, *, include_pdf=True, algorithm="ECDSA") -> bytes
  • verify_sig(buffer, public_key, *, include_pdf=None) -> bool

Error handling

All errors extend SDFError with a code field:

from sdf import parse_sdf, SDFError, SDF_ERRORS

try:
    result = parse_sdf(buffer)
except SDFError as e:
    if e.code == SDF_ERRORS["NOT_ZIP"]:
        ...
    elif e.code == SDF_ERRORS["SCHEMA_MISMATCH"]:
        ...

Examples

cd packages/sdf-python
python examples/produce_invoice.py   # creates invoice.sdf
python examples/read_invoice.py [path.sdf]
python examples/sign_and_verify.py

Tests

cd packages/sdf-python
pip install -e ".[dev]"
pytest

What is SDF?

A .sdf file is a ZIP archive containing:

invoice.sdf
├── visual.pdf      ← human-readable layer (any PDF viewer)
├── data.json       ← machine-readable layer
├── schema.json     ← validation rules
├── meta.json       ← identity and provenance
└── signature.sig   ← digital signature (optional)

License

BUSL-1.1 — Copyright (c) 2026 Yunus YILDIZ

See LICENSE and the root LICENSE for terms.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

etapsky_sdf-0.1.0.tar.gz (13.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

etapsky_sdf-0.1.0-py3-none-any.whl (15.4 kB view details)

Uploaded Python 3

File details

Details for the file etapsky_sdf-0.1.0.tar.gz.

File metadata

  • Download URL: etapsky_sdf-0.1.0.tar.gz
  • Upload date:
  • Size: 13.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.2

File hashes

Hashes for etapsky_sdf-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9fcb678ae4e8d13329bac6c5756e84dbd940d40bb9bd86837a376fde9673d674
MD5 001e64cec6f35ad17385480033f45889
BLAKE2b-256 052c3a846d52a112305bff332d3fc3a2ea070b75489017d7508ba79882ef42bb

See more details on using hashes here.

File details

Details for the file etapsky_sdf-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: etapsky_sdf-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 15.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.2

File hashes

Hashes for etapsky_sdf-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fc393544ce12ef54128aef734df8617f6c4e4e744027348f334df81c615f5fcc
MD5 5865951418ee2a10b1ab66b1a9d7b1e6
BLAKE2b-256 334511c7b0dcde33ffdc0d1c3ac2a81919c28cfcc43b9a53e963752e9069c42d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page