Python SDK
Read secrets from any Python application using Ed25519 machine authentication.
Installation
pip install sikkerkey
Requires Python 3.10+. Single dependency: cryptography (for Ed25519 signing).
Quick Start
from sikkerkey import SikkerKey
sk = SikkerKey("vault_abc123")
secret = sk.get_secret("sk_a1b2c3d4e5")
The SDK loads the machine identity from ~/.sikkerkey/vaults/{vaultId}/identity.json, signs every request with the machine's Ed25519 private key, and returns the decrypted value.
Client Creation
# Explicit vault ID
sk = SikkerKey("vault_abc123")
# Direct path to identity file
sk = SikkerKey("/etc/sikkerkey/vaults/vault_abc123/identity.json")
# Auto-detect from SIKKERKEY_IDENTITY env or single vault on disk
sk = SikkerKey()
Raises ConfigurationError if the identity is missing, the key can't be loaded, or multiple vaults exist without a specified vault ID.
Reading Secrets
Single Value
api_key = sk.get_secret("sk_stripe_prod")
Structured (Multiple Fields)
db = sk.get_fields("sk_db_prod")
host = db["host"] # "db.example.com"
user = db["username"] # "admin"
password = db["password"] # "hunter2"
Raises SecretStructureError if the secret value is not a JSON object.
Single Field
password = sk.get_field("sk_db_prod", "password")
Raises FieldNotFoundError if the field doesn't exist. The error message includes available field names.
Listing Secrets
# All secrets this machine can access
secrets = sk.list_secrets()
for s in secrets:
print(f"{s.id}: {s.name}")
# Secrets in a specific project
project_secrets = sk.list_secrets_by_project("proj_abc123")
Returns list[SecretListItem] with attributes id, name, field_names (optional), and project_id (optional).
Export
# All secrets as a flat dict
env = sk.export()
# {"API_KEY": "sk-live-...", "DB_CREDS_HOST": "db.example.com", "DB_CREDS_PASSWORD": "s3cret"}
# Scoped to a project
env = sk.export(project_id="proj_abc123")
# Inject into environment
import os
os.environ.update(sk.export())
Structured secrets are flattened: SECRET_NAME_FIELD_NAME.
Watching for Changes
Watch secrets for real-time updates. When a secret is rotated, updated, or deleted, the callback fires with the new value.
from sikkerkey import WatchStatus
def on_db_change(event):
if event.status == WatchStatus.CHANGED:
# event.value has the new value
# event.fields has parsed key-value pairs for structured secrets
db.configure_credentials(
username=event.fields["username"],
password=event.fields["password"],
)
elif event.status == WatchStatus.DELETED:
print("Secret deleted")
elif event.status == WatchStatus.ACCESS_DENIED:
print("Access revoked")
elif event.status == WatchStatus.ERROR:
print(f"Error: {event.error}")
sk.watch("sk_db_credentials", on_db_change)
Polling starts automatically on the first watch() call and runs on a background daemon thread. Default interval is 15 seconds (server enforces a 10-second minimum).
sk.set_poll_interval(30) # seconds
sk.unwatch("sk_db_credentials") # stop watching one secret
sk.close() # stop all watches
SikkerKey can be used as a context manager for automatic cleanup.
Multi-Vault
prod = SikkerKey("vault_a1b2c3d4e5f6g7h8")
staging = SikkerKey("vault_x9y8z7w6v5u4t3s2")
prod_db = prod.get_secret("sk_db_prod")
staging_db = staging.get_secret("sk_db_staging")
List Registered Vaults
vaults = SikkerKey.list_vaults()
# ["vault_a1b2c3d4e5f6g7h8", "vault_x9y8z7w6v5u4t3s2"]
Static method on the SikkerKey class.
Identity Resolution
- Explicit path - starts with
/or containsidentity.json - Vault ID - looks up
~/.sikkerkey/vaults/{vaultId}/identity.json SIKKERKEY_IDENTITYenv - path to identity file- Auto-detect - single vault on disk
The vault_ prefix is added automatically if not present. Override base directory with SIKKERKEY_HOME.
Error Handling
from sikkerkey import SikkerKey, NotFoundError, AccessDeniedError, AuthenticationError
try:
secret = sk.get_secret("sk_nonexistent")
except NotFoundError:
# 404 - secret doesn't exist
except AccessDeniedError:
# 403 - machine not approved or no grant
except AuthenticationError:
# 401 - invalid signature or unknown machine
except RateLimitedError:
# 429 - too many requests (retried automatically)
except ServerSealedError:
# 503 - server needs unseal (retried automatically)
except ConfigurationError:
# identity file missing, key not found
except ApiError as e:
# any other HTTP error
print(e.http_status)
Exception Hierarchy
SikkerKeyError
├── ConfigurationError - identity/key issues
├── SecretStructureError - secret is not a JSON object (get_fields)
├── FieldNotFoundError - field not in structured secret (get_field)
└── ApiError - HTTP error (has http_status: int)
├── AuthenticationError - 401
├── AccessDeniedError - 403
├── NotFoundError - 404
├── ConflictError - 409
├── RateLimitedError - 429
└── ServerSealedError - 503
Properties
| Property | Type | Description |
|---|---|---|
machine_id | str | Machine UUID |
machine_name | str | Hostname from bootstrap |
vault_id | str | Vault this identity belongs to |
api_url | str | SikkerKey API URL |
Read-only @property decorators.
Method Reference
| Method | Returns | Description |
|---|---|---|
SikkerKey(vault_or_path?) | SikkerKey | Create client |
SikkerKey.list_vaults() | list[str] | List registered vault IDs (static) |
get_secret(secret_id) | str | Read a secret value |
get_fields(secret_id) | dict[str, str] | Read structured secret |
get_field(secret_id, field) | str | Read single field |
list_secrets() | list[SecretListItem] | List all accessible secrets |
list_secrets_by_project(project_id) | list[SecretListItem] | List secrets in a project |
export(project_id?) | dict[str, str] | Export as env map |
watch(secret_id, callback) | None | Watch a secret for changes |
unwatch(secret_id) | None | Stop watching a secret |
set_poll_interval(seconds) | None | Set poll interval (min 10s) |
close() | None | Stop all watches, shut down polling |
Retry Behavior
429 and 503 responses are retried up to 3 times with exponential backoff (1s, 2s, 4s). Each retry uses a fresh timestamp and nonce. Network errors are also retried.
Environment Variables
| Variable | Description |
|---|---|
SIKKERKEY_IDENTITY | Path to identity.json - overrides vault lookup |
SIKKERKEY_HOME | Base config directory (default: ~/.sikkerkey) |
Dependencies
| Dependency | Version | Purpose |
|---|---|---|
cryptography | >=41.0 | Ed25519 key loading and signing |
All other functionality uses Python stdlib: urllib.request, json, hashlib, secrets. Requires Python 3.10+. All HTTP requests have a 15-second timeout.