SPIFFE identities delivered as files — JWT-SVIDs and trust bundles without agents
Project description
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file spiffile-0.0.1.tar.gz.
File metadata
- Download URL: spiffile-0.0.1.tar.gz
- Upload date:
- Size: 36.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a32d4de5c2a50c4b6b1488e658025f3a63abf7f05c98fe464a187253e6138b4
|
|
| MD5 |
fccdc5d549d1adf88e4cce510570f464
|
|
| BLAKE2b-256 |
9c56161f13f7f8df309cc267981cf884ec18b6dd51d90d1009bb85eaf74a12bb
|
Provenance
The following attestation bundles were made for spiffile-0.0.1.tar.gz:
Publisher:
publish.yml on PeterSR/spiffile
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spiffile-0.0.1.tar.gz -
Subject digest:
2a32d4de5c2a50c4b6b1488e658025f3a63abf7f05c98fe464a187253e6138b4 - Sigstore transparency entry: 1740438458
- Sigstore integration time:
-
Permalink:
PeterSR/spiffile@51f2ffc536f57da75e7c125286aadfe7a0fe230d -
Branch / Tag:
refs/tags/python-v0.0.1 - Owner: https://github.com/PeterSR
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@51f2ffc536f57da75e7c125286aadfe7a0fe230d -
Trigger Event:
push
-
Statement type:
File details
Details for the file spiffile-0.0.1-py3-none-any.whl.
File metadata
- Download URL: spiffile-0.0.1-py3-none-any.whl
- Upload date:
- Size: 16.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
19a062a959abc68ad9b4c4fea2dd3980a6c3d29e144997de8c4da1c7dc0460ec
|
|
| MD5 |
fcf1041eca5cf062a71620001bbad64f
|
|
| BLAKE2b-256 |
09185664b70a8a0b105a6a4494febca0cdbeda4a36de5017ab1b6e8511fe4395
|
Provenance
The following attestation bundles were made for spiffile-0.0.1-py3-none-any.whl:
Publisher:
publish.yml on PeterSR/spiffile
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spiffile-0.0.1-py3-none-any.whl -
Subject digest:
19a062a959abc68ad9b4c4fea2dd3980a6c3d29e144997de8c4da1c7dc0460ec - Sigstore transparency entry: 1740438464
- Sigstore integration time:
-
Permalink:
PeterSR/spiffile@51f2ffc536f57da75e7c125286aadfe7a0fe230d -
Branch / Tag:
refs/tags/python-v0.0.1 - Owner: https://github.com/PeterSR
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@51f2ffc536f57da75e7c125286aadfe7a0fe230d -
Trigger Event:
push
-
Statement type: