http

package
v1.3.1 Latest Latest
Warning

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

Go to latest
Published: Jun 11, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package http is Harbor's HTTP transport driver for the unified tool catalog (Phase 27). It ships two registration paths — inline `RegisterHTTPTool(...)` for the dev loop and a UTCP-style YAML manifest loader for operator deployments — converging on the same `tools.ToolDescriptor` shape (Transport = TransportHTTP). Every invocation runs through the Phase 26 `ToolPolicy` reliability shell (D-024); the driver itself does NOT loop independently.

Static auth (API key, bearer, cookie) is supported via the `AuthSpec` value plus a secret loaded from operator-supplied config; secrets MUST NOT live in URL templates or request payloads (AGENTS.md §7). OAuth / token-exchange flows are deferred to Phase 30 via the unified pause/resume primitive.

Concurrent reuse (D-025): every HTTP `ToolDescriptor` is safe under N concurrent invocations against the same underlying `*http.Client` because (a) per-invocation state lives on the goroutine stack (request value, response value, attempt context, classifier), and (b) the descriptor's compiled `text/template`s + cached schema validators are read-only after construction.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrAuthMissing — auth spec declares a secret-bearing kind but
	// the supplied secret is empty. Surfaced loudly at register-time
	// so misconfiguration is caught at boot, not at first invocation.
	ErrAuthMissing = errors.New("http: auth secret missing or empty")
	// ErrAuthInvalidSpec — AuthSpec internally inconsistent (e.g.
	// api_key with both HeaderName and QueryParam set).
	ErrAuthInvalidSpec = errors.New("http: auth spec invalid")
)

Sentinel auth errors.

View Source
var (
	// ErrTemplateRender — text/template execution failed (e.g.
	// missing variable referenced via {{ .Args.Foo }}, or a tag
	// the template engine couldn't parse). Wraps the underlying
	// error via %w.
	ErrTemplateRender = errors.New("http: URL/body template render failure")
	// ErrTemplateSecretLeak — a manifest / inline template attempted
	// to reference the .Auth namespace (the credential boundary).
	// Surfaced at registration time so the misconfiguration never
	// reaches a live invocation. AGENTS.md §7 — no credential
	// passthrough by default.
	ErrTemplateSecretLeak = errors.New("http: template attempted to interpolate auth/secret reference")
	// ErrUnsupportedMethod — RegisterHTTPTool got a method outside
	// the HTTP allowlist (POST / GET / PUT / DELETE / PATCH).
	ErrUnsupportedMethod = errors.New("http: HTTP method not supported")
	// ErrIdentityMissing — the per-invocation ctx had no identity
	// quadruple. Identity is mandatory (AGENTS.md §6).
	ErrIdentityMissing = errors.New("http: identity missing from ctx")
	// ErrHTTPStatus — non-2xx response classifier emitted this so
	// upstream callers can errors.Is against it; the wrapped detail
	// carries status + retry hint.
	ErrHTTPStatus = errors.New("http: response status")
)

Sentinel errors for the inline / manifest registration paths and per-invocation failures. Callers compare via errors.Is.

View Source
var ErrManifestInvalid = errors.New("http: manifest invalid")

ErrManifestInvalid wraps every manifest-loader / validation failure. Callers compare via errors.Is.

Functions

func IsRateLimited

func IsRateLimited(err error) (time.Duration, bool)

IsRateLimited reports whether err is (or wraps) a RateLimitedError. Returns the delay alongside the boolean for ergonomic callers.

func RegisterHTTPTool

func RegisterHTTPTool(
	cat tools.ToolCatalog,
	name, method, urlTemplate string,
	opts ...HTTPOption,
) error

RegisterHTTPTool registers a single HTTP tool inline. Returns the catalog's Register error (typically ErrToolDuplicateName) or any pre-registration validation error (unsupported method, secret leak in template, invalid auth spec).

Identity-mandatory: the registered descriptor reads the (tenant, user, session) triple from ctx on every Invoke and fails with ErrIdentityMissing when absent. Phase 30 will extend this with tool-side OAuth tokens (per-identity); Phase 27 honours only the operator-configured static auth secret.

Concurrent reuse (D-025): the produced descriptor is safe for N concurrent goroutines — per-invocation state (request, response, classifier output) lives on the stack; cached templates and compiled schema validators are read-only after construction.

func RegisterManifest

func RegisterManifest(cat tools.ToolCatalog, m *Manifest) error

RegisterManifest registers every tool in m against cat. Errors short-circuit on the first failure with the offending tool name in the wrapped detail; previously-registered tools remain in the catalog (the caller controls catalog lifetime).

Concurrent reuse (D-025): each registered descriptor is safe under N concurrent invocations (see RegisterHTTPTool).

Types

type AuthKind

type AuthKind string

AuthKind is the static-auth discriminator. Phase 27 ships three values; OAuth and token-exchange land in Phase 30.

const (
	// AuthKindNone — no auth applied (default). Useful for public
	// endpoints (e.g. weather APIs that take an API key as a query
	// parameter via AuthKindAPIKey + QueryParam, or completely open
	// services).
	AuthKindNone AuthKind = ""
	// AuthKindAPIKey — secret placed in either a header (when
	// HeaderName is set) or a query parameter (when QueryParam is
	// set). Exactly one MUST be set.
	AuthKindAPIKey AuthKind = "api_key"
	// AuthKindBearer — RFC 6750 bearer token. Sets the
	// "Authorization: Bearer <secret>" header.
	AuthKindBearer AuthKind = "bearer"
	// AuthKindCookie — secret placed in the named cookie.
	AuthKindCookie AuthKind = "cookie"
)

type AuthSpec

type AuthSpec struct {
	Kind       AuthKind
	HeaderName string
	QueryParam string
	CookieName string
}

AuthSpec configures static authentication for an HTTP tool. The shape is value-typed (no pointers) so a tool descriptor that declares auth at construction has it baked-in for every invocation; the secret value lives separately in `httpToolConfig.secret` so the AuthSpec itself stays loggable / printable.

The Kind drives which of the optional fields is consulted:

  • AuthKindAPIKey: HeaderName XOR QueryParam (exactly one).
  • AuthKindBearer: no extra fields.
  • AuthKindCookie: CookieName (mandatory).

func (AuthSpec) Validate

func (s AuthSpec) Validate() error

Validate reports whether the spec is internally consistent. Returns nil for AuthKindNone (no auth applied). Returns ErrAuthInvalidSpec wrapped with the offending detail otherwise.

type HTTPOption

type HTTPOption func(*httpToolConfig)

HTTPOption configures an HTTP tool at registration. Mirrors the option-functional pattern from the inproc driver.

func WithArgsSchema

func WithArgsSchema(schema []byte) HTTPOption

WithArgsSchema attaches the input JSON schema (raw bytes). When absent, the descriptor's Validate is a permissive pass-through — the operator opts INTO validation explicitly.

func WithAuth

func WithAuth(spec AuthSpec, secret string) HTTPOption

WithAuth attaches a static auth spec and the corresponding secret value. The secret is loaded from operator-supplied config (env variable, manifest auth_ref); it MUST NOT come from a request payload (D-NNN: no credential passthrough by default).

func WithAuthScopes

func WithAuthScopes(scopes ...string) HTTPOption

WithAuthScopes adds required catalog-visibility scopes.

func WithBodyTemplate

func WithBodyTemplate(tmpl string) HTTPOption

WithBodyTemplate sets a text/template for the request body. The rendered output is sent with `Content-Type: application/json` unless overridden by WithHeaders.

func WithClient

func WithClient(c *http.Client) HTTPOption

WithClient overrides the *http.Client used for this tool. Operators with bespoke transports (custom TLS roots, proxy, retryable client) wire them in here.

func WithDescription

func WithDescription(s string) HTTPOption

WithDescription overrides the Tool's planner-facing description.

func WithHTTPSource

func WithHTTPSource(id tools.ToolSourceID) HTTPOption

WithHTTPSource overrides the descriptor's ToolSourceID. Defaults to "inline:<tool-name>" for the inline registration path or "manifest:<filename>" for the manifest path.

func WithHeaders

func WithHeaders(h map[string]string) HTTPOption

WithHeaders adds static headers applied AFTER auth so an operator can't accidentally overwrite the auth header with a static one.

func WithLoading

func WithLoading(m tools.LoadingMode) HTTPOption

WithLoading overrides LoadingMode (default: LoadingAlways).

func WithOutSchema

func WithOutSchema(schema []byte) HTTPOption

WithOutSchema attaches the output JSON schema (raw bytes).

func WithPolicy

func WithPolicy(p tools.ToolPolicy) HTTPOption

WithPolicy overrides the per-tool ToolPolicy.

func WithSideEffect

func WithSideEffect(s tools.SideEffect) HTTPOption

WithSideEffect declares the tool's side-effect class.

func WithTags

func WithTags(tags ...string) HTTPOption

WithTags adds operator-facing tags.

type Manifest

type Manifest struct {
	// Tools declares the HTTP tool surface.
	Tools []ManifestTool `yaml:"tools"`
	// Auth declares static-auth specs keyed by `auth_ref`. A tool
	// references one via ManifestTool.AuthRef.
	Auth map[string]ManifestAuth `yaml:"auth,omitempty"`
}

Manifest is the UTCP-style YAML schema describing one or more HTTP tools as Harbor `Tool`s. `LoadManifest` parses a manifest file and `RegisterManifest` registers its tools onto a catalog.

The boot-path wiring is NOT yet built: no production path reads `ToolsConfig.HTTPManifests` and calls these (the config validator rejects a populated list for exactly that reason — see docs/notes/sdk-friction-audit.md §1). Until the loader lands, embedders call `LoadManifest` + `RegisterManifest` (or the inline `RegisterHTTPTool`) from their own assembly code.

Schema (YAML):

auth:
  weather_key:
    kind: api_key
    header: X-API-Key
    secret: ${WEATHER_API_KEY}      # env-ref form is mandatory; literal secrets rejected
  github_bot:
    kind: bearer
    secret: ${GITHUB_BOT_TOKEN}

tools:
  - name: weather.lookup
    method: GET
    url_template: https://api.weather.example/v1/now?city={{ .Args.city | urlquery }}
    description: Look up current weather by city name.
    auth_ref: weather_key
    args_schema: '{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}'
    out_schema:  '{"type":"object","properties":{"temp_c":{"type":"number"}}}'
    side_effect: external
    tags: [weather]
    loading: always

  - name: gh.create_issue
    method: POST
    url_template: https://api.github.com/repos/{{ .Args.owner }}/{{ .Args.repo }}/issues
    body_template: '{"title":{{ .Args.title }},"body":{{ .Args.body }}}'
    auth_ref: github_bot

Security boundary: a template that references the `.Auth` namespace is rejected loudly at load time (AGENTS.md §7 — no credential passthrough). Secrets are sourced from `auth_ref` lookups only, and the manifest may NOT inline literal secret strings — every `auth.<name>.secret` value MUST match `${ENV_VAR}` shape so the actual value lives in the operator's environment / secret manager.

func LoadManifest

func LoadManifest(path string) (*Manifest, error)

LoadManifest reads a YAML manifest from disk and returns a fully-validated `*Manifest` with `${ENV_VAR}` secret references expanded against `os.Getenv`. Returns `ErrManifestInvalid` wrapped with the offending detail on any validation failure (unknown fields, missing referenced env var, literal secret value, name collision, template-secret leak, etc.).

Path traversal is mitigated via `filepath.Clean` — the resulting absolute path is what the loader reads.

type ManifestAuth

type ManifestAuth struct {
	Kind   AuthKind `yaml:"kind"`
	Header string   `yaml:"header,omitempty"`
	Query  string   `yaml:"query,omitempty"`
	Cookie string   `yaml:"cookie,omitempty"`
	Secret string   `yaml:"secret" secret:"true"`
}

ManifestAuth is one static-auth spec entry in a manifest. The Secret field carries the operator-supplied secret value in `${ENV_VAR}` reference form; literal values are rejected at load time (AGENTS.md §7).

type ManifestTool

type ManifestTool struct {
	Name        string            `yaml:"name"`
	Method      string            `yaml:"method"`
	URLTemplate string            `yaml:"url_template"`
	Description string            `yaml:"description,omitempty"`
	ArgsSchema  string            `yaml:"args_schema,omitempty"`
	OutSchema   string            `yaml:"out_schema,omitempty"`
	Headers     map[string]string `yaml:"headers,omitempty"`
	Body        string            `yaml:"body_template,omitempty"`
	AuthRef     string            `yaml:"auth_ref,omitempty"`
	SideEffect  tools.SideEffect  `yaml:"side_effect,omitempty"`
	Tags        []string          `yaml:"tags,omitempty"`
	AuthScopes  []string          `yaml:"auth_scopes,omitempty"`
	Loading     tools.LoadingMode `yaml:"loading,omitempty"`
	Policy      *tools.ToolPolicy `yaml:"policy,omitempty"`
	// SourceID overrides the descriptor's ToolSourceID; defaults to
	// "manifest:<file>#<name>" when empty.
	SourceID string `yaml:"source_id,omitempty"`
}

ManifestTool is one HTTP-tool entry in a manifest.

type RateLimitedError

type RateLimitedError interface {
	error
	RateLimitDelay() time.Duration
	Status() int
}

RateLimitedError is implemented by error types that carry a parsed Retry-After delay. Callers can errors.As against this interface to honour the delay (e.g. as a sleep floor before retry).

Jump to

Keyboard shortcuts

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