apiserver

package
v0.0.9 Latest Latest
Warning

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

Go to latest
Published: Aug 4, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

README

pkg/apiserver — Native REST API Server

A Gin HTTP server listening on :8080, providing tenant-side Sandbox CRUD and API Key management interfaces.

Directory Structure

handlers/
  server.go        # Handler implementation (implements StrictServerInterface)
  server_test.go   # Unit tests for the handler
gen/
  agentbox.gen.go  # Generated by oapi-codegen from openapi.yaml, strictly DO NOT edit manually

API Generation Workflow (OpenAPI-First)

Authoritative specification: pkg/openapi/native/openapi.yaml

make generate-api   # → pkg/apiserver/gen/agentbox.gen.go

Workflow for modifying API fields:

  1. Edit pkg/openapi/native/openapi.yaml
  2. Run make generate-api
  3. Update the conversion functions in handlers/server.go:
    • genSpecToK8sSpec() — HTTP DTO → K8s CRD spec
    • domainTemplateSpecToGen() — K8s CRD spec → HTTP DTO
  4. Add/update tests in handlers/server_test.go

Template Spec Field Mapping

HTTP DTO Field K8s CRD Field Conversion Method
template (YAML string) EmbeddedSandboxTemplate.Template (*corev1.PodTemplateSpec) yaml.Unmarshal / yaml.Marshal
runtimes ([]Runtime) EmbeddedSandboxTemplate.Runtimes 1:1, protocol string ↔ corev1.Protocol
reservation EmbeddedSandboxTemplate.Reservation replicaQuota: resource.ParseQuantity / .String()
visibility SandboxTemplateSpec.Visibility 1:1
version, description, idleImage Direct string fields No conversion
Template version ordering on Update

spec.version must strictly increase on every PUT /admin/sandboxtemplates/{name}, with one carve-out: an update whose only difference is the agentbox.navix.sh/docs annotation may reuse the current version. Docs are prose about using the template — they never reach the rendered Pod (they are not even synced onto member Pools), so they name no new template revision. A lower version is always rejected.

Implemented in service/sandboxtemplate_service.go (versionRegressed() / docsOnlyChange(), shared by the optimistic-lock and retry Update paths). The dashboard mirrors the same rule in lib/utils/template-crd.ts (isDocsOnlyChange()) so the editor does not accept what the server rejects.

Docs placeholders

The docs annotation supports these placeholders, substituted server-side:

Placeholder GET /envs/{name} (envDocs) GET /sandboxtemplates/{name} (docs)
${AGBX_ENV_NAME} the Env's name YOUR_ENV_NAME
${AGBX_POOL_NAME} first member Pool that exists in the local cluster, else <envName>-pool-name YOUR_POOL_NAME
${AGBX_API_KEY} caller's first key with a recoverable token (else 422 API_KEY_REQUIRED) YOUR_API_KEY
${AGBX_CLUSTER_ID} local cluster ID same
${AGBX_NATIVE_URL} / ${AGBX_E2B_URL} / ${AGBX_DATA_URL} the local cluster's gateway.{nativeURL,e2bURL,dataURL} same
${AGBX_DATA_DOMAIN} data URL minus scheme — the E2B SDK's E2B_DOMAIN form same
${AGBX_HOST} gateway hostname (from the data URL) same
${AGBX_INNER_IP} IP that host is pinned to by hostAliases — lets in-cluster clients skip the public gateway same
${AGBX_HTTPS} "true" / "false" for the data URL's scheme same
${AGBX_REGISTRY_HOST} the cluster's first registries[].host same

Everything from ${AGBX_CLUSTER_ID} down is a fact about the serving cluster (read from the cluster-config ConfigMap via ClusterService.Endpoints), so both renderers substitute the real value; only the env-scoped ones degrade to hints on the Template page.

A placeholder whose value is unknown is left in the output verbatim — an unconfigured gateway, a host with no alias, or a cluster with no registry must not render as an empty string, or users would copy a broken URL without noticing.

handlers/env_docs.go owns both renderers (docsVars.apply() is the single substitution point). The pool name comes from docsPoolName(), which reads the Env's local status segment (a member marked Missing has no live Pool) so only real, local Pools are named.

Testing

go test ./pkg/apiserver/... -v

Discovery Endpoints (AI-Friendly)

Since v0.3.0, configuration-related errors will return a list of available resources in ErrorResponse.detail to facilitate self-healing for AI Agents. See docs/api/discovery-endpoints.md for details:

  • GET /v1/clusters — Exposes the cluster catalog, enabling the SDK/CLI to discover valid prefixes for {clusterID}.{uuid} / {clusterID}::{poolName}
  • POST /v1/sandboxes 404 → detail.availablePools
  • POST /v1/sandboxpools 400 (template does not exist) → detail.availableTemplates
  • POST /v1/sandboxpools 400 (missing quotaURL) → detail.availableQuotaURLs

SOP for adding new discovery responses:

  1. Extend a *Detail type in pkg/apiserver/domain/errors.go
  2. Implement a build*Detail(ctx, ...) helper in the corresponding service method
  3. Assign appErr.Detail = ... when returning an AppError
  4. Add an example (including the detail structure) under the corresponding response in pkg/openapi/native/openapi.yaml
  5. Add a new section in docs/api/discovery-endpoints.md

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// BindAddress is the TCP address the HTTP server listens on (e.g. ":8080").
	BindAddress string
	// KeyStore is the API key store used for authentication.
	KeyStore apikey.KeyStore
	// AdminKeyManager validates the shared admin key.  When nil, admin-key
	// authentication is disabled (dev mode).
	AdminKeyManager *apikey.AdminKeyManager
	// IAMService resolves team/user identities to Kubernetes namespaces and
	// handles JWT-based authentication issued by the BFF.
	IAMService service.IAMService
	// RestConfig is the Kubernetes REST config, used for WebSocket exec (terminal).
	// When nil, the terminal endpoint is disabled.
	RestConfig *rest.Config
	// Secret is the shared secret used to authenticate ws-proxy connections on
	// /v1/ws/sync. When empty, the sync endpoint is disabled.
	Secret string
	// ClusterConfigSink is called whenever a cluster_config_sync frame arrives from
	// ws-proxy. Typically a cluster.ConfigMapWriter that persists the routing table
	// so ExtProc can read it. May be nil when cluster config sync is not required.
	ClusterConfigSink service.ClusterConfigSink
	// Forwarder is the cross-cluster forwarder. When nil, cross-cluster requests
	// will be rejected. Typically constructed from cluster store + localClusterID.
	// The forwarder already carries the local cluster ID internally.
	Forwarder *service.CrossClusterForwarder
	// ClusterStore is the shared in-memory catalog of known clusters. It is
	// surfaced through GET /v1/clusters so SDK/CLI callers can discover valid
	// cross-cluster prefixes. May be nil in single-cluster deployments.
	ClusterStore *cluster.Store
	// LocalClusterID identifies the cluster serving this API server. Used to mark
	// the `local: true` entry in the /v1/clusters response. Empty in
	// single-cluster deployments where cross-cluster routing is disabled.
	LocalClusterID string
	// QuotaProvider selects the quota backend exposed by GET /v1/quotas and
	// inspected by the /v1/feature-gates endpoint. When nil, a Noop provider
	// is used (feature disabled).
	QuotaProvider quotaplugin.Provider
	// InstanceTypeProvider selects the InstanceType catalog backend exposed
	// by GET /v1/instancetypes and inspected by the /v1/feature-gates
	// endpoint. When nil, a Noop provider is used (feature disabled).
	InstanceTypeProvider instancetypeplugin.Provider
	// ServerVersion is the build-time version string stamped on every response
	// via X-AgentBox-Server-Version. Set from pkg/version.Version in app.go.
	ServerVersion string
	// FederationRegistry / FederationSource enable cross-cluster capacity
	// federation on the sync service. Both nil in single-cluster deployments,
	// which disables federation entirely. Only consulted when Secret is set
	// (federation rides the same ws-proxy sync connection).
	FederationRegistry *federation.Registry
	FederationSource   federation.CapacitySource
}

Config holds the configuration for the API server.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server is the HTTP API server.

func New

func New(cfg Config, k8sClient client.Client, clientset kubernetes.Interface, sandboxStore store.SandboxStore,
	pluginManager *plugins.PluginManager, envoyGatewayBaseURL string,
	sandboxSvc service.SandboxService) *Server

New creates and configures the HTTP server with all service layers wired up. pluginManager may be nil (disables lifecycle plugins — open-source mode). sandboxSvc may be nil; when nil a default service is constructed internally. Pass a non-nil sandboxSvc to share the service with other components (e.g. SandboxPoolReconciler for idle-pod notifications).

func (*Server) Start

func (s *Server) Start(ctx context.Context) error

Start runs the server until ctx is cancelled, then gracefully shuts down.

Directories

Path Synopsis
Package gen provides primitives to interact with the openapi HTTP API.
Package gen provides primitives to interact with the openapi HTTP API.
Package handlers implements the StrictServerInterface generated from the OpenAPI spec.
Package handlers implements the StrictServerInterface generated from the OpenAPI spec.
envautoscaler
Package envautoscaler serves the Env-scoped autoscaler-config CRUD surface.
Package envautoscaler serves the Env-scoped autoscaler-config CRUD surface.
envcommon
Package envcommon hosts the helpers shared between the env shell service (pkg/apiserver/service) and the per-resource sub-services (envmember, envautoscaler).
Package envcommon hosts the helpers shared between the env shell service (pkg/apiserver/service) and the per-resource sub-services (envmember, envautoscaler).
envmember
Package envmember serves the Env-scoped Pool CRUD surface: `/v1/sandboxenvs/{name}/sandboxpools/*`.
Package envmember serves the Env-scoped Pool CRUD surface: `/v1/sandboxenvs/{name}/sandboxpools/*`.
envscheduler
Package envscheduler implements the in-process Env-level request router.
Package envscheduler implements the in-process Env-level request router.
federation
Package federation holds the Worker-side soft-state view of cross-cluster SandboxEnv capacity.
Package federation holds the Worker-side soft-state view of cross-cluster SandboxEnv capacity.

Jump to

Keyboard shortcuts

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