caddy

package
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: May 19, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func InFluxPortRouteID added in v0.2.1

func InFluxPortRouteID(id string, port int) string

func InFluxSandboxRouteID added in v0.2.1

func InFluxSandboxRouteID(id string) string

IDs exposed for the zombie GC to add to its keep-set.

func IngressPortSNIRouteID added in v0.2.1

func IngressPortSNIRouteID(id string, port int) string

func IngressSandboxSNIRouteID added in v0.2.1

func IngressSandboxSNIRouteID(id string) string

Types

type Client

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

func New

func New(cfg config.Config) *Client

func (*Client) DeleteInFluxPortRoute added in v0.2.1

func (c *Client) DeleteInFluxPortRoute(ctx context.Context, id string, port int) error

DeleteInFluxPortRoute mirrors DeleteInFluxSandboxRoute for per-port URLs.

func (*Client) DeleteInFluxSandboxRoute added in v0.2.1

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

DeleteInFluxSandboxRoute drops the in-flux 503 route once the live route is back. 404 is treated as success.

func (*Client) DeletePortRoute

func (c *Client) DeletePortRoute(ctx context.Context, id string, port int) error

func (*Client) DeleteRouteByID added in v0.1.4

func (c *Client) DeleteRouteByID(ctx context.Context, routeID string) error

DeleteRouteByID is the zombie-GC entry point: the reconcile sweep finds an @id under apps/http or the tls-mux server that doesn't correspond to any sandbox row, and calls this to drop it. Wraps the same DELETE /id/<routeID> the typed helpers use, so 404 is still treated as success.

func (*Client) DeleteSandboxRoute

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

func (*Client) DeleteTCPRoute added in v0.1.4

func (c *Client) DeleteTCPRoute(ctx context.Context, hostPort int) error

DeleteTCPRoute removes the layer4 server holding hostPort. 404 is treated as success — the desired post-condition is "not present", and it isn't.

func (*Client) DeleteTCPServer added in v0.1.4

func (c *Client) DeleteTCPServer(ctx context.Context, serverID string) error

DeleteTCPServer drops a layer4 server by its name (e.g. tcp-port-37412). Used by reconcile's zombie GC; not tied to a specific sandbox/port pair so the caller doesn't have to know which exposure originally owned the port.

func (*Client) DeleteTLSSNIRoute added in v0.1.4

func (c *Client) DeleteTLSSNIRoute(ctx context.Context, id string, port int) error

DeleteTLSSNIRoute removes one SNI route by @id. 404 is treated as success for the same reason DeleteSandboxRoute does.

func (*Client) Enabled

func (c *Client) Enabled() bool

func (*Client) EnsureLayer4 added in v0.1.4

func (c *Client) EnsureLayer4(ctx context.Context, tlsListen, tlsFallback string) error

EnsureLayer4 idempotently bootstraps the layer4 app and (when tlsListen is non-empty) the shared SNI-mux server. Safe to call on every sandboxd start — the admin API treats a no-op POST as 200 and a PATCH on a missing key as 404, so we issue a PUT only when the path actually doesn't exist.

Without this bootstrap, the very first UpsertTCPRoute would fail because /config/apps/layer4 doesn't exist yet on a fresh Caddy.

When tlsListen is non-empty, tlsFallback must point at the local HTTPS listener that owned the same port before caddy-l4 took it over (the API site, the on-demand-TLS catch-all, etc.). caddy-l4 routes by SNI for sandbox subdomains and forwards the rest of the traffic — including ACME HTTP-01 cert validation that piggy-backs on :443 ALPN and any non-sandbox hostname — to the fallback. Empty tlsFallback with non-empty tlsListen is rejected; the service layer surfaces it as a config error at boot.

func (*Client) L4TLSFallback added in v0.1.4

func (c *Client) L4TLSFallback() string

L4TLSFallback returns the address caddy-l4 forwards non-sandbox SNI to (the regular HTTPS Caddy site that handles the API, on-demand TLS, and the 404 catch-all). Meaningful only when L4TLSListen is non-empty.

func (*Client) L4TLSListen added in v0.1.4

func (c *Client) L4TLSListen() string

L4TLSListen returns the listen address configured for the shared TLS-SNI multiplexer. Empty means TLS-SNI exposure is disabled — the service layer should reject protocol="tls" requests and skip EnsureLayer4 of the mux.

func (*Client) Ping

func (c *Client) Ping(ctx context.Context) error

func (*Client) PortPublicURL

func (c *Client) PortPublicURL(id string, port int) string

func (*Client) PublicHost added in v0.1.6

func (c *Client) PublicHost() string

PublicHost returns the dial target for raw-TCP exposures. In domain mode the base domain is returned so TCP URLs are consistent with HTTP sandbox URLs; otherwise falls back to the configured publicHost IP.

func (*Client) SNIHost added in v0.1.4

func (c *Client) SNIHost(id string, port int) string

SNIHost is the per-sandbox subdomain caddy-l4 routes by for a TLS exposure. Returns empty in IP mode (no domain configured), which the service layer uses to detect "TLS not supported in this deployment" without sniffing.

func (*Client) SandboxPublicURL

func (c *Client) SandboxPublicURL(id string) string

func (*Client) Snapshot added in v0.1.4

func (c *Client) Snapshot(ctx context.Context) (Snapshot, error)

func (*Client) TCPPublicEndpoint added in v0.1.4

func (c *Client) TCPPublicEndpoint(hostPort int) string

TCPPublicEndpoint returns the URL clients dial for a raw TCP exposure allocated at hostPort. In domain mode the base domain is used as the dial target (e.g. tcp://sandbox.aerol.cloud:22534); otherwise falls back to publicHost.

func (*Client) TLSPublicEndpoint added in v0.1.4

func (c *Client) TLSPublicEndpoint(id string, port int, l4Listen string) string

TLSPublicEndpoint returns the dial target for a TLS-SNI multiplexed exposure. The host portion is the per-sandbox subdomain caddy-l4 uses for SNI matching (so the certificate the upstream presents must cover that name); the port is the layer4 listener address operators configured. Empty l4Listen means TLS-SNI mode is disabled.

func (*Client) UpsertInFluxPortRoute added in v0.2.1

func (c *Client) UpsertInFluxPortRoute(ctx context.Context, id string, port int) error

UpsertInFluxPortRoute mirrors UpsertInFluxSandboxRoute for per-port URLs.

func (*Client) UpsertInFluxSandboxRoute added in v0.2.1

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

UpsertInFluxSandboxRoute installs an HTTP route for the sandbox's public hostname/path that responds with 503 Service Unavailable + Retry-After: 2. Used by the cluster-ingress reconciler when a placement is orphaned or when the owner's data-plane host hasn't gossiped yet — the alternative is the Caddy fallback 404, which clients can't tell apart from "sandbox does not exist". The route has its own @id namespace (suffix "-in-flux") so it can coexist with a live route during transitions; the reconciler is responsible for deleting the live route before installing the in-flux route, and vice versa.

In domain mode the L4 SNI mux falls through to the local HTTPS listener for any SNI it doesn't recognize, so an HTTP route registered for the sandbox hostname captures in-flux traffic without needing a separate L4 route.

func (*Client) UpsertPortRoute

func (c *Client) UpsertPortRoute(ctx context.Context, id, containerIP string, port int) error

func (*Client) UpsertPortRouteToPeer added in v0.2.1

func (c *Client) UpsertPortRouteToPeer(ctx context.Context, id string, port int, peerHost string) error

UpsertPortRouteToPeer installs the IP/path-mode per-port route for a sandbox owned by another node. In domain mode this is handled by SNI pass-through routes in the layer4 mux.

func (*Client) UpsertSNIPassthroughRoute added in v0.2.1

func (c *Client) UpsertSNIPassthroughRoute(ctx context.Context, routeID, sniHost, peerHost string, peerPort int) error

UpsertSNIPassthroughRoute publishes a layer4 SNI route that does not terminate TLS. Non-owner ingress nodes use this to forward domain-mode sandbox hosts to the owner node's :443 mux, preserving the original ClientHello and letting the owner perform the normal local routing.

func (*Client) UpsertSandboxRoute

func (c *Client) UpsertSandboxRoute(ctx context.Context, id, containerIP string, toolboxPort int) error

func (*Client) UpsertSandboxRouteToPeer added in v0.2.1

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

UpsertSandboxRouteToPeer installs the IP/path-mode ingress route for a sandbox owned by another node. Domain-mode clusters use caddy-l4 SNI pass-through instead, because the local HTTPS app sits behind the :443 layer4 mux and remote proxying would require dynamic upstream TLS SNI.

func (*Client) UpsertTCPProxyRoute added in v0.2.1

func (c *Client) UpsertTCPProxyRoute(ctx context.Context, id string, port, hostPort int, peerHost string, peerPort int) error

UpsertTCPProxyRoute creates a raw-TCP ingress server bound to hostPort that forwards to another node's hostPort. This is the non-owner half of stable cluster TCP exposure: every node can accept tcp://cluster-host:hostPort, but only the owner forwards from hostPort to the container.

func (*Client) UpsertTCPRoute added in v0.1.4

func (c *Client) UpsertTCPRoute(ctx context.Context, id, containerIP string, port, hostPort int) error

UpsertTCPRoute creates (or replaces) the layer4 server bound to hostPort. One server per host-port allocation; PUT replaces the entire server config in place, so re-running with a different upstream IP after a sandbox restart is the right way to refresh routing without poking at routes/0.

func (*Client) UpsertTLSSNIRoute added in v0.1.4

func (c *Client) UpsertTLSSNIRoute(ctx context.Context, id, sniHost, containerIP string, port int) error

UpsertTLSSNIRoute publishes (or refreshes) one SNI route inside the shared tls-mux layer4 server. PATCH /id/<routeID> replaces the existing route in place without disturbing siblings; if the @id isn't there yet (404) we PUT at routes/0 so SNI matching tries it ahead of any future fallback.

The handler chain is [tls, proxy]: caddy-l4 terminates TLS using Caddy's own cert manager (which already holds the wildcard for *.$DOMAIN, issued once at startup via DNS-01), then proxies the now-plaintext bytes to the container on its native port. The container speaks raw TCP — no cert, no private key, nothing TLS-related lives inside the user's sandbox. The connection_policies entry is intentionally empty: Caddy picks the cert by SNI from the shared cert manager, so there is no per-route cert config.

Caller must have already called EnsureLayer4 with a non-empty tlsListen at least once; otherwise the routes/0 PUT will land on a missing server.

type Snapshot added in v0.1.4

type Snapshot struct {
	// HTTPRouteIDs are the @ids of routes under apps/http that match our
	// "sandbox-..." prefix. Both the per-sandbox toolbox routes and the
	// per-port HTTP routes show up here.
	HTTPRouteIDs []string
	// L4TCPServerIDs are server names under apps/layer4 of the form
	// tcp-port-<hostPort>. Each maps 1:1 to a host-port allocation in the DB.
	L4TCPServerIDs []string
	// L4TLSRouteIDs are @ids of SNI routes inside the tls-mux server.
	L4TLSRouteIDs []string
}

Snapshot is the read side of reconcile's zombie-route detection. It walks the live Caddy config once and returns every entity whose name follows our conventions, so the service layer can compare against the DB and delete anything that has no matching row.

Jump to

Keyboard shortcuts

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