Clients

Go client

The Go client is what your application uses to get its secrets: it loads a vault and hands your code the secrets you ask for.

Install it

Requires Go 1.26 or later.

go get go.valt0.com/valt0

Open a vault

A vault is opened by its id; the name is a label you choose.

valt0.OpenVault loads a vault from the local service. Pass it the id that valt0 info prints for the vault. The service can only open a vault that has been imported into it. A vault you create is imported for you; for one made somewhere else, run valt0 import first.

It returns an error if the service cannot be reached or the vault cannot be loaded.

Go
import "go.valt0.com/valt0"

vault, err := valt0.OpenVault(valt0.VaultOptions{
    Id:   "your-vault-id",
    Name: "your-vault-name",
})
if err != nil {
    log.Fatal(err)
}

Read a secret

Ask for a secret by name, use it, then destroy it.

vault.Secret(name) fetches one secret and returns a *Secret. Take the value out as bytes where you can: Bytes() hands back the secret's own buffer, and Destroy() zeroes it, so defer Destroy() as soon as you have the secret.

If you need the secret as a string use Reveal().

Go
secret, err := vault.Secret("DATABASE_PASSWORD")
if err != nil {
    log.Fatal(err)
}
// Zero the underlying buffer as soon as you are finished
defer secret.Destroy()

// Preferred: raw bytes, wiped by Destroy()
pw := secret.Bytes()

// Or, when a string is unavoidable
db.Connect(secret.Reveal())

Unless you ask for its value, a Secret prints as [REDACTED]. It implements fmt.Stringer, fmt.GoStringer, json.Marshaler, encoding.TextMarshaler and slog.LogValuer, so a fmt call, a JSON or text payload and a log line all get the redacted form. The value comes out only through Reveal() or Bytes().

Go
fmt.Println(secret)                         // [REDACTED]
fmt.Printf("%#v\n", secret)                 // [REDACTED]
json.Marshal(map[string]any{"pw": secret})  // {"pw":"[REDACTED]"}
slog.Info("connecting", "pw", secret)       // ... pw=[REDACTED]

secret.Reveal()                             // the actual value

Handle errors

The two ways a connection fails have errors of their own.

OpenVault returns valt0.ErrCantConnectToService when it cannot find the service, which usually means the service is not running: valt0 status tells you whether it is. It returns valt0.ErrNoServiceResponse when the service did not answer within the one second the client waits.

Check for either with errors.Is. Any other error, such as a vault that could not be loaded, comes back as it is.

Go
vault, err := valt0.OpenVault(opts)
switch {
case errors.Is(err, valt0.ErrCantConnectToService):
    // the service was not found: is it running?
case errors.Is(err, valt0.ErrNoServiceResponse):
    // the service did not answer within the transport timeout (1s)
case err != nil:
    // any other error, e.g. the vault could not be loaded
}

API

OpenVault(opts): (Vault, error)

Opens the vault identified by Id in opts, a valt0.VaultOptions. Returns an error if the service cannot be reached or the vault cannot be loaded; the two connection failures are ErrCantConnectToService and ErrNoServiceResponse.

Options

Idstring
The vault's id, the one valt0 info prints. It is the only value used to find the vault.
Namestring
A label for the vault, used in messages.

Vault

The opened vault.

Methods

Id()
The vault's id.
Name()
The vault's name.
Secret(name)(*Secret, error)
Fetches one secret by name.

Secret

One secret, fetched from a vault.

Methods

Bytes()
The raw bytes: the secret's own buffer, not a copy. Preferable to use where you can, because Destroy() wipes it.
Reveal()string
The value as a string. A string cannot be wiped, so use it sparingly.
Destroy()
Zeroes the buffer.