hostproxy

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Mar 26, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package hostproxy provides a host-side HTTP server that containers can call to perform actions on the host, such as opening URLs in the browser.

Package hostproxy provides a host-side HTTP server that containers can call to perform actions on the host, such as opening URLs in the browser.

Index

Constants

View Source
const CallbackSessionType = "callback"

CallbackSessionType is the session type identifier for callback sessions.

View Source
const DefaultCallbackTTL = 5 * time.Minute

DefaultCallbackTTL is the default time-to-live for callback sessions.

View Source
const SessionIDLength = 16

SessionIDLength is the number of random bytes used for session IDs. 16 bytes = 32 hex characters, providing 128 bits of entropy.

Variables

View Source
var ErrCallbackAlreadyReceived = errors.New("callback already received")

ErrCallbackAlreadyReceived is returned when attempting to capture a callback for a session that has already received one. TODO: this is being printed over the claude REPL when its active. We have to figure out how to print errors without overwriting onto the REPL ui. either supress the printing of this if claude is active, or find a way to print above/below the REPL.

Functions

func GetDaemonPID

func GetDaemonPID(pidFile string) int

GetDaemonPID returns the PID of the running daemon, or 0 if not running.

func IsDaemonRunning

func IsDaemonRunning(pidFile string) bool

IsDaemonRunning checks if a daemon is running by checking the PID file and verifying the process is alive.

func StopDaemon

func StopDaemon(pidFile string) error

StopDaemon sends SIGTERM to the daemon process.

Types

type CallbackChannel

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

CallbackChannel handles OAuth callback routing. It manages the registration of callback expectations and captures incoming callbacks from the browser.

func NewCallbackChannel

func NewCallbackChannel(store *SessionStore, log *logger.Logger) *CallbackChannel

NewCallbackChannel creates a new callback channel using the provided session store.

func (*CallbackChannel) Capture

func (c *CallbackChannel) Capture(sessionID string, r *http.Request) error

Capture stores the incoming callback request data for the specified session. Only the first callback for each session is captured (single-use). Returns an error if the session is not found or already received a callback.

func (*CallbackChannel) Delete

func (c *CallbackChannel) Delete(sessionID string)

Delete removes a callback session.

func (*CallbackChannel) GetData

func (c *CallbackChannel) GetData(sessionID string) (*CallbackData, bool)

GetData retrieves the captured callback data for the specified session. Returns nil if no callback has been received yet.

func (*CallbackChannel) GetPath

func (c *CallbackChannel) GetPath(sessionID string) (string, bool)

GetPath returns the expected callback path for the specified session.

func (*CallbackChannel) GetPort

func (c *CallbackChannel) GetPort(sessionID string) (int, bool)

GetPort returns the target port for the specified session.

func (*CallbackChannel) IsReceived

func (c *CallbackChannel) IsReceived(sessionID string) bool

IsReceived checks if a callback has been received for the session.

func (*CallbackChannel) Register

func (c *CallbackChannel) Register(port int, path string, ttl time.Duration) (*Session, error)

Register creates a new callback session expecting a callback on the specified port and path. Returns the session for use in URL rewriting.

type CallbackData

type CallbackData struct {
	Method     string            `json:"method"`
	Path       string            `json:"path"`
	Query      string            `json:"query"`
	Headers    map[string]string `json:"headers,omitempty"`
	Body       string            `json:"body,omitempty"`
	ReceivedAt time.Time         `json:"received_at"`
}

CallbackData contains the captured OAuth callback request data.

type ContainerLister

type ContainerLister interface {
	ContainerList(ctx context.Context, options client.ContainerListOptions) (client.ContainerListResult, error)
	io.Closer
}

ContainerLister is the minimal interface needed for container watcher functionality. This allows the daemon to work without directly coupling to the full Docker client, and enables testing with mock implementations.

Note: This package imports github.com/moby/moby/client directly rather than going through pkg/whail. This is intentional because the daemon runs as a standalone subprocess that only needs to list containers - it doesn't need whail's jail semantics or label enforcement. The interface pattern still allows for testing.

type Daemon

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

Daemon manages the host proxy server as a background process. It polls Docker for clawker containers and auto-exits when none are running.

func NewDaemon

func NewDaemon(cfg config.Config, log *logger.Logger, opts ...DaemonOption) (*Daemon, error)

NewDaemon creates a new daemon that reads all settings from cfg.HostProxyConfig(). Optional DaemonOption values override individual config settings (used by CLI flags). It creates a Docker client internally. Tests that need a mock docker client construct &Daemon{...} directly (the pattern used by watchContainers tests).

func (*Daemon) Run

func (d *Daemon) Run(ctx context.Context) error

Run starts the daemon and blocks until it receives a signal or auto-exits.

type DaemonOption added in v0.2.0

type DaemonOption func(*Daemon)

DaemonOption is a functional option for overriding daemon config values. CLI flags use these to take precedence over config without mutating the config object.

func WithDaemonPort added in v0.2.0

func WithDaemonPort(port int) DaemonOption

WithDaemonPort overrides the daemon listen port.

func WithGracePeriod added in v0.2.0

func WithGracePeriod(period time.Duration) DaemonOption

WithGracePeriod overrides the initial grace period.

func WithPollInterval added in v0.2.0

func WithPollInterval(interval time.Duration) DaemonOption

WithPollInterval overrides the container poll interval.

type HostProxyService added in v0.1.2

type HostProxyService interface {
	// EnsureRunning ensures the host proxy is running. Spawns a daemon if needed.
	EnsureRunning() error
	// IsRunning returns whether the host proxy is currently running.
	IsRunning() bool
	// ProxyURL returns the URL containers should use to reach the host proxy.
	ProxyURL() string
}

HostProxyService is the interface for host proxy operations used by container commands. Commands interact with the host proxy through this interface, enabling test doubles that don't spawn daemon subprocesses.

Concrete implementation: Manager. Mock: hostproxytest.MockManager.

type Manager

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

Manager manages the lifecycle of the host proxy daemon. It spawns a daemon subprocess that persists beyond CLI lifetime, enabling containers to use the proxy even after the CLI exits.

func NewManager

func NewManager(cfg config.Config, log *logger.Logger) (*Manager, error)

NewManager creates a new host proxy manager. Port is read and validated from cfg.HostProxyConfig().Manager.Port at construction time.

func (*Manager) EnsureRunning

func (m *Manager) EnsureRunning() error

EnsureRunning ensures the host proxy daemon is running. If the daemon is not running, it spawns a new daemon subprocess.

func (*Manager) IsRunning

func (m *Manager) IsRunning() bool

IsRunning returns whether the host proxy daemon is running.

func (*Manager) Port

func (m *Manager) Port() int

Port returns the port the host proxy daemon is configured to use.

func (*Manager) ProxyURL

func (m *Manager) ProxyURL() string

ProxyURL returns the URL containers should use to reach the host proxy. This uses host.docker.internal which Docker automatically resolves to the host.

func (*Manager) Stop

func (m *Manager) Stop()

Stop does nothing for the daemon-based manager. The daemon self-terminates when no clawker containers are running. Use StopDaemon() for explicit daemon teardown.

func (*Manager) StopDaemon

func (m *Manager) StopDaemon() error

StopDaemon explicitly stops the daemon process.

type Server

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

Server is an HTTP server that handles requests from containers to perform host-side actions.

func NewServer

func NewServer(port int, log *logger.Logger) *Server

NewServer creates a new host proxy server on the specified port.

func (*Server) IsRunning

func (s *Server) IsRunning() bool

IsRunning returns whether the server is currently running.

func (*Server) Port

func (s *Server) Port() int

Port returns the port the server is configured to use.

func (*Server) Start

func (s *Server) Start() error

Start starts the HTTP server in a goroutine. It listens on both IPv4 (127.0.0.1) and IPv6 ([::1]) loopback addresses to support containers that resolve host.docker.internal to either protocol.

func (*Server) Stop

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

Stop gracefully shuts down the server and cleans up resources.

type Session

type Session struct {
	ID        string
	Type      string // e.g., "callback", "webhook", "message"
	CreatedAt time.Time
	ExpiresAt time.Time
	Metadata  map[string]any // Channel-specific data
	// contains filtered or unexported fields
}

Session is the base type for all proxy sessions. It stores common metadata and provides thread-safe access to session data.

func (*Session) CaptureOnce

func (s *Session) CaptureOnce(receivedKey string) bool

CaptureOnce atomically checks if capture happened and marks it if not. Returns true if this call captured (was first), false if already captured.

func (*Session) GetMetadata

func (s *Session) GetMetadata(key string) (any, bool)

GetMetadata safely retrieves a metadata value by key.

func (*Session) IsExpired

func (s *Session) IsExpired() bool

IsExpired returns true if the session has passed its expiration time.

func (*Session) SetMetadata

func (s *Session) SetMetadata(key string, value any)

SetMetadata safely sets a metadata value.

type SessionStore

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

SessionStore manages sessions across all channels. It provides thread-safe create, get, delete, and cleanup operations.

func NewSessionStore

func NewSessionStore() *SessionStore

NewSessionStore creates a new session store and starts the background cleanup goroutine. Callers MUST call Stop() when done to prevent goroutine leaks.

func (*SessionStore) Count

func (s *SessionStore) Count() int

Count returns the number of active sessions.

func (*SessionStore) Create

func (s *SessionStore) Create(sessionType string, ttl time.Duration, metadata map[string]any) (*Session, error)

Create creates a new session with the given type, TTL, and metadata. Returns the created session with a unique cryptographically random ID.

func (*SessionStore) Delete

func (s *SessionStore) Delete(id string)

Delete removes a session by ID.

func (*SessionStore) Get

func (s *SessionStore) Get(id string) *Session

Get retrieves a session by ID. Returns nil if not found or expired.

func (*SessionStore) SetOnDelete

func (s *SessionStore) SetOnDelete(fn func(session *Session))

SetOnDelete sets a callback function that will be called when a session is deleted. The callback receives the session before it is removed from the store.

func (*SessionStore) Stop

func (s *SessionStore) Stop()

Stop stops the background cleanup goroutine.

Directories

Path Synopsis
Package internals provides embedded container-side scripts and source code that run inside clawker containers to communicate with the host proxy and socketbridge.
Package internals provides embedded container-side scripts and source code that run inside clawker containers to communicate with the host proxy and socketbridge.
cmd/callback-forwarder command
callback-forwarder polls the host proxy for captured OAuth callback data and forwards it to the local HTTP server (Claude Code's callback listener).
callback-forwarder polls the host proxy for captured OAuth callback data and forwards it to the local HTTP server (Claude Code's callback listener).
cmd/clawker-socket-server command
socket-forwarder is a multiplexing socket forwarder that runs inside clawker containers.
socket-forwarder is a multiplexing socket forwarder that runs inside clawker containers.

Jump to

Keyboard shortcuts

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