client

package module
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Apr 13, 2026 License: Apache-2.0 Imports: 47 Imported by: 10

README

Directory Golang SDK

Overview

Dir Golang SDK provides a simple way to interact with the Directory API. It allows developers to integrate and use Directory functionality from their applications with ease.

Features

The Directory SDK provides comprehensive access to all Directory APIs with a simple, intuitive interface:

Store API
  • Record Management: Push records to the store and pull them by reference
  • Metadata Operations: Look up record metadata without downloading full content
  • Data Lifecycle: Delete records permanently from the store
  • Referrer Support: Push and pull artifacts for existing records
  • Sync Management: Manage storage synchronization policies between Directory servers
Search API
  • Flexible Search: Search stored records using text, semantic, and structured queries
  • Advanced Filtering: Filter results by metadata, content type, and other criteria
Routing API
  • Network Publishing: Publish records to make them discoverable across the network
  • Content Discovery: List and query published records across the network
  • Network Management: Unpublish records to remove them from network discovery
Signing and Verification
  • Local Signing: Sign records locally using private keys or OIDC-based authentication.
  • Remote Verification: Verify record signatures using the Directory gRPC API
Developer Experience
  • Async Support: Non-blocking operations with streaming responses for large datasets
  • Error Handling: Comprehensive gRPC error handling with detailed error messages
  • Configuration: Flexible configuration via environment variables or direct instantiation

Installation

  1. Initialize the project:
go mod init example.com/myapp
  1. Add the SDK to your project:
go get github.com/agntcy/dir/client

Configuration

The SDK can be configured via environment variables or direct instantiation.

Environment Variables
Variable Description Default
DIRECTORY_CLIENT_SERVER_ADDRESS Directory server address 0.0.0.0:8888
DIRECTORY_CLIENT_AUTH_MODE Authentication mode: x509, jwt, or empty for insecure "" (insecure)
DIRECTORY_CLIENT_SPIFFE_SOCKET_PATH SPIFFE Workload API socket path ""
DIRECTORY_CLIENT_JWT_AUDIENCE JWT audience for JWT authentication ""
Authentication

The SDK supports multiple authentication modes:

1. Insecure (No Authentication)

For local development only. Not recommended for production.

Environment Variables:

export DIRECTORY_CLIENT_SERVER_ADDRESS="localhost:8888"
# AUTH_MODE is empty or not set

Code Example:

import (
    "context"
    "github.com/agntcy/dir/client"
)

ctx := context.Background()
config := &client.Config{
    ServerAddress: "localhost:8888",
    // AuthMode is empty - insecure connection
}
c, err := client.New(ctx, client.WithConfig(config))
if err != nil {
    // handle error
}
defer c.Close() // Always close to cleanup resources
2. X509 (X.509-SVID)

Recommended for production. Requires SPIRE agent.

Environment Variables:

export DIRECTORY_CLIENT_SERVER_ADDRESS="localhost:8888"
export DIRECTORY_CLIENT_AUTH_MODE="x509"
export DIRECTORY_CLIENT_SPIFFE_SOCKET_PATH="unix:///run/spire/agent-sockets/api.sock"

Code Example:

import (
    "context"
    "github.com/agntcy/dir/client"
)

ctx := context.Background()
config := &client.Config{
    ServerAddress:    "localhost:8888",
    AuthMode:         "x509",
    SpiffeSocketPath: "unix:///run/spire/agent-sockets/api.sock",
}
c, err := client.New(ctx, client.WithConfig(config))
if err != nil {
    // handle error
}
defer c.Close() // Always close to cleanup resources
3. JWT (JWT-SVID)

Alternative to X.509 for client authentication. Requires SPIRE agent.

Note: In JWT mode, the server presents its X.509-SVID via TLS for server authentication and encryption, while the client authenticates using a JWT-SVID. This provides both transport security and client authentication, following the official SPIFFE JWT pattern.

Environment Variables:

export DIRECTORY_CLIENT_SERVER_ADDRESS="localhost:8888"
export DIRECTORY_CLIENT_AUTH_MODE="jwt"
export DIRECTORY_CLIENT_SPIFFE_SOCKET_PATH="unix:///run/spire/agent-sockets/api.sock"
export DIRECTORY_CLIENT_JWT_AUDIENCE="spiffe://example.org/dir-server"

Code Example:

import (
    "context"
    "github.com/agntcy/dir/client"
)

ctx := context.Background()
config := &client.Config{
    ServerAddress:    "localhost:8888",
    AuthMode:         "jwt",
    SpiffeSocketPath: "unix:///run/spire/agent-sockets/api.sock",
    JWTAudience:      "spiffe://example.org/dir-server",
}
c, err := client.New(ctx, client.WithConfig(config))
if err != nil {
    // handle error
}
defer c.Close() // Always close to cleanup resources
4. OIDC (OpenID Connect)

For human users and CI/scripts authenticating via an OIDC provider (e.g. Dex).

Environment Variables:

export DIRECTORY_CLIENT_SERVER_ADDRESS="gateway.example.com:443"
export DIRECTORY_CLIENT_AUTH_MODE="oidc"
export DIRECTORY_CLIENT_AUTH_TOKEN="eyJhbG..."  # pre-issued JWT (optional; skips interactive login)

Interactive login (via CLI):

dirctl auth login --oidc-issuer=https://dex.example.com --oidc-client-id=dirctl

Code Example:

import (
    "context"
    "github.com/agntcy/dir/client"
)

ctx := context.Background()
config := &client.Config{
    ServerAddress: "gateway.example.com:443",
    AuthMode:      "oidc",
    AuthToken:     "eyJhbG...",  // or leave empty to use cached token from dirctl auth login
}
c, err := client.New(ctx, client.WithConfig(config))
if err != nil {
    // handle error
}
defer c.Close()

Getting Started

Prerequisites
  • Golang - Go programming language
1. Server Setup

Option A: Local Development Server

# Clone the repository and start the server using Taskfile
task server:start

Option B: Custom Server

# Set your Directory server address
export DIRECTORY_CLIENT_SERVER_ADDRESS="your-server:8888"
2. SDK Installation
# Add the Directory SDK
go get github.com/agntcy/dir/client

Documentation

Index

Constants

View Source
const (
	DefaultEnvPrefix = "DIRECTORY_CLIENT"

	DefaultServerAddress = "0.0.0.0:8888"
	DefaultTlsSkipVerify = false
	DefaultCallbackPort  = 8484
	DefaultOAuthTimeout  = 5 * time.Minute
)
View Source
const (
	// DefaultTokenCacheDir is the default directory for storing cached tokens (relative to home directory).
	// When XDG_CONFIG_HOME is set, tokens are stored at $XDG_CONFIG_HOME/dirctl instead.
	//nolint:gosec // G101: This is a directory path, not a credential
	DefaultTokenCacheDir = ".config/dirctl"

	// TokenCacheFile is the filename for the cached token.
	//nolint:gosec // G101: This is a filename, not a credential
	TokenCacheFile = "auth-token.json"

	// DefaultTokenValidityDuration is how long a token is considered valid if no expiry is set.
	DefaultTokenValidityDuration = 8 * time.Hour

	// TokenExpiryBuffer is how much time before actual expiry we consider a token expired.
	TokenExpiryBuffer = 5 * time.Minute
)
View Source
const DefaultOIDCScopes = "openid email profile"

DefaultOIDCScopes are the OIDC scopes requested for interactive login.

Variables

View Source
var DefaultConfig = Config{
	ServerAddress: DefaultServerAddress,
}
View Source
var OIDC = &OIDCProvider{}

OIDC provides OIDC authentication flows.

Functions

This section is empty.

Types

type AuthResult added in v1.2.0

type AuthResult struct {
	AccessToken  string
	RefreshToken string
	TokenType    string
	ExpiresAt    time.Time
	IDToken      string
	Subject      string
	Email        string
	Name         string
}

AuthResult is the unified result from any OIDC authentication flow (PKCE or device).

type CachedToken added in v1.0.0

type CachedToken struct {
	// AccessToken is the authentication token.
	AccessToken string `json:"access_token"` // #nosec G117: intentional field - for cached token

	// TokenType is the token type (usually "bearer").
	TokenType string `json:"token_type,omitempty"`

	// Provider is the authentication provider (oidc, github, google, azure, etc.)
	Provider string `json:"provider,omitempty"`

	// Issuer is the OIDC issuer URL (for Provider=oidc). Enables multi-issuer support.
	Issuer string `json:"issuer,omitempty"`

	// RefreshToken is the refresh token, if the IdP returned one (for token refresh).
	RefreshToken string `json:"refresh_token,omitempty"` // #nosec G101: intentional field - for cached token

	// ExpiresAt is when the token expires.
	ExpiresAt time.Time `json:"expires_at,omitzero"`

	// User is the authenticated username (e.g. preferred_username or name for OIDC).
	User string `json:"user,omitempty"`

	// UserID is the provider-specific user ID (e.g. sub for OIDC).
	UserID string `json:"user_id,omitempty"`

	// Email is the user's email address.
	Email string `json:"email,omitempty"`

	// CreatedAt is when the token was cached.
	CreatedAt time.Time `json:"created_at"`
}

CachedToken represents a cached authentication token from any provider.

type Client

func New

func New(ctx context.Context, opts ...Option) (*Client, error)

func (*Client) Close

func (c *Client) Close() error

func (*Client) CreateSync

func (c *Client) CreateSync(ctx context.Context, remoteURL string, cids []string) (string, error)

func (*Client) Delete

func (c *Client) Delete(ctx context.Context, recordRef *corev1.RecordRef) error

Delete removes a record from the store using its reference.

func (*Client) DeleteBatch

func (c *Client) DeleteBatch(ctx context.Context, recordRefs []*corev1.RecordRef) error

DeleteBatch removes multiple records from the store in a single stream for efficiency.

func (*Client) DeleteReferrer added in v1.2.0

func (*Client) DeleteStream

func (c *Client) DeleteStream(ctx context.Context, refsCh <-chan *corev1.RecordRef) (streaming.StreamResult[emptypb.Empty], error)

DeleteStream provides efficient streaming delete operations using channels. Record references are sent as they become available and delete confirmations are returned as they're processed. This method maintains a single gRPC stream for all operations, dramatically improving efficiency.

func (*Client) DeleteSync

func (c *Client) DeleteSync(ctx context.Context, syncID string) error

func (*Client) GetSync

func (c *Client) GetSync(ctx context.Context, syncID string) (*storev1.GetSyncResponse, error)

func (*Client) GetVerificationInfo added in v1.0.0

func (c *Client) GetVerificationInfo(ctx context.Context, cid string) (*namingv1.GetVerificationInfoResponse, error)

GetVerificationInfo retrieves the verification info for a record by CID.

func (*Client) GetVerificationInfoByName added in v1.0.0

func (c *Client) GetVerificationInfoByName(ctx context.Context, name string, version string) (*namingv1.GetVerificationInfoResponse, error)

GetVerificationInfoByName retrieves the verification info for a record by name. If version is empty, the latest version is used.

func (*Client) GetWorkload added in v1.2.0

func (c *Client) GetWorkload(ctx context.Context, workloadID string) (*runtimev1.Workload, error)

func (*Client) List

func (c *Client) List(ctx context.Context, req *routingv1.ListRequest) (<-chan *routingv1.ListResponse, error)

func (*Client) ListSyncs

func (c *Client) ListSyncs(ctx context.Context, req *storev1.ListSyncsRequest) (<-chan *storev1.ListSyncsItem, error)

func (*Client) ListWorkloads added in v1.2.0

func (c *Client) ListWorkloads(ctx context.Context, labels map[string]string) ([]*runtimev1.Workload, error)

func (*Client) ListWorkloadsStream added in v1.2.0

func (c *Client) ListWorkloadsStream(ctx context.Context, labels map[string]string) (streaming.StreamResult[runtimev1.Workload], error)

func (*Client) ListenStream

ListenStream streams events from the server with the specified filters.

Returns a StreamResult that provides structured channels for receiving events, errors, and completion signals.

Example - Listen to all events:

result, err := client.ListenStream(ctx, &eventsv1.ListenRequest{})
if err != nil {
    return err
}

for {
    select {
    case resp := <-result.ResCh():
        event := resp.GetEvent()
        fmt.Printf("Event: %s - %s\n", event.Type, event.ResourceId)
    case err := <-result.ErrCh():
        return fmt.Errorf("stream error: %w", err)
    case <-result.DoneCh():
        return nil
    case <-ctx.Done():
        return ctx.Err()
    }
}

Example - Filter by event type:

result, err := client.ListenStream(ctx, &eventsv1.ListenRequest{
    EventTypes: []eventsv1.EventType{
        eventsv1.EventType_EVENT_TYPE_RECORD_PUSHED,
        eventsv1.EventType_EVENT_TYPE_RECORD_PUBLISHED,
    },
})

Example - Filter by labels:

result, err := client.ListenStream(ctx, &eventsv1.ListenRequest{
    LabelFilters: []string{"/skills/AI"},
})

func (*Client) Lookup

func (c *Client) Lookup(ctx context.Context, recordRef *corev1.RecordRef) (*corev1.RecordMeta, error)

Lookup retrieves metadata for a record using its reference.

func (*Client) LookupBatch

func (c *Client) LookupBatch(ctx context.Context, recordRefs []*corev1.RecordRef) ([]*corev1.RecordMeta, error)

LookupBatch retrieves metadata for multiple records in a single stream for efficiency.

func (*Client) LookupStream

func (c *Client) LookupStream(ctx context.Context, refsCh <-chan *corev1.RecordRef) (streaming.StreamResult[corev1.RecordMeta], error)

LookupStream provides efficient streaming lookup operations using channels. Record references are sent as they become available and metadata is returned as it's processed. This method maintains a single gRPC stream for all operations, dramatically improving efficiency.

Uses sequential streaming pattern (Send → Recv → Send → Recv) which ensures strict ordering of request-response pairs.

func (*Client) Publish

func (c *Client) Publish(ctx context.Context, req *routingv1.PublishRequest) error

func (*Client) Pull

func (c *Client) Pull(ctx context.Context, recordRef *corev1.RecordRef) (*corev1.Record, error)

Pull retrieves a single record from the store using its reference. This is a convenience wrapper around PullBatch for single-record operations.

func (*Client) PullBatch

func (c *Client) PullBatch(ctx context.Context, recordRefs []*corev1.RecordRef) ([]*corev1.Record, error)

PullBatch retrieves multiple records in a single stream for efficiency. This is a convenience method that accepts a slice and returns a slice, built on top of the streaming implementation for consistency.

func (*Client) PullPublicKeys added in v1.1.0

func (c *Client) PullPublicKeys(ctx context.Context, recordRef *corev1.RecordRef) ([]string, error)

PullPublicKeys fetches all public key referrers for a record.

func (*Client) PullReferrer

PullReferrer retrieves all referrers using the PullReferrer RPC.

func (*Client) PullSignatures added in v1.1.0

func (c *Client) PullSignatures(ctx context.Context, recordRef *corev1.RecordRef) ([]*signv1.Signature, error)

PullSignatures fetches all signature referrers for a record.

func (*Client) PullStream

func (c *Client) PullStream(ctx context.Context, refsCh <-chan *corev1.RecordRef) (streaming.StreamResult[corev1.Record], error)

PullStream retrieves multiple records efficiently using a single bidirectional stream. This method is ideal for batch operations and takes full advantage of gRPC streaming. The input channel allows you to send record refs as they become available.

func (*Client) Push

func (c *Client) Push(ctx context.Context, record *corev1.Record) (*corev1.RecordRef, error)

Push sends a complete record to the store and returns a record reference. This is a convenience wrapper around PushBatch for single-record operations. The record must be ≤4MB as per the v1 store service specification.

func (*Client) PushBatch

func (c *Client) PushBatch(ctx context.Context, records []*corev1.Record) ([]*corev1.RecordRef, error)

PushBatch sends multiple records in a single stream for efficiency. This is a convenience method that accepts a slice and returns a slice, built on top of the streaming implementation for consistency.

func (*Client) PushReferrer

PushReferrer stores a signature using the PushReferrer RPC.

func (*Client) PushStream

func (c *Client) PushStream(ctx context.Context, recordsCh <-chan *corev1.Record) (streaming.StreamResult[corev1.RecordRef], error)

PushStream uploads multiple records efficiently using a single bidirectional stream. This method is ideal for batch operations and takes full advantage of gRPC streaming. The input channel allows you to send records as they become available.

func (*Client) Resolve added in v1.0.0

func (c *Client) Resolve(ctx context.Context, name string, version string) (*namingv1.ResolveResponse, error)

Resolve resolves a record reference (name with optional version) to CIDs. Returns all matching records sorted by created_at descending (newest first). If version is empty, returns all versions; otherwise returns matches for the specific version.

func (*Client) SearchCIDs added in v0.5.6

SearchCIDs searches for record CIDs matching the given request.

func (*Client) SearchRecords added in v0.5.6

SearchRecords searches for full records matching the given request.

func (*Client) SearchRouting

func (c *Client) SearchRouting(ctx context.Context, req *routingv1.SearchRequest) (<-chan *routingv1.SearchResponse, error)

func (*Client) Sign

Sign routes to the appropriate signing method based on provider type. This is the main entry point for signing operations.

func (*Client) Unpublish

func (c *Client) Unpublish(ctx context.Context, req *routingv1.UnpublishRequest) error

func (*Client) Verify

Verify verifies signatures for a record. When from_server is true, the result is the server's cached verification; when false, verification is performed locally.

type Config

type Config struct {
	ServerAddress    string `json:"server_address,omitempty"     mapstructure:"server_address"`
	TlsSkipVerify    bool   `json:"tls_skip_verify,omitempty"    mapstructure:"tls_skip_verify"`
	TlsCertFile      string `json:"tls_cert_file,omitempty"      mapstructure:"tls_cert_file"`
	TlsKeyFile       string `json:"tls_key_file,omitempty"       mapstructure:"tls_key_file"`
	TlsCAFile        string `json:"tls_ca_file,omitempty"        mapstructure:"tls_ca_file"`
	SpiffeSocketPath string `json:"spiffe_socket_path,omitempty" mapstructure:"spiffe_socket_path"`
	SpiffeToken      string `json:"spiffe_token,omitempty"       mapstructure:"spiffe_token"`
	AuthMode         string `json:"auth_mode,omitempty"          mapstructure:"auth_mode"`
	JWTAudience      string `json:"jwt_audience,omitempty"       mapstructure:"jwt_audience"`

	// OIDC configuration (for interactive login)
	OIDCIssuer   string `json:"oidc_issuer,omitempty"    mapstructure:"oidc_issuer"`
	OIDCClientID string `json:"oidc_client_id,omitempty" mapstructure:"oidc_client_id"`

	// Pre-issued Bearer token for CI/scripts (skips interactive login)
	AuthToken string `json:"auth_token,omitempty" mapstructure:"auth_token"`
}

func LoadConfig

func LoadConfig() (*Config, error)

type DeviceFlowConfig added in v1.0.0

type DeviceFlowConfig struct {
	Issuer       string
	ClientID     string
	Scopes       []string
	PollInterval time.Duration
	Timeout      time.Duration
	Output       io.Writer
}

DeviceFlowConfig holds configuration for the OAuth 2.0 Device Authorization Grant (RFC 8628).

type OIDCProvider added in v1.2.0

type OIDCProvider struct{}

OIDCProvider groups OIDC-related methods.

func (*OIDCProvider) RunDeviceFlow added in v1.2.0

func (*OIDCProvider) RunDeviceFlow(ctx context.Context, cfg *DeviceFlowConfig) (*AuthResult, error)

RunDeviceFlow performs the OAuth 2.0 Device Authorization Grant (RFC 8628).

func (*OIDCProvider) RunPKCEFlow added in v1.2.0

func (*OIDCProvider) RunPKCEFlow(ctx context.Context, cfg *PKCEConfig) (*AuthResult, error)

RunPKCEFlow performs the OIDC Authorization Code flow with PKCE.

type Option

type Option func(*options) error

func WithConfig

func WithConfig(config *Config) Option

func WithEnvConfig

func WithEnvConfig() Option

type PKCEConfig added in v1.2.0

type PKCEConfig struct {
	Issuer          string
	ClientID        string
	RedirectURI     string
	Scopes          []string
	CallbackPort    int
	SkipBrowserOpen bool
	Timeout         time.Duration
	Output          io.Writer
}

PKCEConfig holds configuration for the OIDC PKCE flow.

type TokenCache added in v1.0.0

type TokenCache struct {
	// CacheDir is the directory where tokens are stored.
	CacheDir string
}

TokenCache manages cached authentication tokens from any provider.

func NewTokenCache added in v1.0.0

func NewTokenCache() *TokenCache

NewTokenCache creates a new token cache with the default directory. Respects XDG_CONFIG_HOME environment variable for config directory location.

func NewTokenCacheWithDir added in v1.0.0

func NewTokenCacheWithDir(dir string) *TokenCache

NewTokenCacheWithDir creates a new token cache with a custom directory.

func (*TokenCache) Clear added in v1.0.0

func (c *TokenCache) Clear() error

Clear removes the cached token.

func (*TokenCache) GetCachePath added in v1.0.0

func (c *TokenCache) GetCachePath() string

GetCachePath returns the full path to the token cache file.

func (*TokenCache) GetValidToken added in v1.0.0

func (c *TokenCache) GetValidToken() (*CachedToken, error)

GetValidToken returns a valid cached token or nil if none exists.

func (*TokenCache) IsValid added in v1.0.0

func (c *TokenCache) IsValid(token *CachedToken) bool

IsValid checks if a cached token is still valid. A token is considered valid if it exists and hasn't expired.

func (*TokenCache) Load added in v1.0.0

func (c *TokenCache) Load() (*CachedToken, error)

Load loads the cached token from disk. Returns nil if no valid token is found.

func (*TokenCache) Save added in v1.0.0

func (c *TokenCache) Save(token *CachedToken) error

Save saves a token to the cache.

type TokenCacheEntry added in v1.0.0

type TokenCacheEntry = CachedToken

TokenCacheEntry is an alias for CachedToken (for compatibility).

Directories

Path Synopsis
utils
verify
Package verify provides signature verification types and logic (Fetcher, VerifyWithFetcher).
Package verify provides signature verification types and logic (Fetcher, VerifyWithFetcher).

Jump to

Keyboard shortcuts

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