client

package module
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Mar 19, 2026 License: Apache-2.0 Imports: 43 Imported by: 8

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 three 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

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
)
View Source
const (
	// DefaultCallbackPort is the default port for the OAuth callback server.
	DefaultCallbackPort = 8484

	// DefaultOAuthTimeout is the default timeout for the OAuth flow.
	DefaultOAuthTimeout = 5 * time.Minute

	// DefaultOAuthScopes are the default OAuth scopes to request.
	DefaultOAuthScopes = "user:email,read:org"
)
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
)

Variables

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

Functions

This section is empty.

Types

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 (github, google, azure, etc.)
	Provider string `json:"provider,omitempty"`

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

	// User is the authenticated username.
	User string `json:"user,omitempty"`

	// UserID is the provider-specific user ID.
	UserID string `json:"user_id,omitempty"`

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

	// Orgs are the user's organizations/tenants/domains.
	Orgs []string `json:"orgs,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) 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) 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) 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

func (c *Client) PullReferrer(ctx context.Context, req *storev1.PullReferrerRequest) (<-chan *storev1.PullReferrerResponse, error)

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"`

	// OAuth configuration (for browser-based login)
	GitHubClientID     string `json:"github_client_id,omitempty"     mapstructure:"github_client_id"`
	GitHubClientSecret string `json:"github_client_secret,omitempty" mapstructure:"github_client_secret"`

	// GitHub token (PAT or OAuth) - can be set via flag/env for CI/CD use
	// Developers: use 'dirctl auth login' instead
	// CI/CD: set via DIRECTORY_CLIENT_GITHUB_TOKEN env var or --github-token flag
	GitHubToken string `json:"github_token,omitempty" mapstructure:"github_token"`
}

func LoadConfig

func LoadConfig() (*Config, error)

type DeviceCodeResponse added in v1.0.0

type DeviceCodeResponse struct {
	DeviceCode      string `json:"device_code"`
	UserCode        string `json:"user_code"`
	VerificationURI string `json:"verification_uri"`
	ExpiresIn       int    `json:"expires_in"`
	Interval        int    `json:"interval"` // Minimum seconds between polls
}

DeviceCodeResponse is the response from GitHub's device code endpoint.

type DeviceFlowConfig added in v1.0.0

type DeviceFlowConfig struct {
	ClientID string
	Scopes   []string
	Output   io.Writer // Where to write user instructions (default: os.Stdout)
}

DeviceFlowConfig configures the device authorization flow.

type DeviceFlowError added in v1.0.0

type DeviceFlowError struct {
	Code        string
	Description string
	NewInterval int // New polling interval (for slow_down errors)
}

DeviceFlowError represents an OAuth2 device flow error.

func (*DeviceFlowError) Error added in v1.0.0

func (e *DeviceFlowError) Error() string

Error implements the error interface.

type DeviceFlowResult added in v1.0.0

type DeviceFlowResult struct {
	AccessToken string //nolint:gosec // G117: intentional field for OAuth token
	TokenType   string
	Scope       string
	ExpiresAt   time.Time // Calculated expiry (GitHub doesn't provide expires_in for device flow)
}

DeviceFlowResult contains the successful device flow result.

func StartDeviceFlow added in v1.0.0

func StartDeviceFlow(ctx context.Context, config *DeviceFlowConfig) (*DeviceFlowResult, error)

StartDeviceFlow initiates GitHub OAuth2 device authorization flow. This flow is ideal for CLI applications, SSH sessions, and headless environments.

The flow:

  1. Request device and user codes from GitHub
  2. Display verification URL and user code to the user
  3. Poll GitHub until user completes authorization
  4. Return access token

The user can complete authorization on any device (phone, laptop, etc.).

type DeviceTokenResponse added in v1.0.0

type DeviceTokenResponse struct {
	AccessToken      string `json:"access_token"` //nolint:gosec // G117: intentional field for OAuth token
	TokenType        string `json:"token_type"`
	Scope            string `json:"scope"`
	Error            string `json:"error,omitempty"`
	ErrorDescription string `json:"error_description,omitempty"`
	Interval         int    `json:"interval,omitempty"` // New interval when slow_down is returned
}

DeviceTokenResponse is the response from GitHub's token endpoint during polling.

type OAuthConfig added in v1.0.0

type OAuthConfig struct {
	// ClientID is the GitHub OAuth App client ID.
	ClientID string

	// ClientSecret is the GitHub OAuth App client secret.
	// This can be empty for public clients using PKCE.
	ClientSecret string //nolint:gosec // G117: intentional field

	// Scopes are the OAuth scopes to request.
	// Default: ["user:email", "read:org"]
	Scopes []string

	// CallbackPort is the port for the local callback server.
	// Default: 8484
	CallbackPort int

	// Timeout is the maximum time to wait for the OAuth flow.
	// Default: 5 minutes
	Timeout time.Duration

	// SkipBrowserOpen skips automatically opening the browser.
	// Useful for headless environments or testing.
	SkipBrowserOpen bool

	// Output is where to write status messages.
	// Default: os.Stdout
	Output io.Writer
}

OAuthConfig holds the configuration for GitHub OAuth.

type OAuthTokenResult added in v1.0.0

type OAuthTokenResult struct {
	// AccessToken is the GitHub access token.
	AccessToken string //nolint:gosec // G117: intentional field

	// TokenType is the token type (usually "bearer").
	TokenType string

	// ExpiresAt is when the token expires (if available).
	ExpiresAt time.Time

	// Scopes are the granted scopes.
	Scopes []string
}

OAuthTokenResult holds the result of a successful OAuth flow.

func InteractiveLogin added in v1.0.0

func InteractiveLogin(ctx context.Context, cfg OAuthConfig) (*OAuthTokenResult, error)

InteractiveLogin performs an interactive browser-based GitHub OAuth login. It opens the user's browser to GitHub's authorization page, starts a local HTTP server to receive the callback, and exchanges the code for an access token.

type Option

type Option func(*options) error

func WithConfig

func WithConfig(config *Config) Option

func WithEnvConfig

func WithEnvConfig() Option

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