agent

package
v0.0.0-...-d62396e Latest Latest
Warning

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

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package agent implements hope's remote-host model. A hope-agent runs on a remote Docker host and dials OUT to a hope hub (so the remote needs only outbound access — no inbound ports, no VPN). Over that single connection it multiplexes (yamux) the host's Docker socket back to hope: hope opens a stream per Docker request and the agent proxies each to the local daemon. So hope's entire existing Docker layer works against the remote host unchanged.

Index

Constants

View Source
const ReversePort = 8790

ReversePort is the port the agent listens on (inside its container) for a co-located plugin's reverse-channel HTTP, reachable by the agent's container id once hope attaches the agent to the ink-plugins network. Shared by the agent listener and the callback URL hope hands an agent-hosted plugin.

Variables

This section is empty.

Functions

func Run

func Run(ctx context.Context, opts Options) error

Run connects to a hope hub and serves the local Docker socket over the tunnel until ctx is cancelled, reconnecting on drop.

Types

type AgentInfo

type AgentInfo struct {
	Version     string `json:"version"`
	Revision    string `json:"revision"`
	GoVersion   string `json:"go_version"`
	Platform    string `json:"platform"`
	BuildTime   string `json:"build_time"`
	ContainerID string `json:"container_id"` // the agent's own container id (for self-recreate)
}

AgentInfo is the remote agent's build metadata, reported in the handshake.

type FrontServer

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

FrontServer is a sov gateway.Server that fronts hope's HTTP listener so the agent tunnel can ride the SAME port as the UI/API (so it traverses Cloudflare on 443 with no extra port). It claims one path for the WebSocket tunnel and hands every other request to sov's dispatch untouched.

The non-tunnel bridge mirrors sov's default NetHTTPServer: a 4 MiB body cap, stripping inbound X-Sov-* identity headers (hope faces the public internet, so it must not trust smuggled identity claims), and streaming responses with a per-chunk flush and no write deadline.

func NewFrontServer

func NewFrontServer(hub *Hub, wsPath string) *FrontServer

NewFrontServer returns a Server that serves the agent WebSocket at wsPath and forwards everything else to the gateway.

func (*FrontServer) Handle

func (s *FrontServer) Handle(h gateway.RequestHandler)

Handle records the gateway's request handler (sov calls this once at boot).

func (*FrontServer) ListenAndServe

func (s *FrontServer) ListenAndServe(ctx context.Context, addr string) error

ListenAndServe binds addr and serves until ctx is cancelled.

type Host

type Host struct {
	ID          string
	Docker      *docker.Client
	Remote      string
	ConnectedAt time.Time
	Info        AgentInfo
	// contains filtered or unexported fields
}

Host is a connected remote Docker host, exposed to hope as a normal docker.Client that happens to run over the agent tunnel.

func (*Host) DialContainer

func (h *Host) DialContainer(addr string) (net.Conn, error)

DialContainer opens a tunnel stream that the agent connects to addr (an ip:port on the agent's host) and returns it as a net.Conn. Requires a stream-type-capable agent.

type HostInfo

type HostInfo struct {
	ID          string    `json:"id"`
	Remote      string    `json:"remote"`
	ConnectedAt time.Time `json:"connected_at"`
	Info        AgentInfo `json:"info"`
}

HostInfo is the frontend-facing summary of a connected agent.

type Hub

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

Hub accepts agent connections and registers each as a Host.

func NewHub

func NewHub(token, configPath string, log Logger) *Hub

NewHub builds a hub. token is the shared enrollment secret; configPath is the docker config.json for registry creds applied to remote pulls.

func (*Hub) DialContainer

func (h *Hub) DialContainer(hostID, addr string) (net.Conn, error)

DialContainer opens a stream to a connected host's agent that connects to addr (ip:port) on that host, for reaching a plugin container over the tunnel.

func (*Hub) Listen

func (h *Hub) Listen(ctx context.Context, addr string) error

Listen accepts agents on addr until ctx is cancelled.

func (*Hub) OnConnect

func (h *Hub) OnConnect(fn func(ctx context.Context, host *Host))

OnConnect registers a callback run for each agent once it's online, with a context cancelled when that agent disconnects. hope uses it to start the host's background jobs (registry creds, update + disk crawlers) so a remote host gets the same periodic work as the local daemon.

func (*Hub) Registry

func (h *Hub) Registry() *Registry

Registry exposes the live hosts for routing.

func (*Hub) ServeWS

func (h *Hub) ServeWS(ctx context.Context) http.HandlerFunc

ServeWS upgrades an HTTP request to a WebSocket and runs the agent tunnel over it, so the agent can reach the hub on hope's main HTTPS port (through Cloudflare) with no extra port. Cloudflare Access auth (service token or a bypass policy) is enforced at the edge before the request arrives; the shared token in the handshake is the second factor. ctx bounds the tunnel lifetime.

func (*Hub) SetBus

func (h *Hub) SetBus(bus *events.Bus)

SetBus wires the event bus after construction (the hub is built before the bus in serve.go). Safe to leave unset — publishing on a nil bus is a no-op.

func (*Hub) SetReverseTarget

func (h *Hub) SetReverseTarget(addr string)

SetReverseTarget wires hope's own gateway address (loopback, e.g. 127.0.0.1:8080) that relayed plugin reverse-channel calls from agent-hosted plugins are piped to. Empty (unset) leaves the reverse-over-agent path off — the hub won't advertise the `reverse` capability, so agents stay co-located-only for the reverse channel.

type Logger

type Logger interface {
	Info(msg string, kv ...any)
	Warn(msg string, kv ...any)
	Error(msg string, kv ...any)
}

Logger is the small logging surface the agent/hub need (hope's logger fits).

type Options

type Options struct {
	// Connect is the hub endpoint. A ws://host/path or wss://host/path URL rides
	// hope's main HTTPS port through Cloudflare (no extra port); a bare host:port
	// or tcp://host:port uses a raw TCP hub listener (LAN/overlay).
	Connect string
	Token   string // shared enrollment secret the hub checks
	HostID  string // stable id this host registers under
	Docker  string // local docker endpoint to expose (unix:// or tcp://)
	// CFAccessClientID/Secret, when set, are sent as the Cloudflare Access
	// service-token headers so the agent passes Access as a machine identity
	// (leave empty if you instead bypass Access for the agent path).
	CFAccessClientID     string
	CFAccessClientSecret string
	Log                  Logger
}

Options configures a hope-agent.

type Registry

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

Registry tracks the live agents. Safe for concurrent use.

func (*Registry) Get

func (r *Registry) Get(id string) *docker.Client

Get returns the docker client for a host id, or nil.

func (*Registry) Host

func (r *Registry) Host(id string) *Host

Host returns the live Host for an id (build info, connection), or nil.

func (*Registry) List

func (r *Registry) List() []HostInfo

List returns the connected hosts (id + when + build info), newest-stable order.

Jump to

Keyboard shortcuts

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