Skip to main content

spiffile (Python)

Python implementation of the spiffile profile — SPIFFE identities delivered as files. See the project README for the why.

Install

pip install spiffile

Only dependency: PyJWT[crypto].

Configure

Point a service at its identity material with environment variables:

SPIFFILE_ID_FILE=/identity/id               # my SPIFFE ID
SPIFFILE_KEY_FILE=/identity/key.pem         # my private key
SPIFFILE_BUNDLE_FILE=/identity/bundle.json  # everyone's public keys
# — or the single-directory shorthand —
SPIFFILE_DIR=/identity                      # containing id, key.pem, bundle.json

Both the bundle and the private key are hot-reloaded on change, so key rotation and revocation propagate without restarts.

Use

Load once at startup

from spiffile import Identity

identity = Identity.from_env()
# or explicitly:
identity = Identity.from_files("id", "key.pem", "bundle.json")

Outbound — prove who you are

Mint a short-lived token per request, audience-bound to the one service you're calling:

import httpx

token = identity.token(audience="spiffe://example.org/billing")  # ~60s TTL
httpx.post(url, headers={"Authorization": f"Bearer {token}"})

Inbound — know who's calling

from spiffile import InvalidTokenError, UnknownIdentityError

try:
    caller = identity.verify(token)   # audience defaults to my own ID
except (InvalidTokenError, UnknownIdentityError):
    ...  # 401

caller.id      # SpiffeId of the verified peer
caller.claims  # full verified JWT claims

Authorization stays yours — compare caller.id against whatever policy the route demands.

FastAPI sketch

from fastapi import Depends, HTTPException, Request

def require_caller(*allowed: str):
    def dependency(request: Request):
        auth = request.headers.get("Authorization", "")
        if not auth.startswith("Bearer "):
            raise HTTPException(401)
        try:
            caller = identity.verify(auth.removeprefix("Bearer "))
        except (InvalidTokenError, UnknownIdentityError):
            raise HTTPException(401)
        if str(caller.id) not in allowed:
            raise HTTPException(403)
        return caller
    return dependency

router = APIRouter(
    prefix="/management",
    dependencies=[Depends(require_caller("spiffe://example.org/control-plane"))],
)

Provisioning (spiffile.provision)

Building blocks for producers — your dev tooling, scripts, or operator stay thin glue:

from spiffile.provision import (
    init_root, add_service, rotate_service, remove_service, service_env,
)

root = init_root("/tmp/identity", trust_domain="example.org")
add_service(root, "orders")          # keypair + bundle entry (idempotent)
add_service(root, "billing")

service_env(root, "orders")
# {"SPIFFILE_ID_FILE": ".../services/orders/id",
#  "SPIFFILE_KEY_FILE": ".../services/orders/key.pem",
#  "SPIFFILE_BUNDLE_FILE": ".../bundle.json"}

rotate_service(root, "orders")                  # new key, old kept for overlap
rotate_service(root, "orders", keep_old=False)  # new key, old revoked
remove_service(root, "orders")                  # full revocation

The resulting layout:

<root>/
  bundle.json                # distribute to every service
  services/<name>/id         # the service's SPIFFE ID
  services/<name>/key.pem    # deliver only to that service (0600)

In production you'd typically not use these helpers on a box — generate keys into your secrets store and let your delivery machinery (secrets operator, mounted Secrets) place the same three files. The consumer code never knows the difference.

Errors

All exceptions derive from spiffile.SpiffileError:

  • InvalidSpiffeIdError — malformed SPIFFE ID
  • InvalidBundleError — malformed/incompatible bundle document
  • UnknownIdentityError — claimed caller has no keys in the bundle
  • InvalidTokenError — signature/audience/expiry/claims failure

Development

cd python
uv sync
uv run pytest
uv run ruff check src tests && uv run ruff format --check src tests

Metadata

Release files for spiffile 0.1.0

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

Source distribution (sdist)

Source distribution for spiffile 0.1.0
File Size Uploaded Provenance
spiffile-0.1.0.tar.gz 36.9 kB With provenance Details

Built distribution (wheel)

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

Total release size: 53.8 kB

Release files / spiffile-0.1.0.tar.gz

Download URL spiffile-0.1.0.tar.gz
Size 36.9 kB
Tags Source
SHA-256 checksum
How to use checksums
14bd40929d6bf7e6dba53d789f823d9f745251bf37fedea6dd492093a50a3c05
BLAKE2b-256 checksum
How to use checksums
ece15a8b14622aa2720728328958a866db0a16fa1aa368d7869a742bd7237cb7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 8, 2026.

Transparency log

Release files / spiffile-0.1.0-py3-none-any.whl

Download URL spiffile-0.1.0-py3-none-any.whl
Size 16.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f162cba3ec53a9963f7c8d2dfd1a9eec7d14d1d84bb4f3407f712d1f924cf51f
BLAKE2b-256 checksum
How to use checksums
c3d38597a8000266a56994ba346d7b3223aaf74588d86a59927ffdd417f686f5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 8, 2026.

Transparency log

Security for spiffile 0.1.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

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for spiffile-0.1.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 Jun 8, 2026.

Transparency log

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