teamvault

package module
v4.13.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 3, 2026 License: BSD-2-Clause Imports: 19 Imported by: 0

README

Teamvault Utils

Go Reference CI Go Report Card

A Go library and CLI tools for interacting with TeamVault secret management system. Provides type-safe access to passwords, usernames, URLs, and files stored in TeamVault, with support for template parsing and configuration generation.

Features

  • Type-Safe API: Strongly typed interfaces for accessing TeamVault secrets
  • Multiple Connectors: Remote, cache, disk fallback, and dummy connectors
  • Template Parsing: Parse configuration templates with TeamVault placeholders
  • Config Generation: Generate configuration files from templates
  • CLI Tools: Command-line utilities for quick secret access
  • Dependency Injection: Clean architecture with testable components


On macOS, store your TeamVault password in the login Keychain so it never needs to appear in a plaintext config file:

  1. Create a config file with only url and user — leave out pass:

    {
        "url": "https://teamvault.example.com",
        "user": "my-user"
    }
    
  2. Run teamvault-login once to verify your credentials and store the password in the Keychain:

    teamvault-login --teamvault-config ~/.teamvault.json
    

    The command prompts for your TeamVault password (hidden), verifies it against the API, and writes it to the macOS login Keychain on success.

Multi-vault setup: repeat for each config file:

teamvault-login --teamvault-config ~/.teamvault.json
teamvault-login --teamvault-config ~/.teamvault-sm.json

Removing a stored password:

security delete-generic-password -s teamvault-utils -a https://teamvault.example.com

Note: Putting pass directly in the config file still works (the legacy path), but the password is stored in plaintext on disk. The Keychain path is strongly preferred on macOS.

Non-macOS: teamvault-login verifies credentials but does not persist them. Users on Linux/Windows should continue to supply the password via flag, environment variable, or config file for now.


Installation

go get github.com/bborbe/teamvault-utils/v4

Quick Start

package main

import (
    "context"
    "fmt"
    "net/http"

    teamvault "github.com/bborbe/teamvault-utils/v4"
    libtime "github.com/bborbe/time"
)

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

    // Create a connector
    connector := teamvault.NewRemoteConnector(
        http.DefaultClient,
        teamvault.Url("https://teamvault.example.com"),
        teamvault.User("my-user"),
        teamvault.Password("my-pass"),
        libtime.NewCurrentDateTime(),
    )

    // Retrieve a password
    password, err := connector.Password(ctx, teamvault.Key("vLVLbm"))
    if err != nil {
        panic(err)
    }

    fmt.Printf("Password: %s\n", password)
}

Library Usage

Using the Connector Interface

The Connector interface provides access to TeamVault secrets:

import (
    "context"
    "net/http"

    teamvault "github.com/bborbe/teamvault-utils/v4"
    libtime "github.com/bborbe/time"
)

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

    connector := teamvault.NewRemoteConnector(
        http.DefaultClient,
        teamvault.Url("https://teamvault.example.com"),
        teamvault.User("my-user"),
        teamvault.Password("my-pass"),
        libtime.NewCurrentDateTime(),
    )

    // Get password
    password, err := connector.Password(ctx, teamvault.Key("abc123"))
    if err != nil {
        // handle error
    }

    // Get username
    user, err := connector.User(ctx, teamvault.Key("abc123"))
    if err != nil {
        // handle error
    }

    // Get URL
    url, err := connector.Url(ctx, teamvault.Key("abc123"))
    if err != nil {
        // handle error
    }

    // Get file
    file, err := connector.File(ctx, teamvault.Key("abc123"))
    if err != nil {
        // handle error
    }

    // Search for secrets
    keys, err := connector.Search(ctx, "database")
    if err != nil {
        // handle error
    }
}
Template Parsing with ConfigParser

Parse configuration templates containing TeamVault placeholders:

import (
    "context"

    teamvault "github.com/bborbe/teamvault-utils/v4"
)

func parseConfig(connector teamvault.Connector) {
    ctx := context.Background()

    parser := teamvault.NewConfigParser(connector)

    template := []byte(`
database:
  username: {{ "vLVLbm" | teamvaultUser }}
  password: {{ "vLVLbm" | teamvaultPassword }}
  url: {{ "vLVLbm" | teamvaultUrl }}
`)

    result, err := parser.Parse(ctx, template)
    if err != nil {
        // handle error
    }

    // result now contains resolved values
}
Using Different Connector Types

Cache Connector (for performance):

connector := teamvault.NewCacheConnector(
    teamvault.NewRemoteConnector(
        http.DefaultClient,
        teamvault.Url("https://teamvault.example.com"),
        teamvault.User("my-user"),
        teamvault.Password("my-pass"),
        libtime.NewCurrentDateTime(),
    ),
)

Disk Fallback Connector (for reliability):

connector := teamvault.NewDiskFallbackConnector(
    teamvault.NewRemoteConnector(
        http.DefaultClient,
        teamvault.Url("https://teamvault.example.com"),
        teamvault.User("my-user"),
        teamvault.Password("my-pass"),
        libtime.NewCurrentDateTime(),
    ),
)

Dummy Connector (for testing):

connector := teamvault.NewDummyConnector()
Creating Connectors with Factory

Use the factory package for simplified connector creation:

import (
    "context"
    "net/http"

    teamvault "github.com/bborbe/teamvault-utils/v4"
    "github.com/bborbe/teamvault-utils/v4/factory"
    libtime "github.com/bborbe/time"
)

func createConnector() (teamvault.Connector, error) {
    ctx := context.Background()

    httpClient, err := factory.CreateHttpClient(ctx)
    if err != nil {
        return nil, err
    }

    connector, err := factory.CreateConnectorWithConfig(
        ctx,
        httpClient,
        teamvault.TeamvaultConfigPath("~/.teamvault.json"),
        teamvault.Url(""),
        teamvault.User(""),
        teamvault.Password(""),
        teamvault.Staging(false),
        true, // enable cache
        libtime.NewCurrentDateTime(),
    )
    if err != nil {
        return nil, err
    }

    return connector, nil
}

API Documentation

For complete API documentation, visit pkg.go.dev.


CLI Tools

The library includes several command-line tools for quick secret access.

Common flags

All teamvault-* CLI tools accept these flags:

--teamvault-timeout=5s   HTTP request timeout for TeamVault API calls (env: TEAMVAULT_TIMEOUT; default: 5s)
--cache                  Enable disk-fallback cache (env: CACHE)

Cache behavior: Cache is enabled if EITHER the --cache / CACHE env var is true OR the config file's cacheEnabled: true is set. There is no way to force-disable via CLI when the config opts in; edit the config file to disable.

Teamvault Login

Verify TeamVault credentials and store the password in the macOS Keychain. Recommended first step on macOS — see Setup (macOS, recommended) for the full flow.

Install:

go get github.com/bborbe/teamvault-utils/v4/cmd/teamvault-login

Run:

teamvault-login --teamvault-config ~/.teamvault.json

The command prompts for your TeamVault password (hidden input), verifies it against the API, and on success stores it in the macOS login Keychain. On non-macOS platforms it verifies only — no Keychain write.

Teamvault Get Password

Install:

go get github.com/bborbe/teamvault-utils/v4/cmd/teamvault-password

Run:

teamvault-password \
  --teamvault-config ~/.teamvault.json \
  --teamvault-key vLVLbm
Teamvault Get Username

Install:

go get github.com/bborbe/teamvault-utils/v4/cmd/teamvault-username

Run:

teamvault-username \
  --teamvault-config ~/.teamvault.json \
  --teamvault-key vLVLbm
Teamvault Get URL

Install:

go get github.com/bborbe/teamvault-utils/v4/cmd/teamvault-url

Run:

teamvault-url \
  --teamvault-config ~/.teamvault.json \
  --teamvault-key vLVLbm
Parse Config with Teamvault Secrets

Install:

go get github.com/bborbe/teamvault-utils/v4/cmd/teamvault-config-parser

Sample config template:

foo=bar
username={{ "vLVLbm" | teamvaultUser }}
password={{ "vLVLbm" | teamvaultPassword }}
url={{ "vLVLbm" | teamvaultUrl }}

Run:

cat my.config | teamvault-config-parser \
  --teamvault-config ~/.teamvault.json \
  --logtostderr \
  -v=2
Generate Config Directory from Templates

Install:

go get github.com/bborbe/teamvault-utils/v4/cmd/teamvault-config-dir-generator

TeamVault config file (~/.teamvault.json):

{
    "url": "https://teamvault.example.com",
    "user": "my-user",
    "pass": "my-pass",
    "cacheEnabled": true,
    "timeout": "30s"
}

cacheEnabled: true enables disk-fallback caching. timeout sets the HTTP request timeout (default: 5 seconds when absent).

Run:

teamvault-config-dir-generator \
  --teamvault-config ~/.teamvault.json \
  --source-dir templates \
  --target-dir results \
  --logtostderr \
  -v=2

Development

Running Tests
make test
Code Generation (Mocks)
make generate
Full Development Workflow
make precommit  # Format, test, lint, and check

Full Example

Here's a complete, runnable example demonstrating real-world usage patterns combining multiple features:

package main

import (
    "context"
    "fmt"
    "net/http"
    "os"

    teamvault "github.com/bborbe/teamvault-utils/v4"
    libtime "github.com/bborbe/time"
)

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

    // Create a cached connector for better performance
    // The cache connector wraps the remote connector and caches responses
    connector := teamvault.NewCacheConnector(
        teamvault.NewRemoteConnector(
            http.DefaultClient,
            teamvault.Url("https://teamvault.example.com"),
            teamvault.User("my-user"),
            teamvault.Password("my-pass"),
            libtime.NewCurrentDateTime(),
        ),
    )

    // Example 1: Retrieve individual secrets
    password, err := connector.Password(ctx, teamvault.Key("vLVLbm"))
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error getting password: %v\n", err)
        os.Exit(1)
    }
    fmt.Printf("Retrieved password (length: %d)\n", len(password.String()))

    user, err := connector.User(ctx, teamvault.Key("vLVLbm"))
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error getting user: %v\n", err)
        os.Exit(1)
    }
    fmt.Printf("Retrieved user: %s\n", user.String())

    // Example 2: Parse a configuration template
    parser := teamvault.NewConfigParser(connector)

    configTemplate := []byte(`
# Database Configuration
database:
  host: {{ "vLVLbm" | teamvaultUrl }}
  username: {{ "vLVLbm" | teamvaultUser }}
  password: {{ "vLVLbm" | teamvaultPassword }}

# Application Settings
app:
  environment: {{ "production" | env }}
  debug: false
`)

    result, err := parser.Parse(ctx, configTemplate)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error parsing config: %v\n", err)
        os.Exit(1)
    }

    fmt.Println("\nGenerated Configuration:")
    fmt.Println(string(result))

    // Example 3: Generate configuration files from templates
    generator := teamvault.NewConfigGenerator(parser)

    err = generator.Generate(
        ctx,
        teamvault.SourceDirectory("./templates"),
        teamvault.TargetDirectory("./config"),
    )
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error generating configs: %v\n", err)
        os.Exit(1)
    }

    fmt.Println("\nConfiguration files generated successfully!")
}

This example demonstrates:

  • Creating a cached connector for performance optimization
  • Retrieving individual secrets (password, user)
  • Parsing configuration templates with TeamVault placeholders
  • Generating multiple configuration files from a template directory

Testing

Testing code that uses this library is straightforward using the mock connector or dummy connector:

import (
    "context"
    "testing"

    teamvault "github.com/bborbe/teamvault-utils/v4"
    "github.com/bborbe/teamvault-utils/v4/mocks"
)

func TestYourCode(t *testing.T) {
    ctx := context.Background()

    // Use mock connector for testing
    mockConnector := &mocks.Connector{}
    mockConnector.PasswordReturns(teamvault.Password("test-password"), nil)

    // Test your code with the mock
    result, err := mockConnector.Password(ctx, teamvault.Key("test-key"))
    if err != nil {
        t.Fatal(err)
    }

    if result != "test-password" {
        t.Errorf("expected test-password, got %s", result)
    }
}

func TestWithDummyConnector(t *testing.T) {
    // Or use dummy connector for simple tests
    connector := teamvault.NewDummyConnector()

    // Test your code with dummy connector
}

License

This project is licensed under the BSD-style license. See the LICENSE file for details.

Documentation

Overview

Package teamvault provides utilities for accessing and managing TeamVault secrets.

TeamVault is a secret management system, and this package offers Go clients for retrieving passwords, users, URLs, and files from TeamVault instances. It includes various connector implementations for different use cases including remote access, caching, disk fallback, and testing.

The package also provides configuration parsing and generation capabilities to replace TeamVault placeholders in configuration templates with actual secret values.

Index

Constants

View Source
const KeychainServiceName = "teamvault-utils"

KeychainServiceName is the constant service name used for all teamvault-utils Keychain entries. The account key is the TeamVault URL, which keeps multi-vault setups isolated automatically.

Variables

View Source
var ErrKeychainNotSupported = errors.New(
	context.Background(),
	"keychain storage is supported on macOS only in v1",
)

ErrKeychainNotSupported indicates the current platform has no supported credential store backend. Callers may match this with errors.Is to differentiate "no Keychain on this platform" from real Keychain failures.

Functions

func NormalizePath added in v4.6.1

func NormalizePath(path string) (string, error)

NormalizePath converts a path to an absolute path, expanding ~ to the home directory.

Types

type ApiUrl

type ApiUrl string

ApiUrl represents a TeamVault API URL.

func (ApiUrl) Key

func (a ApiUrl) Key() (Key, error)

Key extracts the TeamVault Key from the API URL path.

func (ApiUrl) String

func (a ApiUrl) String() string

String returns the string representation of the ApiUrl.

type Config

type Config struct {
	Url          Url              `json:"url"`
	User         User             `json:"user"`
	Password     Password         `json:"pass"`
	CacheEnabled bool             `json:"cacheEnabled,omitempty"`
	Timeout      libtime.Duration `json:"timeout,omitempty"`
}

Config holds the configuration for connecting to a TeamVault instance.

func ParseTeamvaultConfig

func ParseTeamvaultConfig(content []byte) (*Config, error)

ParseTeamvaultConfig parses a TeamVault configuration from JSON content.

type ConfigGenerator

type ConfigGenerator interface {
	Generate(
		ctx context.Context,
		sourceDirectory SourceDirectory,
		targetDirectory TargetDirectory,
	) error
}

ConfigGenerator generates configuration files by parsing templates and replacing TeamVault placeholders.

func NewConfigGenerator

func NewConfigGenerator(configParser ConfigParser) ConfigGenerator

NewConfigGenerator creates a new ConfigGenerator with the given ConfigParser.

type ConfigParser

type ConfigParser interface {
	Parse(ctx context.Context, content []byte) ([]byte, error)
}

ConfigParser parses configuration templates and replaces TeamVault placeholders with actual values.

func NewConfigParser

func NewConfigParser(
	teamvaultConnector Connector,
) ConfigParser

NewConfigParser creates a new ConfigParser with the given TeamVault Connector.

type Connector

type Connector interface {
	Password(ctx context.Context, key Key) (Password, error)
	User(ctx context.Context, key Key) (User, error)
	Url(ctx context.Context, key Key) (Url, error)
	File(ctx context.Context, key Key) (File, error)
	Search(ctx context.Context, name string) ([]Key, error)
}

Connector provides access to TeamVault secrets including passwords, users, URLs, and files.

func NewCacheConnector

func NewCacheConnector(connector Connector) Connector

NewCacheConnector creates a new Connector that caches responses from the underlying connector.

func NewDiskFallbackConnector

func NewDiskFallbackConnector(connector Connector) Connector

NewDiskFallbackConnector creates a new Connector that uses disk cache as fallback when the underlying connector fails.

func NewDummyConnector

func NewDummyConnector() Connector

NewDummyConnector creates a new Connector that returns deterministic dummy values for testing.

func NewRemoteConnector

func NewRemoteConnector(
	httpClient *http.Client,
	url Url,
	user User,
	pass Password,
	currentDateTime time.CurrentDateTime,
) Connector

NewRemoteConnector creates a new Connector that connects to a remote TeamVault instance.

type CurrentRevision

type CurrentRevision string

CurrentRevision represents the current revision identifier of a TeamVault secret.

func (CurrentRevision) String

func (t CurrentRevision) String() string

String returns the string representation of the CurrentRevision.

type File

type File string

File represents a base64-encoded file stored in TeamVault.

func (File) Content

func (t File) Content() ([]byte, error)

Content decodes and returns the file content from base64 encoding.

func (File) String

func (t File) String() string

String returns the string representation of the File.

type HtpasswdGenerator

type HtpasswdGenerator interface {
	Generate(ctx context.Context, key Key) ([]byte, error)
}

HtpasswdGenerator generates htpasswd formatted credentials from TeamVault secrets.

func NewHtpasswdGenerator

func NewHtpasswdGenerator(connector Connector) HtpasswdGenerator

NewHtpasswdGenerator creates a new HtpasswdGenerator with the given Connector.

type Key

type Key string

Key represents a TeamVault secret identifier.

func (Key) String

func (k Key) String() string

String returns the string representation of the Key.

func (Key) Validate added in v4.6.0

func (k Key) Validate(ctx context.Context) error

Validate checks if the Key is not empty.

type Keychain added in v4.9.0

type Keychain interface {
	// ReadPassword returns the password stored for the given TeamVault URL,
	// or ("", nil) if no entry exists. A non-nil error indicates a real
	// failure (Keychain locked, security binary error, etc.) — callers
	// should surface this to the user, not fall through silently.
	ReadPassword(ctx context.Context, url Url) (Password, error)

	// WritePassword stores or overwrites the password for the given URL.
	// On non-darwin platforms it returns ErrKeychainNotSupported.
	WritePassword(ctx context.Context, url Url, password Password) error
}

Keychain reads and writes TeamVault passwords from the OS credential store. On macOS it backs onto the login Keychain via the `security(1)` binary. On other platforms it is a no-op: ReadPassword returns ("", nil); WritePassword returns ErrKeychainNotSupported.

func NewKeychain added in v4.9.0

func NewKeychain() Keychain

NewKeychain returns a Keychain backed by the OS credential store. On macOS uses Keychain, on Linux uses Secret Service, on Windows uses Credential Manager. On platforms without a supported backend, ReadPassword returns ("", nil) for missing entries and Read/WritePassword return ErrKeychainNotSupported for no-backend errors.

func NewKeychainWithClient added in v4.13.0

func NewKeychainWithClient(client KeyringClient) Keychain

NewKeychainWithClient returns a Keychain using the given KeyringClient. Useful for tests that need to inject a fake credential store.

type KeyringClient added in v4.13.0

type KeyringClient interface {
	Get(service, user string) (string, error)
	Set(service, user, password string) error
}

KeyringClient is the package-private seam over zalando/go-keyring used by darwinKeychain. It exists so unit tests can drive WritePassword/ReadPassword without touching the real macOS Keychain. NewKeychain wires up the real implementation; tests construct darwinKeychain with a Counterfeiter fake.

type Password

type Password string

Password represents a TeamVault password.

func (Password) String

func (t Password) String() string

String returns the string representation of the Password.

func (*Password) UnmarshalJSON added in v4.7.5

func (t *Password) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler to handle both string and number types.

type RealKeyringClient added in v4.13.0

type RealKeyringClient struct{}

func (RealKeyringClient) Get added in v4.13.0

func (RealKeyringClient) Get(service, user string) (string, error)

func (RealKeyringClient) Set added in v4.13.0

func (RealKeyringClient) Set(service, user, password string) error

type SourceDirectory

type SourceDirectory string

SourceDirectory represents the source directory path for configuration generation.

func (SourceDirectory) String

func (s SourceDirectory) String() string

String returns the string representation of the SourceDirectory.

type Staging

type Staging bool

Staging indicates whether the TeamVault instance is a staging environment.

func (Staging) Bool

func (s Staging) Bool() bool

Bool returns the boolean value of Staging.

type TargetDirectory

type TargetDirectory string

TargetDirectory represents the target directory path for configuration generation.

func (TargetDirectory) String

func (t TargetDirectory) String() string

String returns the string representation of the TargetDirectory.

type TeamvaultConfigPath

type TeamvaultConfigPath string

TeamvaultConfigPath represents a path to a TeamVault configuration file.

func (TeamvaultConfigPath) Exists

func (t TeamvaultConfigPath) Exists() bool

Exists checks if the TeamvaultConfigPath points to an existing non-empty file.

func (TeamvaultConfigPath) NormalizePath

func (t TeamvaultConfigPath) NormalizePath() (TeamvaultConfigPath, error)

NormalizePath converts the TeamvaultConfigPath to an absolute path.

func (TeamvaultConfigPath) Parse

func (t TeamvaultConfigPath) Parse() (*Config, error)

Parse reads and parses the TeamVault configuration from the file.

func (TeamvaultConfigPath) String

func (t TeamvaultConfigPath) String() string

String returns the string representation of the TeamvaultConfigPath.

type Url

type Url string

Url represents a TeamVault URL or secret URL value.

func (Url) String

func (u Url) String() string

String returns the string representation of the Url.

type User

type User string

User represents a TeamVault username.

func (User) String

func (u User) String() string

String returns the string representation of the User.

func (*User) UnmarshalJSON added in v4.7.5

func (u *User) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler to handle both string and number types.

Directories

Path Synopsis
cmd
teamvault-file command
teamvault-login command
teamvault-url command
Package factory provides factory functions for creating TeamVault connectors and HTTP clients.
Package factory provides factory functions for creating TeamVault connectors and HTTP clients.
Code generated by counterfeiter.
Code generated by counterfeiter.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL