← Back to Docs

Go Go SDK

Idiomatic Go package with context support and concurrent-safe operations

Context Support

All operations accept context for cancellation and timeouts

Concurrent Safe

Thread-safe operations with proper locking

Zero Dependencies

Standard library only for core functionality

Idiomatic API

Follows Go conventions and best practices

Installation

go get github.com/avp-protocol/avp-go

Quick Start

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/avp-protocol/avp-go"
)

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

    // Create client with file backend
    backend, err := avp.NewFileBackend("./vault.enc", "your-password")
    if err != nil {
        log.Fatal(err)
    }

    client := avp.NewClient(backend)

    // Authenticate to workspace
    session, err := client.Authenticate(ctx, "my-agent", nil)
    if err != nil {
        log.Fatal(err)
    }

    // Store a secret
    err = client.Store(ctx, session.ID, "OPENAI_API_KEY", []byte("sk-..."), nil)
    if err != nil {
        log.Fatal(err)
    }

    // Retrieve a secret
    secret, err := client.Retrieve(ctx, session.ID, "OPENAI_API_KEY", nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Key: %s\n", string(secret.Value))
}

Backends

MemoryBackend

In-memory storage for testing.

import "github.com/avp-protocol/avp-go"

backend := avp.NewMemoryBackend()
client := avp.NewClient(backend)

FileBackend

Encrypted file storage using AES-256-GCM.

backend, err := avp.NewFileBackend("./vault.enc", "password")
if err != nil {
    log.Fatal(err)
}

client := avp.NewClient(backend)

KeychainBackend

OS-native credential storage.

backend, err := avp.NewKeychainBackend("my-app-avp")
if err != nil {
    log.Fatal(err)
}

client := avp.NewClient(backend)

Operations

Discover

info, err := client.Discover(ctx)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Version: %s\n", info.Version)
fmt.Printf("Backends: %v\n", info.Backends)
fmt.Printf("Operations: %v\n", info.Operations)

Authenticate

import "time"

// Default TTL (1 hour)
session, err := client.Authenticate(ctx, "workspace", nil)

// Custom TTL
ttl := 2 * time.Hour
session, err := client.Authenticate(ctx, "workspace", &avp.AuthOptions{
    TTL: &ttl,
})

fmt.Printf("Session ID: %s\n", session.ID)
fmt.Printf("Expires: %v\n", session.ExpiresAt)

Store

// Simple store
err := client.Store(ctx, session.ID, "API_KEY", []byte("sk-..."), nil)

// With options
err := client.Store(ctx, session.ID, "API_KEY", []byte("sk-..."), &avp.StoreOptions{
    Labels: map[string]string{
        "env":      "production",
        "provider": "openai",
    },
    ExpiresAt: time.Now().Add(30 * 24 * time.Hour), // 30 days
})

Retrieve

// Latest version
secret, err := client.Retrieve(ctx, session.ID, "API_KEY", nil)

// Specific version
version := uint32(1)
secret, err := client.Retrieve(ctx, session.ID, "API_KEY", &avp.RetrieveOptions{
    Version: &version,
})

fmt.Printf("Value: %s\n", string(secret.Value))
fmt.Printf("Version: %d\n", secret.Version)
fmt.Printf("Labels: %v\n", secret.Labels)

Delete

err := client.Delete(ctx, session.ID, "OLD_KEY")

List

// List all
secrets, err := client.List(ctx, session.ID, nil)

// With label filter
secrets, err := client.List(ctx, session.ID, &avp.ListOptions{
    LabelFilter: map[string]string{
        "env": "production",
    },
})

for _, s := range secrets {
    fmt.Printf("%s: v%d\n", s.Name, s.Version)
}

Rotate

result, err := client.Rotate(ctx, session.ID, "API_KEY", []byte("sk-new-..."))
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Previous: v%d\n", result.PreviousVersion)
fmt.Printf("New: v%d\n", result.NewVersion)

Error Handling

import "errors"

secret, err := client.Retrieve(ctx, session.ID, "UNKNOWN", nil)
if err != nil {
    var avpErr *avp.Error
    if errors.As(err, &avpErr) {
        switch avpErr.Code {
        case avp.ErrNotFound:
            fmt.Println("Secret not found")
        case avp.ErrSessionExpired:
            fmt.Println("Session expired, re-authenticate")
        case avp.ErrUnauthorized:
            fmt.Println("Not authorized for this workspace")
        default:
            fmt.Printf("AVP Error: %s\n", avpErr.Message)
        }
    } else {
        fmt.Printf("Error: %v\n", err)
    }
}

Context and Timeouts

import (
    "context"
    "time"
)

// With timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

secret, err := client.Retrieve(ctx, session.ID, "API_KEY", nil)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        fmt.Println("Operation timed out")
    }
}

// With cancellation
ctx, cancel := context.WithCancel(context.Background())

go func() {
    // Cancel after some condition
    cancel()
}()

secret, err := client.Retrieve(ctx, session.ID, "API_KEY", nil)

Concurrent Usage

Thread Safety: The AVP client is safe for concurrent use. Multiple goroutines can call methods on the same client simultaneously.
import "sync"

var wg sync.WaitGroup

keys := []string{"KEY1", "KEY2", "KEY3"}

for _, key := range keys {
    wg.Add(1)
    go func(k string) {
        defer wg.Done()
        secret, err := client.Retrieve(ctx, session.ID, k, nil)
        if err != nil {
            log.Printf("Error retrieving %s: %v", k, err)
            return
        }
        fmt.Printf("%s: %s\n", k, string(secret.Value))
    }(key)
}

wg.Wait()

Custom Backend

package main

import (
    "context"
    "time"

    "github.com/avp-protocol/avp-go"
)

type MyBackend struct {
    // Your storage implementation
}

func (b *MyBackend) Discover(ctx context.Context) (*avp.DiscoverInfo, error) {
    // Return capabilities
}

func (b *MyBackend) Authenticate(ctx context.Context, workspace string, opts *avp.AuthOptions) (*avp.Session, error) {
    // Create session
}

func (b *MyBackend) Store(ctx context.Context, sessionID, name string, value []byte, opts *avp.StoreOptions) error {
    // Store secret
}

func (b *MyBackend) Retrieve(ctx context.Context, sessionID, name string, opts *avp.RetrieveOptions) (*avp.Secret, error) {
    // Retrieve secret
}

func (b *MyBackend) Delete(ctx context.Context, sessionID, name string) error {
    // Delete secret
}

func (b *MyBackend) List(ctx context.Context, sessionID string, opts *avp.ListOptions) ([]*avp.SecretMetadata, error) {
    // List secrets
}

func (b *MyBackend) Rotate(ctx context.Context, sessionID, name string, newValue []byte) (*avp.RotateResult, error) {
    // Rotate secret
}

// Verify interface implementation
var _ avp.Backend = (*MyBackend)(nil)

Complete Example

package main

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

    "github.com/avp-protocol/avp-go"
)

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

    // Initialize
    backend, err := avp.NewFileBackend("./agent-vault.enc", os.Getenv("VAULT_PASSWORD"))
    if err != nil {
        log.Fatal(err)
    }

    client := avp.NewClient(backend)

    // Authenticate
    session, err := client.Authenticate(ctx, "openai-agent", nil)
    if err != nil {
        log.Fatal(err)
    }

    // Store with labels
    err = client.Store(ctx, session.ID, "OPENAI_API_KEY", []byte(os.Getenv("OPENAI_API_KEY")), &avp.StoreOptions{
        Labels: map[string]string{
            "provider": "openai",
            "env":      "production",
        },
    })
    if err != nil {
        log.Fatal(err)
    }

    // Retrieve and use
    secret, err := client.Retrieve(ctx, session.ID, "OPENAI_API_KEY", nil)
    if err != nil {
        log.Fatal(err)
    }

    apiKey := string(secret.Value)
    fmt.Printf("Using API key v%d\n", secret.Version)

    // Use apiKey with OpenAI client...
    _ = apiKey

    // Rotate when needed
    result, err := client.Rotate(ctx, session.ID, "OPENAI_API_KEY", []byte("sk-new-..."))
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Rotated from v%d to v%d\n", result.PreviousVersion, result.NewVersion)
}