api

package
v0.1.17 Latest Latest
Warning

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

Go to latest
Published: Jul 12, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package api is the llmbox HTTP box-control API: the JSON surface the server exposes for box operations (create/get/list/destroy/logs/exec, proxies, spokes) and a Client that speaks it. The UI and any programmatic caller drive boxes through this API; the stand-alone llmbox-mcp binary is one such caller, wrapping the Client so it can serve those operations as MCP tools.

Backend is the operation contract both sides share: the server implements it, NewHandler serves an implementation over HTTP, and Client is an implementation backed by a remote server.

Index

Constants

View Source
const (
	PathCreateBox           = "/api/v1/create-box"
	PathLookupBox           = "/api/v1/lookup-box"
	PathListBoxes           = "/api/v1/list-boxes"
	PathSpokeStatuses       = "/api/v1/spoke-statuses"
	PathCreateSpoke         = "/api/v1/create-spoke"
	PathDropSpoke           = "/api/v1/drop-spoke"
	PathSetDefaultSpoke     = "/api/v1/set-default-spoke"
	PathListJoinTokens      = "/api/v1/list-join-tokens"
	PathRevokeJoinToken     = "/api/v1/revoke-join-token"
	PathRegenerateJoinToken = "/api/v1/regenerate-join-token"
	PathDestroyBox          = "/api/v1/destroy-box"
	PathPauseBox            = "/api/v1/pause-box"
	PathResumeBox           = "/api/v1/resume-box"
	PathBoxExec             = "/api/v1/box-exec"
	PathProxyEnabled        = "/api/v1/proxy-enabled"
	PathCreateProxy         = "/api/v1/create-proxy"
	PathDeleteProxy         = "/api/v1/delete-proxy"
	PathListProxies         = "/api/v1/list-proxies"
)

API route paths. Each maps 1:1 to a Backend method and is served/called with a single POST carrying a JSON body (an empty body for the no-argument methods).

View Source
const TokenPlaceholder = "<one-time-token>"

TokenPlaceholder stands in for the join-token secret in commands re-rendered after creation: the secret is stored only hashed, so once the create response is gone it can never be shown again.

Variables

This section is empty.

Functions

func NewHandler

func NewHandler(b Backend) http.Handler

NewHandler builds the HTTP handler serving b's box operations as the JSON box- control API, one POST route per Backend method. It is meant to be mounted on the server's mux under the /api/v1/ prefix.

SECURITY — this handler performs no authentication of its own: it is pure transport. The hub mounts it behind its API auth middleware (an API key as a bearer token, or an admin login session with a CSRF header), so every route here is only reachable by an authenticated caller. Mounting it anywhere else without an equivalent gate exposes box exec/destroy to anyone who can reach it.

@arg b The backend whose operations are exposed over HTTP. @return http.Handler A mux serving the box-control routes over b.

@testcase TestBackendAPIRoundTrip drives every route through NewClient against NewHandler.

Types

type Backend

type Backend interface {
	// CreateBox launches a box and returns its registered session.
	CreateBox(ctx context.Context, opts sandbox.CreateOptions) (BoxSession, error)
	// LookupByBoxID finds a box's session by its caller-assigned box ID
	// (case-insensitive); ok is false when none matches.
	LookupByBoxID(boxID string) (sess BoxSession, ok bool)
	// ListBoxes returns all boxes managed across every spoke.
	ListBoxes(ctx context.Context) ([]BoxView, error)
	// SpokeStatuses returns every spoke and whether it is currently connected.
	SpokeStatuses(ctx context.Context) ([]SpokeStatus, error)
	// CreateSpoke mints a one-time join token enrolling a new spoke and returns it
	// with the ready-to-run start command. backend picks the command's box backend
	// ("docker" or "firecracker"; empty means docker); ttl<=0 uses the default.
	CreateSpoke(ctx context.Context, name, backend string, ttl time.Duration) (SpokeEnrollment, error)
	// DropSpoke removes a spoke's enrollment, revokes its join tokens, and
	// disconnects it.
	DropSpoke(ctx context.Context, name string) error
	// SetDefaultSpoke makes an enrolled spoke the default that unqualified box
	// creates run on.
	SetDefaultSpoke(ctx context.Context, name string) error
	// ListJoinTokens returns every outstanding spoke join token.
	ListJoinTokens(ctx context.Context) ([]JoinTokenInfo, error)
	// RevokeJoinToken deletes one outstanding join token by its ID.
	RevokeJoinToken(ctx context.Context, id string) error
	// RegenerateJoinToken replaces an outstanding join token with a freshly
	// minted one for the same spoke (same name and backend), returning the new
	// enrollment. The old token stops working; the new secret is shown once.
	RegenerateJoinToken(ctx context.Context, id string) (SpokeEnrollment, error)
	// DestroyBox stops and removes the box with the given box ID.
	DestroyBox(ctx context.Context, boxID string) error
	// PauseBox stops the compute of the box with the given box ID to save CPU/RAM,
	// keeping its disk so it can be resumed later.
	PauseBox(ctx context.Context, boxID string) error
	// ResumeBox restarts a paused box's compute; the box comes back on the next
	// ListBoxes.
	ResumeBox(ctx context.Context, boxID string) error
	// BoxExec runs a shell command inside the box with the given box ID.
	BoxExec(ctx context.Context, boxID, command string) (sandbox.ExecResult, error)
	// ProxyEnabled reports whether the HTTP proxy feature is configured.
	ProxyEnabled() bool
	// CreateProxy enables an HTTP proxy to a box's port and returns it. description
	// is an optional human-readable note stamped onto the proxy, or "" for none.
	CreateProxy(ctx context.Context, boxID string, port int, description string) (ProxyInfo, error)
	// DeleteProxy disables the proxy for a box and port.
	DeleteProxy(ctx context.Context, boxID string, port int) error
	// ListProxies returns the enabled proxies, optionally filtered to one box.
	ListProxies(ctx context.Context, boxID string) ([]ProxyInfo, error)
}

Backend is the box-operation contract the API layer needs. The server implements it; tests supply a fake.

type BoxSession

type BoxSession struct {
	BoxID       string
	Generation  string
	Description string
}

BoxSession is the subset of a box's state the API surfaces. It is a flat value (no locks, no pointers into the server) so callers never reach back into the server's internals and tests can construct one directly.

type BoxView

type BoxView struct {
	sandbox.Box
}

BoxView is one listed box. It is the underlying box as tracked by the hub; a box whose init script failed carries phase "broken" and its captured output in LastError.

type Client

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

Client is a Backend that forwards every call to a remote box-control API. It lets a process drive boxes on an upstream llmbox server over HTTP with no in-process access to Docker, the store, or the cluster — the llmbox-mcp binary wraps one to serve those operations as MCP tools.

func NewClient

func NewClient(baseURL string, hc *http.Client) *Client

NewClient builds a Client targeting the box-control API at baseURL (the upstream llmbox server's address). A nil hc uses http.DefaultClient.

@arg baseURL The upstream server's base URL, e.g. http://llmbox:8081. @arg hc The HTTP client to use; nil uses http.DefaultClient. @return *Client A backend forwarding calls to baseURL.

@testcase TestBackendAPIRoundTrip builds a client this way and drives every method.

func (*Client) BoxExec

func (c *Client) BoxExec(ctx context.Context, boxID, command string) (sandbox.ExecResult, error)

BoxExec runs a shell command inside a box by its box ID.

@arg ctx Context for the request. @arg boxID The box ID of the box to run the command in. @arg command The shell command line to run inside the box. @return sandbox.ExecResult The command's stdout, stderr, and exit code. @error error if the command cannot be run.

@testcase TestBackendAPIRoundTrip runs a command through the client.

func (*Client) CreateBox

func (c *Client) CreateBox(ctx context.Context, opts sandbox.CreateOptions) (BoxSession, error)

CreateBox launches a box on the upstream server and returns its session.

@arg ctx Context for the request. @arg opts The image, box ID, description, and target spoke for the box. @return BoxSession The new box's ID, container ID, and auth token. @error error if the box cannot be created.

@testcase TestBackendAPIRoundTrip creates a box through the client.

func (*Client) CreateProxy

func (c *Client) CreateProxy(ctx context.Context, boxID string, port int, description string) (ProxyInfo, error)

CreateProxy enables an HTTP proxy to a box's port on the upstream server.

@arg ctx Context for the request. @arg boxID The box ID whose port to expose. @arg port The port inside the box to forward to. @arg description An optional human-readable note for the proxy, or "" for none. @return ProxyInfo The new proxy's box ID, port, URL, slug, spoke, and description. @error error if the proxy cannot be created.

@testcase TestBackendAPIRoundTrip creates a proxy through the client.

func (*Client) CreateSpoke

func (c *Client) CreateSpoke(ctx context.Context, name, backend string, ttl time.Duration) (SpokeEnrollment, error)

CreateSpoke mints a one-time join token for a new spoke on the upstream server and returns it with the ready-to-run start command.

@arg ctx Context for the request. @arg name The spoke name to enroll. @arg backend The box backend in the returned command ("docker" or "firecracker"; empty means docker). @arg ttl How long the token stays valid; <=0 uses the server default. @return SpokeEnrollment The token and start command. @error error if the token cannot be minted.

@testcase TestBackendAPIRoundTrip creates a spoke enrollment through the client.

func (*Client) DeleteProxy

func (c *Client) DeleteProxy(ctx context.Context, boxID string, port int) error

DeleteProxy disables the proxy for a box and port on the upstream server.

@arg ctx Context for the request. @arg boxID The box ID of the proxy to remove. @arg port The port of the proxy to remove. @error error if the proxy cannot be removed.

@testcase TestBackendAPIRoundTrip deletes a proxy through the client.

func (*Client) DestroyBox

func (c *Client) DestroyBox(ctx context.Context, boxID string) error

DestroyBox stops and removes the box with the given box ID on the upstream server.

@arg ctx Context for the request. @arg boxID The box ID of the box to destroy. @error error if the box cannot be destroyed.

@testcase TestBackendAPIRoundTrip destroys a box through the client.

func (*Client) DropSpoke

func (c *Client) DropSpoke(ctx context.Context, name string) error

DropSpoke removes a spoke's enrollment on the upstream server.

@arg ctx Context for the request. @arg name The spoke name to drop. @error error if the spoke cannot be dropped.

@testcase TestBackendAPIRoundTrip drops a spoke through the client.

func (*Client) ListBoxes

func (c *Client) ListBoxes(ctx context.Context) ([]BoxView, error)

ListBoxes returns all boxes managed by the upstream server.

@arg ctx Context for the request. @return []BoxView The managed boxes. @error error if listing boxes fails.

@testcase TestBackendAPIRoundTrip lists boxes through the client.

func (*Client) ListJoinTokens

func (c *Client) ListJoinTokens(ctx context.Context) ([]JoinTokenInfo, error)

ListJoinTokens returns every outstanding spoke join token from the upstream server.

@arg ctx Context for the request. @return []JoinTokenInfo The outstanding join tokens. @error error if the tokens cannot be read.

@testcase TestBackendAPIRoundTrip lists join tokens through the client.

func (*Client) ListProxies

func (c *Client) ListProxies(ctx context.Context, boxID string) ([]ProxyInfo, error)

ListProxies returns the enabled proxies on the upstream server, optionally filtered to one box.

@arg ctx Context for the request. @arg boxID The box ID to filter by, or "" for all proxies. @return []ProxyInfo The matching proxies. @error error if the proxies cannot be listed.

@testcase TestBackendAPIRoundTrip lists proxies through the client.

func (*Client) LookupByBoxID

func (c *Client) LookupByBoxID(boxID string) (BoxSession, bool)

LookupByBoxID resolves a box by its box ID on the upstream server.

@arg boxID The box ID to look up. @return BoxSession The matching box's session (zero value when not found). @return bool Whether a box with that box ID exists.

@testcase TestBackendAPIRoundTrip looks a box up by box ID through the client.

func (*Client) PauseBox added in v0.1.6

func (c *Client) PauseBox(ctx context.Context, boxID string) error

PauseBox stops a box's compute by its box ID to save CPU/RAM, keeping its disk.

@arg ctx Context for the request. @arg boxID The box ID of the box to pause. @error error if the box cannot be paused.

@testcase TestBackendAPIRoundTrip pauses a box through the client.

func (*Client) ProxyEnabled

func (c *Client) ProxyEnabled() bool

ProxyEnabled reports whether the upstream server has HTTP proxying configured. A transport failure is reported as disabled.

@return bool True when proxying is enabled on the upstream server.

@testcase TestBackendAPIRoundTrip reports proxy enablement through the client.

func (*Client) RegenerateJoinToken added in v0.1.2

func (c *Client) RegenerateJoinToken(ctx context.Context, id string) (SpokeEnrollment, error)

RegenerateJoinToken replaces an outstanding join token on the upstream server with a freshly minted one for the same spoke, returning the new enrollment (the old token stops working; the new secret is shown once).

@arg ctx Context for the request. @arg id The token ID to regenerate. @return SpokeEnrollment The fresh enrollment: name, one-time token, and start command. @error error if the token is unknown or cannot be regenerated.

@testcase TestBackendAPIRoundTrip regenerates a join token through the client.

func (*Client) ResumeBox added in v0.1.6

func (c *Client) ResumeBox(ctx context.Context, boxID string) error

ResumeBox restarts a paused box's compute by its box ID; the box relaunches with a fresh session URL, visible on the next ListBoxes.

@arg ctx Context for the request. @arg boxID The box ID of the box to resume. @error error if the box cannot be resumed.

@testcase TestBackendAPIRoundTrip resumes a box through the client.

func (*Client) RevokeJoinToken

func (c *Client) RevokeJoinToken(ctx context.Context, id string) error

RevokeJoinToken deletes one outstanding join token by ID on the upstream server.

@arg ctx Context for the request. @arg id The token ID to revoke. @error error if the token cannot be revoked.

@testcase TestBackendAPIRoundTrip revokes a join token through the client.

func (*Client) SetAPIKey

func (c *Client) SetAPIKey(key string)

SetAPIKey makes the client authenticate every request with key as a bearer token (Authorization: Bearer <key>). An empty key sends no credential.

@arg key The API key minted with `llmbox-server apikey add`, or "" for none.

@testcase TestClientSendsAPIKey sends the key as a bearer token on requests.

func (*Client) SetDefaultSpoke

func (c *Client) SetDefaultSpoke(ctx context.Context, name string) error

SetDefaultSpoke makes an enrolled spoke the default on the upstream server.

@arg ctx Context for the request. @arg name The spoke name to make the default. @error error if the default cannot be set.

@testcase TestBackendAPIRoundTrip sets the default spoke through the client.

func (*Client) SpokeStatuses

func (c *Client) SpokeStatuses(ctx context.Context) ([]SpokeStatus, error)

SpokeStatuses returns every spoke and its connection status from the upstream server.

@arg ctx Context for the request. @return []SpokeStatus The spokes and their connection status. @error error if the spokes cannot be read.

@testcase TestBackendAPIRoundTrip reads spoke statuses through the client.

type JoinTokenInfo

type JoinTokenInfo struct {
	ID        string    `json:"id" jsonschema:"the opaque token ID used to revoke it"`
	Name      string    `json:"name" jsonschema:"the spoke name the token enrolls"`
	Backend   string    `json:"backend" jsonschema:"the box backend recorded when the token was created"`
	Command   string    `` /* 138-byte string literal not displayed */
	ExpiresAt time.Time `json:"expires_at" jsonschema:"when the token stops being accepted"`
}

JoinTokenInfo describes one outstanding spoke join token: an opaque ID to revoke it by, the spoke name it enrolls, the box backend recorded at creation, the enrollment command with TokenPlaceholder standing in for the secret, and its expiry. The token secret is never recoverable.

type ProxyInfo

type ProxyInfo struct {
	BoxID string `json:"box_id" jsonschema:"the box ID whose port is exposed"`
	Port  int    `json:"port" jsonschema:"the port inside the box that is exposed"`
	URL   string `json:"url" jsonschema:"the URL the user opens to reach the box's port"`
	Slug  string `json:"slug" jsonschema:"the unguessable sub-domain label identifying the proxy"`
	Spoke string `json:"spoke,omitempty" jsonschema:"the spoke the box runs on"`
	// Description is the optional human-readable note supplied when the proxy was created.
	Description string `json:"description,omitempty" jsonschema:"the optional human-readable note supplied when the proxy was created"`
}

ProxyInfo describes one enabled HTTP proxy: the box and port it exposes and the URL the user opens to reach it.

type SpokeEnrollment

type SpokeEnrollment struct {
	Name    string `json:"name" jsonschema:"the spoke name the token enrolls"`
	Token   string `json:"token" jsonschema:"the one-time join token (shown once)"`
	Command string `json:"command" jsonschema:"the copy-pasteable command that starts the spoke and enrolls it"`
}

SpokeEnrollment is the result of minting a join token for a new spoke: the one-time token and the ready-to-run command that starts the spoke with it. The token is shown once and never recoverable.

type SpokeStatus

type SpokeStatus struct {
	Name       string    `json:"name" jsonschema:"the spoke's name"`
	Connected  bool      `json:"connected" jsonschema:"whether the spoke currently has a live connection to the hub"`
	Default    bool      `json:"default,omitempty" jsonschema:"true for the default spoke unqualified box creates run on"`
	EnrolledAt time.Time `json:"enrolled_at,omitempty" jsonschema:"when the spoke enrolled"`
}

SpokeStatus describes one enrolled cluster spoke and its health: whether it currently holds a live hub connection, and whether it is the default spoke that unqualified box creates run on.

Jump to

Keyboard shortcuts

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