Note: Previously known as DataCrunch Go SDK. We're transitioning to Verda.com. Same functionality, new name.
Go SDK for the Verda cloud platform. Manage GPU instances, volumes, SSH keys, and more.
Requirements: Go 1.21+ (needs generics support)
go get github.com/verda-cloud/verdacloud-sdk-gopackage main
import (
"context"
"fmt"
"log"
"os"
"github.com/verda-cloud/verdacloud-sdk-go/pkg/verda"
)
func main() {
client, err := verda.NewClient(
verda.WithClientID(os.Getenv("VERDA_CLIENT_ID")),
verda.WithClientSecret(os.Getenv("VERDA_CLIENT_SECRET")),
)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
instances, err := client.Instances.Get(ctx, "")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d instances\n", len(instances))
}Set your credentials:
export VERDA_CLIENT_ID="your_client_id"
export VERDA_CLIENT_SECRET="your_client_secret"All API calls go through generic request functions that enforce type safety at compile time:
func getRequest[T any](ctx context.Context, client *Client, url string) (T, *Response, error)
func postRequest[T any](ctx context.Context, client *Client, url string, reqBody any) (T, *Response, error)
func deleteRequest[T any](ctx context.Context, client *Client, url string) (T, *Response, error)Service methods like client.Instances.Get() and client.SSHKeys.Create() use these internally. This means:
- Compile-time type checking
- Consistent middleware application (auth, retries, logging)
- Thread-safe request isolation
- Easy to extend for new endpoints
Add custom behavior to all requests:
client, _ := verda.NewClient(
verda.WithClientID("your_client_id"),
verda.WithClientSecret("your_client_secret"),
)
// Log all requests
client.Middleware.AddRequestMiddleware(func(next verda.RequestHandler) verda.RequestHandler {
return func(ctx *verda.RequestContext) error {
log.Printf("Request: %s %s", ctx.Method, ctx.Path)
return next(ctx)
}
})Default middleware includes: authentication, JSON content-type, exponential backoff retries, and error handling.
ctx := context.Background()
// List instances
instances, err := client.Instances.Get(ctx, "")
// Get specific instance
instance, err := client.Instances.GetByID(ctx, "instance_id")
// Create instance
newInstance, err := client.Instances.Create(ctx, verda.CreateInstanceRequest{
InstanceType: "1V100.6V",
Image: "ubuntu-24.04-cuda-12.8-open-docker",
Hostname: "my-gpu-box",
SSHKeyIDs: []string{"ssh_key_id"},
LocationCode: verda.LocationFIN03,
})
// Control instances
err = client.Instances.Shutdown(ctx, "instance_id")
err = client.Instances.Hibernate(ctx, "instance_id")
err = client.Instances.Delete(ctx, "instance_id", nil)
// Check availability
available, err := client.Instances.IsAvailable(ctx, "1V100.6V", false, "")// List keys
keys, err := client.SSHKeys.Get(ctx)
// Create key
newKey, err := client.SSHKeys.Create(ctx, verda.CreateSSHKeyRequest{
Name: "my-key",
PublicKey: "ssh-rsa AAAAB3NzaC1yc2E...",
})
// Delete key
err = client.SSHKeys.Delete(ctx, "key_id")// List volumes
volumes, err := client.Volumes.Get(ctx)
// Get specific volume
volume, err := client.Volumes.GetByID(ctx, "volume_id")Instances, volumes and clusters can carry up to 10 key-value tags each. Keys are lowercased by the API, and a tag with no value is a "freeform" tag.
// Tag a resource at creation time
instance, err := client.Instances.Create(ctx, verda.CreateInstanceRequest{
InstanceType: "1V100.6V",
Image: "ubuntu-24.04-cuda-12.8-open-docker",
Hostname: "my-instance",
Description: "Tagged instance",
Tags: []verda.TagRequest{
{Key: "environment", Value: "production"},
{Key: "benchmark"}, // freeform tag, no value
},
})
// Add a tag to an existing resource
tag, err := client.Instances.AddTag(ctx, instance.ID, verda.TagRequest{
Key: "team",
Value: "platform",
})
// The same methods exist on volumes and clusters
_, err = client.Volumes.AddTag(ctx, volumeID, verda.TagRequest{Key: "environment", Value: "test"})
_, err = client.Clusters.AddTag(ctx, clusterID, verda.TagRequest{Key: "environment", Value: "test"})
// Remove a tag by key
err = client.Instances.DeleteTag(ctx, instance.ID, "team")Tags are returned on the resource itself:
instance, err := client.Instances.GetByID(ctx, instanceID)
for _, tag := range instance.Tags {
fmt.Printf("%s=%s (%s)\n", tag.Key, tag.Value, tag.ID)
}There is no update endpoint. Adding a key that already exists returns a *verda.APIError with
StatusCode 409, so changing a value means deleting the tag first:
if _, err := client.Instances.AddTag(ctx, instanceID, tagReq); err != nil {
var apiErr *verda.APIError
if errors.As(err, &apiErr) && apiErr.StatusCode == http.StatusConflict {
if err := client.Instances.DeleteTag(ctx, instanceID, tagReq.Key); err != nil {
return err
}
_, err = client.Instances.AddTag(ctx, instanceID, tagReq)
}
return err
}Experimental: Private network support is experimental. The API and the SDK types for it may change in backwards-incompatible ways.
ctx := context.Background()
// Create a network, then a subnet in a location
network, err := client.PrivateNetworks.Create(ctx, verda.CreatePrivateNetworkRequest{
Name: "prod-net",
Mode: verda.NetworkModeCustom, // or verda.NetworkModeAuto: subnets per location on first use
})
subnet, err := client.PrivateNetworks.CreateSubnet(ctx, network.ID, verda.CreateSubnetRequest{
LocationCode: "FIN-01",
Name: "workers",
CIDR: "10.0.0.0/24",
})
// Route the subnet's internet traffic through an instance (must have a public IP)
route, err := client.PrivateNetworks.CreateRoute(ctx, network.ID, subnet.ID, verda.CreateRouteRequest{
Destination: "0.0.0.0/0",
InstanceID: instanceID,
})
// Deploy an instance onto the network
private, public := verda.IPAllocAuto, verda.IPAllocAuto
instance, err := client.Instances.Create(ctx, verda.CreateInstanceRequest{
InstanceType: "CPU.4V.16G",
Image: "ubuntu-24.04",
Hostname: "vpc-worker",
Description: "worker on prod-net",
LocationCode: "FIN-01",
Network: network.ID, // or Subnet: subnet.ID
PrivateIP: &private, // IPAllocAuto, IPAllocNone, or verda.IPAllocIPv4("10.0.0.5")
PublicIP: &public, // must be set together with PrivateIP
})private_ip and public_ip must be set together: auto allocates an address, none means
none (a public none requires a private address), or pass a literal IPv4 your project holds.
Omitting both fields gives the platform default. Instance responses
expose PrivateIP, PrivateNetworkID and SubnetID (nil for classic instances).
// Account balance
balance, err := client.Balance.Get(ctx)
// Locations
locations, err := client.Locations.Get(ctx)
// Startup scripts
scripts, err := client.StartupScripts.Get(ctx)
script, err := client.StartupScripts.Create(ctx, verda.CreateStartupScriptRequest{
Name: "setup",
Script: "#!/bin/bash\necho 'Hello'",
})instances, err := client.Instances.Get(ctx, "")
if err != nil {
if apiErr, ok := err.(*verda.APIError); ok {
fmt.Printf("API error %d: %s\n", apiErr.StatusCode, apiErr.Message)
} else {
fmt.Printf("Request failed: %v\n", err)
}
}client, err := verda.NewClient(
verda.WithClientID("your_client_id"),
verda.WithClientSecret("your_client_secret"),
verda.WithBaseURL("https://api.verda.com/v1"), // optional
verda.WithAuthBearerToken("token"), // optional, skip OAuth
verda.WithDebugLogging(true), // optional, enable logging
verda.WithLogger(customLogger), // optional, custom logger
)Optional and disabled by default. There are two ways to enable detailed debug logging:
client, _ := verda.NewClient(
verda.WithDebugLogging(true),
verda.WithClientID("your_client_id"),
verda.WithClientSecret("your_client_secret"),
)
// Add detailed debug logging for all requests/responses
verda.AddDetailedDebugLogging(client)export VERDA_DEBUG=true
go run main.goBoth methods log:
- HTTP method, path, query params
- Request/response headers (Authorization redacted)
- Request/response JSON body (truncated at 1000 chars)
- Status codes and errors
- Token refresh calls are automatically skipped
Note: WithDebugLogging(true) enables basic request timing logs. Use AddDetailedDebugLogging() for full request/response payloads including JSON bodies.
Useful for debugging API issues. In production, leave it off.
# Production (default)
export VERDA_CLIENT_ID="your_client_id"
export VERDA_CLIENT_SECRET="your_client_secret"
# Staging
export VERDA_BASE_URL="https://api-staging.verda.com/v1"
# Debug mode
export VERDA_DEBUG=trueImplement the Logger interface to use your own logging library:
type Logger interface {
Debug(msg string, args ...interface{})
Info(msg string, args ...interface{})
Warn(msg string, args ...interface{})
Error(msg string, args ...interface{})
}Built-in options:
NoOpLogger(default) - no logging, zero overheadStdLogger- uses Go's standard librarySlogLogger- structured logging support
slogLogger := verda.NewSlogLogger(true)
client, _ := verda.NewClient(
verda.WithClientID("your_client_id"),
verda.WithClientSecret("your_client_secret"),
verda.WithLogger(slogLogger),
)No credentials needed, uses mocks:
make test
# With coverage report
make coverage
open build/coverage.htmlWarning: Runs against real API, may incur costs.
export VERDA_CLIENT_ID="your_client_id"
export VERDA_CLIENT_SECRET="your_client_secret"
make test-integration# Setup
make setup # Install tools and configure hooks
# Testing
make test # Run unit tests
make test-integration # Run integration tests
make test-smoke # Run smoke tests (quick CRUD lifecycle)
# Code Quality
make check # Run format, lint, and tests
make lint # Static analysis with golangci-lint
make fmt # Format code
# Maintenance
make clean # Clean build artifacts
make mod-tidy # Clean up dependenciesgit clone https://github.com/your-username/verdacloud-sdk-go.git
cd verdacloud-sdk-go
make setupThis installs golangci-lint and sets up pre-commit hooks. If you don't have pre-commit, install it:
- macOS:
brew install pre-commit - Linux/Windows:
pip install pre-commit
Pre-commit hooks run automatically before each commit to ensure code quality. By default, hooks run locally (no Docker required).
# Normal commit - runs checks locally
git commit -m "your message"
# Use Docker for checks (if needed)
PRE_COMMIT_USE_DOCKER=1 git commit -m "your message"
# Skip hooks temporarily (not recommended)
git commit --no-verify -m "your message"The hooks check:
- Code formatting (gofmt, goimports)
- Linting (golangci-lint)
- Security checks (gosec, govulncheck)
- Unit tests
git checkout -b feature/your-feature
# make changes
git commit -m "feat: add feature"
git push origin feature/your-featurePre-commit hooks automatically:
- Format code with
gofmtandgoimports - Run
golangci-lint - Run unit tests
- Tidy
go.mod
- Add tests for new features (maintain >80% coverage)
- Update docs as needed
- Use conventional commits (
feat:,fix:,docs:) - All PRs must pass CI checks
See .github/CONTRIBUTING.md for details.
Check the example/ directory for complete examples:
make exampleThis repository includes AI-readable documentation to help any AI assistant quickly understand the codebase:
| File | Purpose |
|---|---|
AGENTS.md |
Entry point for AI agents — key files, conventions, quick reference |
.ai/SKILL.md |
Full codebase guide — directory structure, architecture, patterns |
.ai/architecture.md |
Deep dive — request flow, struct layouts, middleware, auth, errors |
Works with Cursor, Copilot, Codex, Claude Code, and any tool that reads repository context files.
MIT License - see LICENSE for details.
- Website: verda.com
- API Docs: docs.verda.com
- Support: support@verda.com
Previously known as DataCrunch. Same team, same service, new name.