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:

  1. Explicit path — if the argument starts with / or contains identity.json, treat it as a file path
  2. Vault ID — looks up ~/.sikkerkey/vaults/{vaultId}/identity.json
  3. Environment variable — reads SIKKERKEY_IDENTITY for a path to identity.json
  4. 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)
    }
}
StatusError containsMeaning
401authentication failedInvalid signature or unknown machine
403access deniedMachine not approved, disabled, or no grant
404not foundSecret does not exist
409conflictInvalid operation
429rate limitedToo many requests (retried automatically)
503server sealedServer needs unseal (retried automatically)

Properties

MethodReturnsDescription
MachineID()stringMachine UUID
MachineName()stringHostname from bootstrap
VaultID()stringVault this identity belongs to
APIURL()stringSikkerKey API URL

Method Reference

MethodReturnsDescription
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()[]stringList 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

VariableDescription
SIKKERKEY_IDENTITYPath to identity.json — overrides vault lookup
SIKKERKEY_HOMEBase 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.