Go SDK
Read secrets from any Go application using Ed25519 machine authentication.
Installation
go get github.com/SikkerKeyOfficial/sikkerkey-go@latest
Zero external dependencies. Standard library only.
Quick Start
import sikkerkey "github.com/SikkerKeyOfficial/sikkerkey-go"
sk, err := sikkerkey.New("vault_abc123")
secret, err := sk.GetSecret("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, err := sikkerkey.New("vault_abc123")
// Direct path to identity file
sk, err := sikkerkey.New("/etc/sikkerkey/vaults/vault_abc123/identity.json")
// Auto-detect from SIKKERKEY_IDENTITY env or single vault on disk
sk, err := sikkerkey.NewAutoDetect()
Auto-detection fails with a clear error if multiple vaults are registered and no vault is specified.
Reading Secrets
Single Value
apiKey, err := sk.GetSecret("sk_stripe_prod")
Structured (Multiple Fields)
fields, err := sk.GetFields("sk_db_prod")
host := fields["host"]
password := fields["password"]
GetFields parses the secret value as JSON into map[string]string. Returns an error if the value is not a JSON object.
Single Field
password, err := sk.GetField("sk_db_prod", "password")
Returns an error if the field does not exist in the structured secret.
Listing Secrets
All Secrets
secrets, err := sk.ListSecrets()
for _, s := range secrets {
fmt.Printf("%s: %s\n", s.ID, s.Name)
}
Returns []SecretListItem with fields ID, Name, FieldNames (*string), and ProjectID (*string).
By Project
secrets, err := sk.ListSecretsByProject("proj_abc123")
Export
Export all accessible secrets as a flat key-value map in a single round trip:
env, err := sk.Export("")
// env["API_KEY"] = "sk-live-..."
// env["DB_CREDS_HOST"] = "db.example.com"
// env["DB_CREDS_PASSWORD"] = "s3cret"
Structured secrets are flattened: SECRET_NAME_FIELD_NAME. For example, a secret named db-creds with fields host and password becomes DB_CREDS_HOST and DB_CREDS_PASSWORD.
Scope to a Project
env, err := sk.Export("proj_abc123")
Pass "" to export all projects.
Watching for Changes
Watch secrets for real-time updates. When a secret is rotated, updated, or deleted, the callback fires with the new value.
sk.Watch("sk_db_credentials", func(event sikkerkey.WatchEvent) {
switch event.Status {
case sikkerkey.WatchStatusChanged:
// event.Value has the new value
// event.Fields has parsed key-value pairs for structured secrets
db.Reconfigure(event.Fields["username"], event.Fields["password"])
case sikkerkey.WatchStatusDeleted:
log.Println("Secret deleted")
case sikkerkey.WatchStatusAccessDenied:
log.Println("Access revoked")
case sikkerkey.WatchStatusError:
log.Printf("Error: %s", event.Error)
}
})
Polling starts automatically on the first Watch() call and runs on a background goroutine. Default interval is 15 seconds (server enforces a 10-second minimum).
sk.SetPollInterval(30) // seconds
sk.Unwatch("sk_db_credentials") // stop watching one secret
sk.Close() // stop all watches
Multi-Vault
A machine registered with multiple vaults uses a separate keypair per vault:
prod, _ := sikkerkey.New("vault_a1b2c3d4e5f6g7h8")
staging, _ := sikkerkey.New("vault_x9y8z7w6v5u4t3s2")
prodDb, _ := prod.GetSecret("sk_db_prod")
stagingDb, _ := staging.GetSecret("sk_db_staging")
List Registered Vaults
vaults := sikkerkey.ListVaults()
// ["vault_a1b2c3d4e5f6g7h8", "vault_x9y8z7w6v5u4t3s2"]
ListVaults is a package-level function, not a method on Client.
Identity Resolution
The SDK resolves the identity file in this order:
- Explicit path — if the argument starts with
/or containsidentity.json, treat it as a file path - Vault ID — looks up
~/.sikkerkey/vaults/{vaultId}/identity.json - Environment variable — reads
SIKKERKEY_IDENTITYfor a path toidentity.json - Auto-detect — if exactly one vault directory exists, uses it automatically
The vault ID prefix vault_ is added automatically if not present. The base directory can be overridden with SIKKERKEY_HOME.
Use NewAutoDetect() to skip the vault ID argument and rely on steps 3–4.
Error Handling
All errors are prefixed with sikkerkey:.
secret, err := sk.GetSecret("sk_nonexistent")
if err != nil {
switch {
case strings.Contains(err.Error(), "not found"):
// 404
case strings.Contains(err.Error(), "authentication failed"):
// 401
case strings.Contains(err.Error(), "access denied"):
// 403
case strings.Contains(err.Error(), "rate limited"):
// 429 (auto-retried first)
case strings.Contains(err.Error(), "server sealed"):
// 503 (auto-retried first)
}
}
| Status | Error contains | Meaning |
|---|---|---|
| 401 | authentication failed | Invalid signature or unknown machine |
| 403 | access denied | Machine not approved, disabled, or no grant |
| 404 | not found | Secret does not exist |
| 409 | conflict | Invalid operation |
| 429 | rate limited | Too many requests (retried automatically) |
| 503 | server sealed | Server needs unseal (retried automatically) |
Properties
| Method | Returns | Description |
|---|---|---|
MachineID() | string | Machine UUID |
MachineName() | string | Hostname from bootstrap |
VaultID() | string | Vault this identity belongs to |
APIURL() | string | SikkerKey API URL |
Method Reference
| Method | Returns | Description |
|---|---|---|
New(vaultOrPath) | (*Client, error) | Create client by vault ID or path |
NewAutoDetect() | (*Client, error) | Create client via auto-detection |
GetSecret(secretID) | (string, error) | Read a secret value |
GetFields(secretID) | (map[string]string, error) | Read structured secret as key-value pairs |
GetField(secretID, field) | (string, error) | Read single field from structured secret |
ListSecrets() | ([]SecretListItem, error) | List all accessible secrets |
ListSecretsByProject(projectID) | ([]SecretListItem, error) | List secrets in a project |
Export(projectID) | (map[string]string, error) | Export secrets as env-var map |
ListVaults() | []string | List registered vault IDs (package function) |
Watch(secretID, callback) | — | Watch a secret for changes |
Unwatch(secretID) | — | Stop watching a secret |
SetPollInterval(seconds) | — | Set poll interval (min 10s) |
Close() | — | Stop all watches, shut down polling |
Retry Behavior
The SDK retries 429 and 503 responses up to 3 times with exponential backoff (1s, 2s, 4s). Each retry uses a fresh timestamp and nonce for replay protection. 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
None. Standard library only: crypto/ed25519, crypto/sha256, crypto/x509, encoding/json, net/http, crypto/rand.
Requires Go 1.22+. All HTTP requests have a 15-second timeout.