# @sdk/access SDK Reference The `github.com/router-for-me/CLIProxyAPI/v6/sdk/access` package centralizes inbound request authentication for the proxy. It offers a lightweight manager that chains credential providers, so servers can reuse the same access control logic inside or outside the CLI runtime. ## Importing ```go import ( sdkaccess "github.com/router-for-me/CLIProxyAPI/v6/sdk/access" "github.com/router-for-me/CLIProxyAPI/v6/internal/config" ) ``` Add the module with `go get github.com/router-for-me/CLIProxyAPI/v6/sdk/access`. ## Manager Lifecycle ```go manager := sdkaccess.NewManager() providers, err := sdkaccess.BuildProviders(cfg) if err != nil { return err } manager.SetProviders(providers) ``` * `NewManager` constructs an empty manager. * `SetProviders` replaces the provider slice using a defensive copy. * `Providers` retrieves a snapshot that can be iterated safely from other goroutines. * `BuildProviders` translates `config.Config` access declarations into runnable providers. When the config omits explicit providers but defines inline API keys, the helper auto-installs the built-in `config-api-key` provider. ## Authenticating Requests ```go result, err := manager.Authenticate(ctx, req) switch { case err == nil: // Authentication succeeded; result describes the provider and principal. case errors.Is(err, sdkaccess.ErrNoCredentials): // No recognizable credentials were supplied. case errors.Is(err, sdkaccess.ErrInvalidCredential): // Supplied credentials were present but rejected. default: // Transport-level failure was returned by a provider. } ``` `Manager.Authenticate` walks the configured providers in order. It returns on the first success, skips providers that surface `ErrNotHandled`, and tracks whether any provider reported `ErrNoCredentials` or `ErrInvalidCredential` for downstream error reporting. If the manager itself is `nil` or no providers are registered, the call returns `nil, nil`, allowing callers to treat access control as disabled without branching on errors. Each `Result` includes the provider identifier, the resolved principal, and optional metadata (for example, which header carried the credential). ## Configuration Layout The manager expects access providers under the `auth.providers` key inside `config.yaml`: ```yaml auth: providers: - name: inline-api type: config-api-key api-keys: - sk-test-123 - sk-prod-456 ``` Fields map directly to `config.AccessProvider`: `name` labels the provider, `type` selects the registered factory, `sdk` can name an external module, `api-keys` seeds inline credentials, and `config` passes provider-specific options. ### Loading providers from external SDK modules To consume a provider shipped in another Go module, point the `sdk` field at the module path and import it for its registration side effect: ```yaml auth: providers: - name: partner-auth type: partner-token sdk: github.com/acme/xplatform/sdk/access/providers/partner config: region: us-west-2 audience: cli-proxy ``` ```go import ( _ "github.com/acme/xplatform/sdk/access/providers/partner" // registers partner-token sdkaccess "github.com/router-for-me/CLIProxyAPI/v6/sdk/access" ) ``` The blank identifier import ensures `init` runs so `sdkaccess.RegisterProvider` executes before `BuildProviders` is called. ## Built-in Providers The SDK ships with one provider out of the box: - `config-api-key`: Validates API keys declared inline or under top-level `api-keys`. It accepts the key from `Authorization: Bearer`, `X-Goog-Api-Key`, `X-Api-Key`, or the `?key=` query string and reports `ErrInvalidCredential` when no match is found. Additional providers can be delivered by third-party packages. When a provider package is imported, it registers itself with `sdkaccess.RegisterProvider`. ### Metadata and auditing `Result.Metadata` carries provider-specific context. The built-in `config-api-key` provider, for example, stores the credential source (`authorization`, `x-goog-api-key`, `x-api-key`, or `query-key`). Populate this map in custom providers to enrich logs and downstream auditing. ## Writing Custom Providers ```go type customProvider struct{} func (p *customProvider) Identifier() string { return "my-provider" } func (p *customProvider) Authenticate(ctx context.Context, r *http.Request) (*sdkaccess.Result, error) { token := r.Header.Get("X-Custom") if token == "" { return nil, sdkaccess.ErrNoCredentials } if token != "expected" { return nil, sdkaccess.ErrInvalidCredential } return &sdkaccess.Result{ Provider: p.Identifier(), Principal: "service-user", Metadata: map[string]string{"source": "x-custom"}, }, nil } func init() { sdkaccess.RegisterProvider("custom", func(cfg *config.AccessProvider, root *config.Config) (sdkaccess.Provider, error) { return &customProvider{}, nil }) } ``` A provider must implement `Identifier()` and `Authenticate()`. To expose it to configuration, call `RegisterProvider` inside `init`. Provider factories receive the specific `AccessProvider` block plus the full root configuration for contextual needs. ## Error Semantics - `ErrNoCredentials`: no credentials were present or recognized by any provider. - `ErrInvalidCredential`: at least one provider processed the credentials but rejected them. - `ErrNotHandled`: instructs the manager to fall through to the next provider without affecting aggregate error reporting. Return custom errors to surface transport failures; they propagate immediately to the caller instead of being masked. ## Integration with cliproxy Service `sdk/cliproxy` wires `@sdk/access` automatically when you build a CLI service via `cliproxy.NewBuilder`. Supplying a preconfigured manager allows you to extend or override the default providers: ```go coreCfg, _ := config.LoadConfig("config.yaml") providers, _ := sdkaccess.BuildProviders(coreCfg) manager := sdkaccess.NewManager() manager.SetProviders(providers) svc, _ := cliproxy.NewBuilder(). WithConfig(coreCfg). WithAccessManager(manager). Build() ``` The service reuses the manager for every inbound request, ensuring consistent authentication across embedded deployments and the canonical CLI binary. ### Hot reloading providers When configuration changes, rebuild providers and swap them into the manager: ```go providers, err := sdkaccess.BuildProviders(newCfg) if err != nil { log.Errorf("reload auth providers failed: %v", err) return } accessManager.SetProviders(providers) ``` This mirrors the behaviour in `cliproxy.Service.refreshAccessProviders` and `api.Server.applyAccessConfig`, enabling runtime updates without restarting the process.