> ## Documentation Index
> Fetch the complete documentation index at: https://docs.westyx.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Go SDK

> Official Go SDK for Westyx Nexus - minimal dependencies, thread-safe.

Official Go SDK for [Westyx Nexus](https://westyx.dev) - the centralised secrets, feature flags, and config service.

```go theme={null}
import (
    "context"
    nexus "gitlab.com/westyx/nexus/sdk/go"
)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

client, err := nexus.NewClient(ctx, nexus.Config{
    BaseURL: "https://blue-ocean-a5rx7.westyx.dev",
    APIKey:  "wxs_...",
})
if err != nil {
    log.Fatal(err)
}

dbURL, ok := client.GetConfig("database.url")
stripeKey, _ := client.GetSecret("stripe.key")
enabled := client.GetFlag("new.checkout", false)
```

## Highlights

* **Go 1.26+** - single module, **minimal dependencies** (stdlib + `aws-sdk-go-v2` config/signer, used only by the `aws_iam` WIF provider)
* **Thread-safe TTL cache** with atomic snapshot replacement and ETag/304 support
* **Background refresh** - caller goroutines are never blocked on cache expiry
* **SSE live updates** - started automatically by `NewClient`; propagates remote changes within milliseconds; falls back to TTL polling after 3 transport errors
* **Public-key vs secret-key access control** - public-key clients get `ErrPublicKeyRestricted` from `GetSecret`
* **Sentinel errors** - `errors.Is(err, nexus.ErrUnauthorized)` style, idiomatic Go
* **Workload Identity Federation** *(v0.2.0)* - Kubernetes / AWS IRSA / GCP / Azure auto-detected; bearer-JWT auth with automatic refresh; **AWS IAM (`aws_iam`)** for ECS/Fargate/Lambda/plain-EC2 *(v0.9.0)*
* **Stream observer hooks** *(v0.2.0)* - opt-in structured callbacks for ops dashboards and load-test tooling
* **Service kind awareness** *(v0.2.0)* - `GetSecret` blocked with `ErrServiceKindMismatch` on `kind=frontend`
* **402 Payment Required handling** *(v0.2.0)* - `ErrBilling` returned, background sync halted, cache served
* **AB Testing batch evaluation** *(v0.3.0)* - `EvaluateAB(ctx, keys, userID, attributes)` for the AB Testing add-on; `ErrABAddonNotAvailable` on 403
* **File-type secrets** *(v0.3.0)* - `SecretTypeFile` materialised to `os.TempDir()/nexus-<key>-<hash>`; `GetSecretFilePath(key)` exposes the path; `Close()` cleans up

## What's new in v0.10.1

* **Config reads are deep-copied** - `GetConfig` and `GetAllConfigs` return a deep copy of object/array config values, so mutating a returned nested `map`/slice can no longer corrupt the shared cache or race a background refresh. Scalars are unaffected.
* **Clean shutdown** - `Close` cancels all background work (TTL refresh, WIF session refresh) and waits for it to finish before removing `file`-secret temp files; a closed client never starts a new background sync, and no orphaned secret files are left on disk.
* **Lint enforced in CI** - committed `.golangci.yml` plus a pinned `golangci-lint` stage covering the root and `openfeature/` modules.

## What's new in v0.10.0

* Version alignment across the Westyx Nexus SDK suite.

## What's new in v0.9.0

* **`aws_iam` WIF provider (AWS IAM Caller Identity)** - authenticates non-EKS AWS compute (**ECS/Fargate, Lambda, plain EC2**) that has IAM credentials but no OIDC token. The SDK SigV4-signs an STS `GetCallerIdentity` request (never sent to AWS) and posts it to `/v1/auth/token-exchange`; Nexus replays it against a pinned STS endpoint to prove your IAM role. The signed `X-Nexus-Server-ID` is your service's own `BaseURL` host - a captured request is valid for that one service only, and there is nothing to configure. See [Workload identity](workload-identity#aws-iam-non-eks-aws-compute).
* **Azure Workload Identity (AKS)** - the `azure` provider now prefers the projected federated token file (`$AZURE_FEDERATED_TOKEN_FILE`) before falling back to IMDS; auto-detection probes the file too.
* **Security hardening** - `NewClient` rejects plain-http `BaseURL`s (loopback excepted); the Azure IMDS path refuses the generic default audience (must be `api://<client-id>`); the token-exchange response read is bounded; AWS auto-detection stats the IRSA token file instead of trusting the env var.
* **WIF test suite** - per-provider token-source tests, auto-detect dispatch, the Azure audience guard, and the signed `aws_iam` payload shape.

## What's new in v0.8.0

* **OpenFeature provider** - new `openfeature/` sub-module (`gitlab.com/westyx/nexus/sdk/go/openfeature`). Wraps an initialized `*nexus.Client` and implements the OpenFeature `FeatureProvider` interface. `EvaluationContext` is ignored - Nexus has no per-user targeting. See [OpenFeature integration](openfeature).

## What's new in v0.5.1

* **Security improvements** - exception messages contain only status codes; `file`-type secret paths are fully hashed so key names are never visible on the filesystem.
* **`Close()` cancels the stream** - `Close()` now stops the `RunStream` goroutine started by `NewClient`; no need to cancel the parent context separately.
* **CI improvements** - tests run on merge requests; publish is restricted to `main`-branch tags.

## What's new in v0.5.0

* **Write API** - `SetSecret`, `DeleteSecret`, `DeleteSecretVersion` for programmatic secret management. secret keys only; public keys get `ErrPublicKeyRestricted` immediately without a network call.
* **`ErrRateLimited`** - new sentinel for HTTP 429 on write endpoints.

## What's new in v0.4.0

* **Path prefix update** - API endpoints moved from `/api/v1/` to `/v1/`; update your `BaseURL` from `nexus.westyx.dev` to `<slug>.westyx.dev`.
* **Quarantine handling** *(v0.4.0)* - new `429` quarantine response pauses sync until expiry. `OnQuarantined(reason, expiresAt)` observer callback added.
* **`Retry-After`-aware SSE 429 handling** - `RunStream` now sleeps the indicated delay (clamped `[5 s, 5 min]`) and reconnects automatically when the server returns `429 Too Many Requests` with a parseable `Retry-After` header. The reconnect does NOT count toward the transport-failure threshold.

## Module

The module is served directly via `proxy.golang.org` - there is no separate package registry.

```sh theme={null}
go get gitlab.com/westyx/nexus/sdk/go@v0.10.1
```

Latest release: **v0.10.1** *(2026-07-10 - deep-copy config reads, clean shutdown / no orphaned secret files, lint-clean CI)*.

## Quick start

```go theme={null}
package main

import (
    "context"
    "log"
    "os"
    "os/signal"
    "syscall"
    nexus "gitlab.com/westyx/nexus/sdk/go"
)

func main() {
    ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
    defer cancel()

    client, err := nexus.NewClient(ctx, nexus.Config{
        BaseURL: os.Getenv("NEXUS_URL"),
        APIKey:  os.Getenv("NEXUS_API_KEY"),
    })
    if err != nil {
        log.Fatalf("nexus: init failed: %v", err)
    }
    defer client.Close()

    dbURL, ok := client.GetConfig("database.url")
    if !ok {
        log.Fatal("nexus: missing database.url config")
    }
    log.Printf("db url: %v", dbURL)

    stripe, err := client.GetSecret("stripe.key")
    if err != nil {
        log.Fatalf("nexus: missing stripe.key secret: %v", err)
    }
    log.Printf("stripe key: %d bytes", len(stripe))

    newUI := client.GetFlag("new.ui", false)
    log.Printf("new UI enabled: %v", newUI)

    // block until signal
    <-ctx.Done()
}
```

## Pages

| Page                                   | Description                                                    |
| -------------------------------------- | -------------------------------------------------------------- |
| [Installation](installation)           | `go get` flow, module path, GOPRIVATE                          |
| [Configuration](configuration)         | `Config` field reference, TTL guidance, HTTP client split      |
| [API reference](api-reference)         | Every public function and method                               |
| [Error handling](error-handling)       | Sentinel errors, failure scenarios                             |
| [SSE live updates](sse-live-updates)   | Auto-start, reconnection, fallback, 429 handling               |
| [Stream observer](stream-observer)     | Opt-in structured callbacks                                    |
| [Workload identity](workload-identity) | Bearer-JWT auth with K8s / AWS / GCP / Azure                   |
| [Caching behaviour](caching-behaviour) | When does the SDK hit the network                              |
| [OpenFeature integration](openfeature) | OpenFeature provider - wrapping the client in the OF ecosystem |
