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 IDInvalidBundleError— malformed/incompatible bundle documentUnknownIdentityError— claimed caller has no keys in the bundleInvalidTokenError— 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)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| spiffile-0.1.0.tar.gz | 36.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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 logSecurity 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.
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 logA 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.