SDK Overview
Choose an SDK, understand the common API surface, and start reading secrets.
SikkerKey SDKs provide read-only access to secrets from your application. Every SDK authenticates using the same Ed25519 machine identity as the CLI. No API keys, no tokens, no sessions.
All secret management (create, update, rotate, delete) is done through the dashboard. SDKs and the CLI are read-only — they fetch secrets, they don't modify them.
Choose Your SDK
All six SDKs share the same read API and behavior. Pick the one that matches your runtime. All SDKs are open source under the MIT license on GitHub.
Common API
Every SDK exposes the same core operations:
| Operation | What it does |
|---|---|
getSecret(id) | Read a single-value secret |
getFields(id) | Read a structured secret as key-value pairs |
getField(id, field) | Read one field from a structured secret |
listSecrets() | List all secrets this machine can access |
listSecretsByProject(projectId) | List secrets in a specific project |
export(projectId?) | Export all secrets as a flat key-value map |
listVaults() | List vault IDs registered on this machine |
watch(id, callback) | Watch a secret for changes in near real-time |
unwatch(id) | Stop watching a secret |
setPollInterval(seconds) | Set the poll interval (minimum 10 seconds) |
close() | Stop all watches and shut down polling |
Naming conventions vary by language (getSecret in Kotlin/Node/PHP, get_secret in Python, GetSecret in Go/.NET), but the read behavior is identical. The watch methods (watch, unwatch, setPollInterval, close) exist in every SDK except PHP, whose single-threaded model has no background polling; for rotation in PHP, re-read the secret or cache it with a short TTL.
Identity Resolution
The SDK finds the machine's Ed25519 keypair automatically. Resolution order:
- Explicit vault ID —
SikkerKey("vault_abc123")looks up~/.sikkerkey/vaults/vault_abc123/identity.json - Explicit path —
SikkerKey("/path/to/identity.json")uses a file path directly - Environment variable —
SIKKERKEY_IDENTITYpoints to anidentity.jsonfile - Auto-detect — if exactly one vault directory exists, uses it automatically
The base directory (~/.sikkerkey/) can be overridden with the SIKKERKEY_HOME environment variable. The vault_ prefix is added automatically if not present.
Serverless and Ephemeral Hosts
On serverless or read-only-filesystem platforms there's no identity file on disk. Every SDK can instead bootstrap a memory-only identity from an enrollment token: it generates an Ed25519 keypair in memory, registers a short-lived ephemeral machine, and reads secrets, without writing anything to disk. The ephemeral machine lives for the lifetime set on the token, so the common pattern is to read what you need at startup and hold the values. See the "Serverless" section on each SDK page (bootstrapInMemory, or BootstrapInMemoryAsync on .NET).
Authentication
Every request is signed with the machine's Ed25519 private key:
- Build the payload:
{method}:{path}:{timestamp}:{nonce}:{bodyHash} - Sign with the private key
- Send four headers:
X-Machine-Id,X-Timestamp,X-Nonce,X-Signature - Server verifies the signature against the stored public key
No tokens are stored, transmitted, or refreshed. Every request is independently authenticated. HTTPS is enforced for all non-localhost connections.
Retry Behavior
All SDKs automatically retry on:
- 429 Too Many Requests — rate limited
- 503 Service Unavailable — server sealed or temporarily down
Retry schedule: 1s, 2s, 4s (exponential backoff, max 3 retries). Each retry generates a fresh timestamp and nonce. Network errors are also retried.
Error Handling
All SDKs use a typed exception hierarchy:
| Exception | HTTP Status | Meaning |
|---|---|---|
AuthenticationError | 401 | Invalid signature or unknown machine |
AccessDeniedError | 403 | Machine not approved, disabled, or no grant |
NotFoundError | 404 | Secret does not exist |
ConflictError | 409 | Invalid operation |
RateLimitedError | 429 | Too many requests (retried automatically) |
ServerSealedError | 503 | Server needs unseal (retried automatically) |
ConfigurationError | — | Identity file missing, key not found |
SecretStructureError | — | Secret is not a JSON object (getFields) |
FieldNotFoundError | — | Field not in structured secret (getField) |
Exception class names vary by language convention (e.g. NotFoundError in Node.js, NotFoundException in Kotlin, NotFoundError in Go).
Watching for Changes
All SDKs except the PHP SDK can watch secrets for changes in near real-time. When a secret is rotated, updated, deleted, or access is revoked, a callback fires with the new value. Polling happens in the background, so your application is never blocked. (PHP is single-threaded with no background worker, so it omits watch; re-read the secret or use a short-TTL cache for rotation instead.)
// Node.js example -- all SDKs have the same API
sk.watch('sk_db_credentials', (event) => {
if (event.status === 'changed') {
db.reconfigure({
username: event.fields.username,
password: event.fields.password,
})
}
})
The SDK polls the server every 15 seconds (configurable, minimum 10). When a change is detected, the SDK fetches the new value through the normal authenticated path -- every read is audited. Structured secrets are automatically parsed into fields.
Statuses:
| Status | Meaning |
|---|---|
changed | Secret was updated or rotated. New value and parsed fields are provided. |
deleted | Secret was deleted. Watch is automatically removed. |
access_denied | Machine no longer has access. Watch is automatically removed. |
error | Failed to fetch the updated value. Error message provided. |
See individual SDK pages for language-specific examples.
Multi-Vault
A machine registered with multiple vaults uses a separate keypair per vault:
const prod = SikkerKey.create('vault_production')
const staging = SikkerKey.create('vault_staging')
const dbPass = await prod.getSecret('sk_db_prod')
const testKey = await staging.getSecret('sk_test_key')
Each vault is fully isolated — different keypairs, different encryption keys, different access grants.
Export and Environment Injection
All SDKs can export secrets as a flat key-value map:
const env = await sk.export()
// { API_KEY: "sk-live-...", DB_CREDS_HOST: "db.example.com", DB_CREDS_PASSWORD: "s3cret" }
Structured secrets are flattened: a secret named DB Creds with fields host and password becomes DB_CREDS_HOST and DB_CREDS_PASSWORD.
For CLI-based injection, use sikkerkey run -- ./your-app which exports all secrets as environment variables into the child process.
Environment Variables
| Variable | Description |
|---|---|
SIKKERKEY_IDENTITY | Path to identity.json — overrides vault lookup |
SIKKERKEY_HOME | Base config directory (default: ~/.sikkerkey) |
SIKKERKEY_VAULT | Vault ID hint (used by some SDKs for auto-detection) |