Documentation
¶
Overview ¶
Package janus is a Caddy module that fronts disposable worker pools: cold Caddyfile capabilities on the data plane, a hot /1.0 control API on the control plane, and nothing durable in between.
Janus registers two Caddy modules: the app "janus" (process-wide control listeners and capability defaults, configured in the global options block) and the HTTP handler "http.handlers.janus" (per-site admission and capability overrides). Cold config admits capabilities; the hot registry wires tenants: apps register their hosts, publish their worker unix sockets, and heartbeat on /1.0, while Janus routes admitted requests host→upstream with doorbell-driven reloads that are invisible to clients.
Capabilities land in order: ping (1) proves the chassis, control (2) serves /1.0, cache (3) is a site-scoped micro-cache with request coalescing, hub (4) terminates WebSockets at the edge and fans JSON directive frames out per app while the tenant observes and steers over plain HTTP, mdns (5) advertises janus.local plus registered .local app hosts over multicast DNS and serves the read-only status front door, auth (6) is URL-prefix gates for auth-less apps: shared users, per-gate allow lists, one host-wide session, and Remote-User strip-and-inject on fall-through, and files (7) serves registered ordered roots and SPA shells with directory-gated site host patterns and trusted Rip-Site context. Sendfile (8) is an always-on reverse-proxy response protocol: a final application X-Sendfile instruction selects any regular file Janus can open, and Janus applies validators, ranges, cache recording, and streaming at the edge. Browse (9) turns selected hot and cold roots into navigable spaces with a content-addressed theme and bounded extension renderers. Access log (10) wraps Caddy's JSON encoder without changing durable bytes and publishes bounded app-scoped NDJSON through the control plane.
The registry, data plane, and hub state live in pooled process state (caddy.UsagePool), so a Caddy config reload never drops a registration or a hub socket; only registry DELETE, heartbeat TTL reap, or process exit tears them down. Everything is memory-only by contract — a restart empties the registry and tenants re-register.
The authoritative contracts live under docs/: the phased build spec, one page per capability, the Janus↔tenant pool protocol, and the performance ledger with raw bench provenance.
Index ¶
- Constants
- type AccessEncoder
- func (e *AccessEncoder) AddArray(key string, value zapcore.ArrayMarshaler) error
- func (e *AccessEncoder) AddBinary(key string, value []byte)
- func (e *AccessEncoder) AddBool(key string, value bool)
- func (e *AccessEncoder) AddByteString(key string, value []byte)
- func (e *AccessEncoder) AddComplex64(key string, value complex64)
- func (e *AccessEncoder) AddComplex128(key string, value complex128)
- func (e *AccessEncoder) AddDuration(key string, value time.Duration)
- func (e *AccessEncoder) AddFloat32(key string, value float32)
- func (e *AccessEncoder) AddFloat64(key string, value float64)
- func (e *AccessEncoder) AddInt(key string, value int)
- func (e *AccessEncoder) AddInt8(key string, value int8)
- func (e *AccessEncoder) AddInt16(key string, value int16)
- func (e *AccessEncoder) AddInt32(key string, value int32)
- func (e *AccessEncoder) AddInt64(key string, value int64)
- func (e *AccessEncoder) AddObject(key string, marshaler zapcore.ObjectMarshaler) error
- func (e *AccessEncoder) AddReflected(key string, value any) error
- func (e *AccessEncoder) AddString(key, value string)
- func (e *AccessEncoder) AddTime(key string, value time.Time)
- func (e *AccessEncoder) AddUint(key string, value uint)
- func (e *AccessEncoder) AddUint8(key string, value uint8)
- func (e *AccessEncoder) AddUint16(key string, value uint16)
- func (e *AccessEncoder) AddUint32(key string, value uint32)
- func (e *AccessEncoder) AddUint64(key string, value uint64)
- func (e *AccessEncoder) AddUintptr(key string, value uintptr)
- func (AccessEncoder) CaddyModule() caddy.ModuleInfo
- func (e *AccessEncoder) Cleanup() error
- func (e *AccessEncoder) Clone() zapcore.Encoder
- func (e *AccessEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error)
- func (e *AccessEncoder) OpenNamespace(key string)
- func (e *AccessEncoder) Provision(ctx caddy.Context) error
- func (e *AccessEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error
- type App
- type AppRecord
- type AuthGate
- type AuthSettings
- type AuthUser
- type BrowseEntry
- type BrowseLink
- type BrowsePage
- type BrowseRenderer
- type BrowseRoot
- type BrowseSettings
- type BrowseSiteSettings
- type CacheSettings
- type Control
- type FilesPolicy
- type FilesRoot
- type Handler
- type HubSettings
- type MdnsSettings
- type SitePolicy
- type Upstream
Constants ¶
const ( DefaultControlInternal = "run/janus.sock" DefaultControlLocal = "http://127.0.0.1:7600/" DefaultControlPublic = "https://0.0.0.0:7601/" )
Default listen targets when a control line omits the address.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AccessEncoder ¶ added in v1.5.0
type AccessEncoder struct {
caddylogging.LogEncoderConfig
// contains filtered or unexported fields
}
AccessEncoder preserves Caddy's JSON access log while publishing the bounded operator stream for entries carrying Janus request facts.
func (*AccessEncoder) AddArray ¶ added in v1.5.0
func (e *AccessEncoder) AddArray(key string, value zapcore.ArrayMarshaler) error
func (*AccessEncoder) AddBinary ¶ added in v1.5.0
func (e *AccessEncoder) AddBinary(key string, value []byte)
func (*AccessEncoder) AddBool ¶ added in v1.5.0
func (e *AccessEncoder) AddBool(key string, value bool)
func (*AccessEncoder) AddByteString ¶ added in v1.5.0
func (e *AccessEncoder) AddByteString(key string, value []byte)
func (*AccessEncoder) AddComplex64 ¶ added in v1.5.0
func (e *AccessEncoder) AddComplex64(key string, value complex64)
func (*AccessEncoder) AddComplex128 ¶ added in v1.5.0
func (e *AccessEncoder) AddComplex128(key string, value complex128)
func (*AccessEncoder) AddDuration ¶ added in v1.5.0
func (e *AccessEncoder) AddDuration(key string, value time.Duration)
func (*AccessEncoder) AddFloat32 ¶ added in v1.5.0
func (e *AccessEncoder) AddFloat32(key string, value float32)
func (*AccessEncoder) AddFloat64 ¶ added in v1.5.0
func (e *AccessEncoder) AddFloat64(key string, value float64)
func (*AccessEncoder) AddInt ¶ added in v1.5.0
func (e *AccessEncoder) AddInt(key string, value int)
func (*AccessEncoder) AddInt8 ¶ added in v1.5.0
func (e *AccessEncoder) AddInt8(key string, value int8)
func (*AccessEncoder) AddInt16 ¶ added in v1.5.0
func (e *AccessEncoder) AddInt16(key string, value int16)
func (*AccessEncoder) AddInt32 ¶ added in v1.5.0
func (e *AccessEncoder) AddInt32(key string, value int32)
func (*AccessEncoder) AddInt64 ¶ added in v1.5.0
func (e *AccessEncoder) AddInt64(key string, value int64)
func (*AccessEncoder) AddObject ¶ added in v1.5.0
func (e *AccessEncoder) AddObject(key string, marshaler zapcore.ObjectMarshaler) error
func (*AccessEncoder) AddReflected ¶ added in v1.5.0
func (e *AccessEncoder) AddReflected(key string, value any) error
func (*AccessEncoder) AddString ¶ added in v1.5.0
func (e *AccessEncoder) AddString(key, value string)
func (*AccessEncoder) AddTime ¶ added in v1.5.0
func (e *AccessEncoder) AddTime(key string, value time.Time)
func (*AccessEncoder) AddUint ¶ added in v1.5.0
func (e *AccessEncoder) AddUint(key string, value uint)
func (*AccessEncoder) AddUint8 ¶ added in v1.5.0
func (e *AccessEncoder) AddUint8(key string, value uint8)
func (*AccessEncoder) AddUint16 ¶ added in v1.5.0
func (e *AccessEncoder) AddUint16(key string, value uint16)
func (*AccessEncoder) AddUint32 ¶ added in v1.5.0
func (e *AccessEncoder) AddUint32(key string, value uint32)
func (*AccessEncoder) AddUint64 ¶ added in v1.5.0
func (e *AccessEncoder) AddUint64(key string, value uint64)
func (*AccessEncoder) AddUintptr ¶ added in v1.5.0
func (e *AccessEncoder) AddUintptr(key string, value uintptr)
func (AccessEncoder) CaddyModule ¶ added in v1.5.0
func (AccessEncoder) CaddyModule() caddy.ModuleInfo
func (*AccessEncoder) Cleanup ¶ added in v1.5.0
func (e *AccessEncoder) Cleanup() error
func (*AccessEncoder) Clone ¶ added in v1.5.0
func (e *AccessEncoder) Clone() zapcore.Encoder
func (*AccessEncoder) EncodeEntry ¶ added in v1.5.0
func (*AccessEncoder) OpenNamespace ¶ added in v1.5.0
func (e *AccessEncoder) OpenNamespace(key string)
func (*AccessEncoder) Provision ¶ added in v1.5.0
func (e *AccessEncoder) Provision(ctx caddy.Context) error
func (*AccessEncoder) UnmarshalCaddyfile ¶ added in v1.5.0
func (e *AccessEncoder) UnmarshalCaddyfile(d *caddyfile.Dispenser) error
type App ¶
type App struct {
// Control is the exact set of control-plane listeners serving the
// hot /1.0 API. Empty means one implicit internal (unix socket)
// listener.
Control []Control `json:"control,omitempty"`
// Ping is the global default for the site-scoped ping capability.
// Default: off. Sites may override.
Ping *bool `json:"ping,omitempty"`
// Cache is the global default and process-wide pool configuration
// for the site-scoped micro-cache. Default: off. Sites may override
// the per-site keys.
Cache *CacheSettings `json:"cache,omitempty"`
// Hub is the global default for the site-scoped hub capability
// (per-app WebSocket fan-out). Default: off. Sites may override.
Hub *HubSettings `json:"hub,omitempty"`
// Mdns is the process-wide LAN-presence capability: the advertised
// .local identity, per-app .local advertising, and the plain-HTTP
// front door. Default: off (nil).
Mdns *MdnsSettings `json:"mdns,omitempty"`
// Auth is the global default for the site-scoped auth wall (edge
// authentication for auth-less apps): default posture plus the
// default user set. Default: off. Sites may override.
Auth *AuthSettings `json:"auth,omitempty"`
// Files is the global default for the site-scoped registered-file
// service. Default: off. Sites may override.
Files *bool `json:"files,omitempty"`
// FilesPrecompressed is the process-wide ordered set of sidecar
// representations admitted by the global files capability. Site
// blocks may turn files on or off but cannot retune this order.
FilesPrecompressed []string `json:"files_precompressed,omitempty"`
// Browse is the process-wide theme and renderer configuration.
// Presence also enables the global site-scoped browse default.
Browse *BrowseSettings `json:"browse,omitempty"`
// HeartbeatTTL is how long a registered app may go without a
// heartbeat before its registration is reaped (same effect as
// DELETE). Default: 15s. The JANUS_HEARTBEAT_TTL environment
// variable is honored as a fallback when this is unset.
HeartbeatTTL caddy.Duration `json:"heartbeat_ttl,omitempty"`
// contains filtered or unexported fields
}
App is the process-wide Janus application (cold config).
func (*App) CaddyModule ¶
func (*App) CaddyModule() caddy.ModuleInfo
CaddyModule returns the Caddy module information.
func (*App) Cleanup ¶
Cleanup first tears down this generation's resources, including when Caddy rejects a started candidate without calling Stop. It then releases the app's reference on the pooled state; the last release (process shutdown) destructs it. A release that leaves other generations holding the state is either a successful reload's old generation retiring or an aborted reload's new generation being torn down — the advertiser tells them apart and rolls an aborted generation back to its surviving predecessor.
func (*App) Provision ¶
Provision sets up the app. Registry, data plane, and hub state come from the pooled process holder, so a config reload binds the new app to the same live state instead of constructing a split-brain registry.
type AppRecord ¶
type AppRecord struct {
ID string `json:"id"`
Name string `json:"name"`
Hosts []string `json:"hosts"`
Upstreams []Upstream `json:"upstreams"`
Site *SitePolicy `json:"site,omitempty"`
Files *FilesPolicy `json:"files,omitempty"`
Lease string `json:"lease"`
// Bridge is the tenant's hub bridge endpoint (optional; empty =
// hub handshakes answer 503). Cold config never carries it: which URL
// the tenant serves is tenant knowledge, exactly like socket paths.
Bridge string `json:"bridge,omitempty"`
// contains filtered or unexported fields
}
AppRecord is one registered app in the hot registry.
type AuthSettings ¶ added in v1.2.0
type AuthSettings struct {
// Enabled turns the wall on or off for the site. Default: off.
// Sites may override the global default; explicit off beats an
// inherited on. Bare auth / auth on sets Enabled true without
// Replace — inherit the global users and gates.
Enabled *bool `json:"enabled,omitempty"`
// Replace is true when a non-empty auth { … } block was parsed.
// Site Replace configs supply the entire effective auth config
// (users, gates, ttl) and do not merge with global.
Replace bool `json:"replace,omitempty"`
// Users is this level's credential table.
Users []AuthUser `json:"users,omitempty"`
// Gates is this level's path-prefix allow lists.
Gates []AuthGate `json:"gates,omitempty"`
// TTL is the sliding idle session timeout. Default: 8h.
TTL *caddy.Duration `json:"ttl,omitempty"`
}
AuthSettings configures the site-scoped auth wall. It appears in the global janus options (default posture) and per site (override). A non-empty auth { … } block at the site replaces the global config wholesale. The full contract is docs/20260728-160734-capability-auth.md.
type AuthUser ¶ added in v1.2.0
AuthUser is one cold-configured credential: a lowercased, header-safe username and its passhash blob.
type BrowseEntry ¶ added in v1.4.0
type BrowseLink ¶ added in v1.4.0
type BrowsePage ¶ added in v1.4.0
type BrowsePage struct {
Version int
Title string
Path string
RootName string
AssetBase string
Parent *BrowseLink
Breadcrumbs []BrowseLink
Entries []BrowseEntry
}
type BrowseRenderer ¶ added in v1.4.0
type BrowseRoot ¶ added in v1.4.0
type BrowseSettings ¶ added in v1.4.0
type BrowseSettings struct {
Theme string `json:"theme,omitempty"`
Timeout *caddy.Duration `json:"timeout,omitempty"`
MaxOutput *int64 `json:"max_output,omitempty"`
Concurrency *int `json:"concurrency,omitempty"`
Renderers []BrowseRenderer `json:"renderers,omitempty"`
// contains filtered or unexported fields
}
type BrowseSiteSettings ¶ added in v1.4.0
type BrowseSiteSettings struct {
Enabled *bool `json:"enabled,omitempty"`
Roots []BrowseRoot `json:"roots,omitempty"`
}
type CacheSettings ¶
type CacheSettings struct {
// Enabled turns the cache on or off for the site. Default: off.
// Sites may override the global default; explicit off beats an
// inherited on.
Enabled *bool `json:"enabled,omitempty"`
// TTL is the freshness window for cached responses when the origin
// sends no explicit lifetime. Default: 1s.
TTL *caddy.Duration `json:"ttl,omitempty"`
// TTLMax caps origin-declared lifetimes (s-maxage or max-age); a
// larger declared value is clamped to this. Default: 10s.
TTLMax *caddy.Duration `json:"ttl_max,omitempty"`
// MaxBody is the largest response body, in bytes, the cache stores;
// larger responses stream through uncached. Default: 262144 (256KiB).
MaxBody *int64 `json:"max_body,omitempty"`
// Debug adds an X-Janus-Cache header (HIT, MISS, BYPASS, …) to every
// response served on the site. Default: off.
Debug *bool `json:"debug,omitempty"`
// MaxBytes is the process-wide memory pool shared by every site's
// cache, in bytes. Global-only: illegal in a site block.
// Default: 67108864 (64MiB).
MaxBytes *int64 `json:"max_bytes,omitempty"`
// (1–100). Global-only: illegal in a site block. Default: 50.
MaxAppShare *int `json:"max_app_share,omitempty"`
}
CacheSettings configures the site-scoped micro-cache with request coalescing. It appears in the global janus options (default plus the process-wide pool knobs) and per site (override); unset keys cascade from the global settings, then built-in defaults. The full contract is docs/20260720-033201-capability-microcache.md.
type Control ¶
type Control struct {
// Mode is internal, local, or public.
Mode string `json:"mode,omitempty"`
// Listen is a unix path (internal) or http(s) URL (local/public).
Listen string `json:"listen,omitempty"`
// Token is the raw token:… suffix value (no "token:" prefix), if any.
Token string `json:"token,omitempty"`
// TokenKind is env, file, or literal.
TokenKind string `json:"token_kind,omitempty"`
// CertFile and KeyFile are the TLS certificate/key paths from the
// cert:… and key:… arguments. Both or neither; only meaningful on a
// TLS listener (public mode or an https:// listen). Unset, a TLS
// listener uses the committed dev pair certs/ripdev.io.{crt,key}.
CertFile string `json:"cert_file,omitempty"`
KeyFile string `json:"key_file,omitempty"`
// contains filtered or unexported fields
}
Control is one self-contained control-plane listener.
type FilesPolicy ¶ added in v1.3.0
type FilesPolicy struct {
Roots []FilesRoot `json:"roots"`
ProxyFirst []string `json:"proxy_first,omitempty"`
Shell string `json:"shell,omitempty"`
}
FilesPolicy declares ordered static roots, worker-first prefixes, and the independent SPA shell.
type FilesRoot ¶ added in v1.3.0
type FilesRoot struct {
Path string `json:"path"`
Cache string `json:"cache,omitempty"`
Browse bool `json:"browse"`
}
FilesRoot is one ordered root with a finite cache policy.
func (*FilesRoot) UnmarshalJSON ¶ added in v1.4.0
type Handler ¶
type Handler struct {
// Ping overrides the global ping default for this site when non-nil.
Ping *bool `json:"ping,omitempty"`
// Cache overrides the global cache default/tuning for this site when
// non-nil (process-wide keys are illegal here).
Cache *CacheSettings `json:"cache,omitempty"`
// Hub overrides the global hub default/tuning for this site when
// non-nil.
Hub *HubSettings `json:"hub,omitempty"`
// Auth overrides the global auth default for this site when non-nil.
// A non-empty auth { … } block replaces the global config wholesale.
Auth *AuthSettings `json:"auth,omitempty"`
// Files overrides the global registered-file service default for this
// site when non-nil.
Files *bool `json:"files,omitempty"`
// Browse overrides the global browse admission default and may carry
// persistent cold roots for this exact-host site.
Browse *BrowseSiteSettings `json:"browse,omitempty"`
// contains filtered or unexported fields
}
Handler is the site-level data-plane admission module.
func (Handler) CaddyModule ¶
func (Handler) CaddyModule() caddy.ModuleInfo
CaddyModule returns the Caddy module information.
func (*Handler) ServeHTTP ¶
ServeHTTP handles admitted requests: on the plain-HTTP port with the mdns front door in shared mode, the handler is the front-door decider (mine serves the front door, not-mine passes through to the next route on the same server — the auto-HTTPS redirects — never 421); everywhere else, site-scoped /ping answers first when enabled and everything else routes through the data plane (registry hosts → upstreams; unknown hosts → 404).
type HubSettings ¶
type HubSettings struct {
// Enabled turns the hub on or off for the site. Default: off.
// Sites may override the global default; explicit off beats an
// inherited on.
Enabled *bool `json:"enabled,omitempty"`
// Mode selects tenant-observed bridge admission or edge-only direct
// admission. Default: bridge.
Mode *string `json:"mode,omitempty"`
// Path is the WebSocket endpoint path; only upgrade requests to it
// are intercepted. Must start with "/" and contain no "?" or "#".
// Default: "/hub".
Path *string `json:"path,omitempty"`
// MaxConns caps concurrent hub connections per app. An app spanning
// several hub-enabled hosts is admitted against the minimum effective
// value across all of them. Default: 4096.
MaxConns *int `json:"max_conns,omitempty"`
// MaxFrame is the largest client frame, in bytes; larger frames close
// the connection (1009). Minimum: 1024 (1KiB). Default: 65536 (64KiB).
MaxFrame *int64 `json:"max_frame,omitempty"`
// MaxChannels caps channels one connection may join. Default: 128.
MaxChannels *int `json:"max_channels,omitempty"`
// Origin is the browser Origin policy: "same" (Origin host must
// equal the request Host), "any" (no check — for token-authenticated
// non-browser clients), or "same" plus allowlisted hostnames, or
// hostnames alone. Failure answers 403 before any tenant contact.
// Default: same.
Origin []string `json:"origin,omitempty"`
}
HubSettings configures the site-scoped hub: per-app WebSocket fan-out terminated at the edge. It appears in the global janus options (default) and per site (override); unset keys cascade from the global settings, then built-in defaults. The full contract is docs/20260720-162350-hub-design.md.
type MdnsSettings ¶ added in v1.1.0
type MdnsSettings struct {
// Name is the advertised mDNS name: exactly one label plus ".local".
// Default: "janus.local".
Name string `json:"name,omitempty"`
// Canonical is the https:// origin the front door hands off to
// (client-side probe + redirect, diagnostic mode on failure).
// Origin only — no path, query, fragment, or userinfo; never an IP
// literal; never a .local name. Default: unset (plain dashboard).
Canonical string `json:"canonical,omitempty"`
// Interfaces pins advertising to exactly these interfaces. Default:
// unset — the live multicast interface set with the loopback and
// IPv4 link-local block list applied.
Interfaces []string `json:"interface,omitempty"`
// Apps controls per-app `.local` advertising. Default: on.
Apps *bool `json:"apps,omitempty"`
// Listen selects the front door's mode. Unset (the default) is
// shared mode: the front door rides inside the HTTP app's plain-HTTP
// port server behind a normal site block (http://*.local { janus })
// and the janus site handler decides per request. Set ("[host]:port")
// is dedicated mode: Janus opens its own listener at that address
// with the strict Host allowlist.
Listen string `json:"listen,omitempty"`
// contains filtered or unexported fields
}
MdnsSettings configures the process-wide mdns capability: the advertised `.local` identity, per-app advertising, and the plain-HTTP front door. The full contract is docs/20260722-034619-capability-mdns.md.
type SitePolicy ¶ added in v1.3.0
type SitePolicy struct {
Host string `json:"host"`
Dir string `json:"dir"`
Aliases map[string]string `json:"aliases,omitempty"`
}
SitePolicy declares one directory-gated host pattern and its exact aliases.
type Upstream ¶
type Upstream struct {
// Path is the unix socket path Janus may dial.
Path string `json:"path"`
// Doorbell marks the tenant's wake-up socket. A doorbell entry must
// be the only entry in the list. Phase 3 stores and validates the
// flag; ringing is data-plane behavior (Phase 4).
Doorbell bool `json:"doorbell,omitempty"`
}
Upstream is one entry in an app's upstream list.
Source Files
¶
- access.go
- access_encoder.go
- access_stream.go
- access_writer.go
- app.go
- apps.go
- auth.go
- auth_cmd.go
- auth_config.go
- browse_config.go
- browse_process_unix.go
- browse_serve.go
- browse_state.go
- cache.go
- cache_config.go
- cache_serve.go
- caddyfile.go
- cascade.go
- control.go
- control_api.go
- control_auth.go
- control_hub.go
- control_mdns.go
- dataplane.go
- doc.go
- files_config.go
- files_serve.go
- handler.go
- hub.go
- hub_bridge.go
- hub_config.go
- hub_conn.go
- hub_frame.go
- hub_ws.go
- mdns.go
- mdns_config.go
- ring.go
- sendfile.go
- sendfile_open_unix.go
- sendfile_transport.go
- state.go
Directories
¶
| Path | Synopsis |
|---|---|
|
testkit is the acceptance-suite support binary for test.sh: fixture HTTP servers on unix sockets, an RFC 6455 WebSocket driver, and small shell utilities.
|
testkit is the acceptance-suite support binary for test.sh: fixture HTTP servers on unix sockets, an RFC 6455 WebSocket driver, and small shell utilities. |