Skip to main content

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 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

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

spiffile-0.0.2.tar.gz (36.4 kB view details)

Uploaded Source

Built Distribution

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

spiffile-0.0.2-py3-none-any.whl (16.6 kB view details)

Uploaded Python 3

File details

Details for the file spiffile-0.0.2.tar.gz.

File metadata

  • Download URL: spiffile-0.0.2.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

Hashes for spiffile-0.0.2.tar.gz
Algorithm Hash digest
SHA256 22ea332595abb68b3e1805ca15d2b676f0e0300a6128a47d58347faf88ebd4ea
MD5 4879e4c8ba148e78ab57d20ee3adbd1e
BLAKE2b-256 3e5ea543ed0ec008df1fd0a5ddd7bd2a7bae817726fc64195f63fded920ea6da

See more details on using hashes here.

Provenance

The following attestation bundles were made for spiffile-0.0.2.tar.gz:

Publisher: publish.yml on PeterSR/spiffile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file spiffile-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: spiffile-0.0.2-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

Hashes for spiffile-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 70bf15f82ad0268a2599b4b025fa83dc2f1b27bfecf80209ab6e57fc588d90d1
MD5 87a3e99b4348536c20f37d886ac98cfc
BLAKE2b-256 5b6b755c98faead824da2df0cd9b0006f044da12f138ed6e30c2817588e1f806

See more details on using hashes here.

Provenance

The following attestation bundles were made for spiffile-0.0.2-py3-none-any.whl:

Publisher: publish.yml on PeterSR/spiffile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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