Documentation
¶
Overview ¶
Package api serves a small newline-delimited JSON protocol over a unix socket, so an agent harness can drive the environment programmatically (run commands, branch, roll back) and get structured results — including the snapshot node id produced by each command, which it can later checkout or branch from.
One JSON request per line; one JSON response per line. Example:
{"op":"exec","cmd":"apt-get install -y jq"}
-> {"ok":true,"exit":0,"stdout":"...","node":"ab12cd34ef56","head":"ab12cd34ef56"}
Index ¶
- Constants
- func GuardHTTP(addr, token string) error
- func Serve(ctx context.Context, r *repo.Repo, c *repo.Capturer, sockPath string) error
- func ServeHTTP(ctx context.Context, r *repo.Repo, c *repo.Capturer, addr, token string) error
- func ServeHTTPListener(ctx context.Context, r *repo.Repo, _ *repo.Capturer, ln net.Listener, ...) error
- func ServeListener(ctx context.Context, r *repo.Repo, c *repo.Capturer, ln net.Listener, ...) error
- type Gate
- type UpgradeHandler
Constants ¶
const HTTPVersion = "0.3.0"
HTTPVersion is reported in the OpenAPI spec's info block. Bumped manually alongside backwards-incompatible HTTP-layer changes (independent of the agentenv release tag, which can grow without changing this surface).
Variables ¶
This section is empty.
Functions ¶
func GuardHTTP ¶ added in v0.3.1
GuardHTTP returns an error if binding addr without a token would expose the API beyond loopback. Exported so the daemon, which now owns the TCP listener itself (for fd handoff across a hot upgrade), can apply the same guard ServeHTTP applies before it binds.
func Serve ¶
Serve listens on sockPath and serves requests until ctx is cancelled.
Security: the socket is created with default fs perms (~0755 after umask), which lets ANY local user connect and invoke `exec` — i.e. run arbitrary commands as whoever runs agentenv (often root). We immediately tighten it to 0600 so only the owning uid can talk to the daemon. Callers that need to share the socket across UIDs should chmod/chown it themselves AFTER Serve has started (with full awareness of the implications).
func ServeHTTP ¶ added in v0.3.0
ServeHTTP runs the HTTP API on addr (e.g. "127.0.0.1:8911"). Blocks until ctx is cancelled. When token != "", every request must carry `Authorization: Bearer <token>` — non-loopback addresses without a token are rejected at startup as a safety guard. Compatible with the socket dispatch: both paths call the same r.* and the same repo handles the locking.
func ServeHTTPListener ¶ added in v0.3.1
func ServeHTTPListener(ctx context.Context, r *repo.Repo, _ *repo.Capturer, ln net.Listener, token string) error
ServeHTTPListener runs the HTTP API on an already-created listener. The hot-upgrade path needs this seam: the daemon owns the tcp listener so it can clear FD_CLOEXEC on it and hand the fd to the new image across execve, which rebuilds the listener with net.FileListener and calls this again — so an in-flight bind is never re-attempted and the port never flaps.
func ServeListener ¶ added in v0.3.1
func ServeListener(ctx context.Context, r *repo.Repo, c *repo.Capturer, ln net.Listener, gate *Gate, up UpgradeHandler) error
ServeListener serves the JSON protocol on an already-created listener. It does NOT remove/chmod/bind — the caller owns the listener's lifecycle. This is the seam the hot-upgrade path needs: the daemon builds the unix listener itself, hands ServeListener a shared Gate (so the upgrade coordinator can serialize against mutating ops) and an UpgradeHandler, and on re-exec the new image rebuilds the listener from the inherited fd and calls this again.
Types ¶
type Gate ¶ added in v0.3.1
Gate serializes mutating operations. The daemon shares ONE Gate between the socket accept loop (writer lock per mutating op) and the upgrade coordinator (which takes the writer lock right before it re-execs), so a hot upgrade can never fire mid-checkout. It embeds sync.RWMutex so read-only ops still run concurrently under RLock.
type UpgradeHandler ¶ added in v0.3.1
type UpgradeHandler interface {
// HandleUpgrade handles one "upgrade" request. It writes any result
// frame(s) via send. On a successful hot-upgrade it re-execs and NEVER
// returns; otherwise it returns after sending exactly one result frame.
HandleUpgrade(req protocol.Request, send func(protocol.Response) error)
}
UpgradeHandler processes the out-of-band "upgrade" op. It's an interface (not a concrete *upgrade.Coordinator) purely to break the api→upgrade→api import cycle: internal/upgrade imports this package for Gate, so api must not import it back.