Skip to main content
Version: Next

Go SDK

Roadmap feature

The SyVault Go SDK is in alpha (v0.1.0) and not yet published to proxy.golang.org. The module path (github.com/syvault/sdk-go) will be published when the SDK reaches a stable release. Track progress in sdks/go/.

The official SyVault Go SDK provides idiomatic Go access to the Secrets Manager API. It handles ECDSA JWT signing, secret decryption, and automatic token refresh.

Installation

go get github.com/syvault/sdk-go

Requires Go 1.21 or later.

Authentication

package main

import (
"context"
"fmt"
"log"
"os"

syvault "github.com/syvault/sdk-go"
)

func main() {
client, err := syvault.NewClient(
syvault.WithServer("https://vault.example.com"),
syvault.WithClientID("c9a1b2d3-4e5f-6789-abcd-ef0123456789"),
syvault.WithClientSecret(os.Getenv("SY_CLIENT_SECRET")),
)
if err != nil {
log.Fatal(err)
}
defer client.Close()
}

Fetching Secrets

List all secrets

ctx := context.Background()
secrets, err := client.Secrets.List(ctx)
if err != nil {
log.Fatal(err)
}

for _, s := range secrets {
fmt.Printf("%s %s\n", s.UID, s.Title)
}

Get a full secret

secret, err := client.Secrets.Get(ctx, "7Kj9mNpQ2xRs")
if err != nil {
log.Fatal(err)
}

fmt.Println(secret.Fields["host"]) // "db.example.com"
fmt.Println(secret.Fields["password"]) // "s3cret!"

Resolve a notation

password, err := client.Secrets.Notation(ctx, "sy://Production/Database/field/password")
if err != nil {
log.Fatal(err)
}

fmt.Println(password) // "s3cret!"

Creating Secrets

newSecret, err := client.Secrets.Create(ctx, &syvault.CreateSecretInput{
FolderID: "folder-uuid-here",
Title: "Stripe API Key",
Fields: map[string]string{
"key": "sk_live_abc123",
"environment": "production",
},
})
if err != nil {
log.Fatal(err)
}

fmt.Println(newSecret.UID) // "Xp4qRtY8mNwK"

Rotating Secrets

result, err := client.Secrets.Rotate(ctx, "7Kj9mNpQ2xRs")
if err != nil {
log.Fatal(err)
}

fmt.Println(result.Status) // "completed"
fmt.Println(result.NewFields["password"])

Error Handling

The SDK provides typed errors for inspection:

import "errors"

secret, err := client.Secrets.Get(ctx, "nonexistent")
if err != nil {
var notFound *syvault.SecretNotFoundError
var authErr *syvault.AuthError

switch {
case errors.As(err, &notFound):
fmt.Printf("Secret %s does not exist\n", notFound.UID)
case errors.As(err, &authErr):
fmt.Println("Authentication failed — check client credentials")
default:
log.Fatal(err)
}
}

Caching

The SDK caches decrypted secrets in memory by default. Configure the cache at construction time:

client, err := syvault.NewClient(
syvault.WithServer("https://vault.example.com"),
syvault.WithClientID("..."),
syvault.WithClientSecret("..."),
syvault.WithCacheTTL(5 * time.Minute),
syvault.WithCacheMax(500),
)

Disable caching:

client, err := syvault.NewClient(
// ...
syvault.WithCacheTTL(0),
)

HTTP Server Example

Inject database credentials into an HTTP server at startup:

package main

import (
"context"
"database/sql"
"fmt"
"log"
"net/http"
"os"

_ "github.com/lib/pq"
syvault "github.com/syvault/sdk-go"
)

func main() {
ctx := context.Background()

sy, err := syvault.NewClient(
syvault.WithServer(os.Getenv("SY_SERVER")),
syvault.WithClientID(os.Getenv("SY_CLIENT_ID")),
syvault.WithClientSecret(os.Getenv("SY_CLIENT_SECRET")),
)
if err != nil {
log.Fatal(err)
}
defer sy.Close()

secret, err := sy.Secrets.Get(ctx, "7Kj9mNpQ2xRs")
if err != nil {
log.Fatal(err)
}

dsn := fmt.Sprintf(
"host=%s port=%s user=%s password=%s dbname=%s sslmode=require",
secret.Fields["host"],
secret.Fields["port"],
secret.Fields["username"],
secret.Fields["password"],
secret.Fields["database"],
)

db, err := sql.Open("postgres", dsn)
if err != nil {
log.Fatal(err)
}
defer db.Close()

http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
if err := db.PingContext(r.Context()); err != nil {
http.Error(w, "db down", 500)
return
}
w.Write([]byte(`{"ok":true}`))
})

log.Println("Listening on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}

Thread Safety

The Client is safe for concurrent use from multiple goroutines. The internal HTTP client, token refresh, and cache are all protected by appropriate synchronization. Create a single client at application startup and share it.