server

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jul 16, 2026 License: LGPL-2.1 Imports: 54 Imported by: 0

README

mod/server

mod/server is the inbound serving layer. It accepts web and Yggdrasil HTTP traffic, applies ingress limits, routes generated API calls, serves package-manager endpoints, renders the HTML UI, streams artifacts, exposes metrics, and hosts brother RPC on enabled web and Yggdrasil entries. The same public release and artifact routes can also be used by a brother as a read-only fallback when RPC is disabled.

Place in the runtime

flowchart TD
  web["web listener"] --> front["front handler"]
  ygg["Yggdrasil listener"] --> front
  front --> rpc["brother RPC gate"]
  front --> router["generated router"]
  router --> funcs["funcObj"]
  funcs --> builders["dataapi, goproxy, composer, feedatom, webui"]
  builders --> storage["storage"]
  builders --> state["state"]
  builders --> cache["byte cache"]

Responsibilities

  • Start listeners for single, shared, split, and Yggdrasil modes.
  • Add listener context, request id, method gate, URI cap, rate limits, and route-prefix handling.
  • Implement generated api.FuncInterface handlers.
  • Serve JSON APIs, Go proxy routes, Composer routes, Atom feeds, HTML UI, OpenAPI, sitemap, logos, favicon, and OG images.
  • Stream large artifacts with ETag, Last-Modified, Content-Length, Range, and HEAD support.
  • Gate brother RPC per listener with brother.rpc.web_enabled and brother.rpc.ygg_enabled.
  • Keep public release metadata and universal archives usable for brother fallback even when /rpc is disabled.
  • Expose public JSON metric groups, including aggregate Yggdrasil state, and optional internal Prometheus text.

Request flow

sequenceDiagram
  participant Client as client
  participant Front as front handler
  participant Router as generated router
  participant Handler as domain handler
  participant Store as storage/cache/state
  Client->>Front: HTTP request
  Front->>Front: ingress checks and listener context
  Front->>Router: route request
  Router->>Handler: typed API call
  Handler->>Store: read or build response
  Store-->>Handler: bytes, stream, or typed result
  Handler-->>Client: typed response

Contracts

  • Business handlers do not write directly to http.ResponseWriter; they return generated response types.
  • Artifacts stream from disk or builders, not through unbounded RAM buffers.
  • HTML builders must use request context so canceled clients stop expensive storage work.
  • RPC is fail-closed when disabled for the current listener.
  • /rpc accepts only CONNECT; normal HTTP methods do not enter the RPC server.
  • Public fallback uses normal generated routes, so reverse proxies handle it like ordinary read-only client traffic.
  • Metrics labels must stay low-cardinality.
  • /metrics/ygg must return 404 when the mesh is disabled. Per-peer details require internal metrics on the current listener; aggregate values remain available with public metrics alone.
  • Cold metadata and typed-object cache misses share the configured cache build gate.
  • Generated sitemap bodies are capped at 8 MiB in addition to web.pages.sitemap_size; generated Atom feeds cap release notes at 64 KiB per entry before markdown rendering and cap the final XML document at 4 MiB.

Two caches

Server responses use two different process-local caches:

Cache Stores Budget Eviction Metrics Use for
cachedBytes / mod/cache final byte bodies plus ETag cache.metadata_max_size sharded LRU by byte size yes XML, JSON, HTML, sitemap, Atom feed and other already-rendered payloads
cachedObj / objCache typed ogen objects about 2 * 4096 entries two-generation map no Composer p2 objects and Go @latest objects

New byte-oriented responses must go through cachedBytes, because it has byte accounting and low-cardinality cache metrics. cachedObj is deliberately limited to typed objects that would otherwise be serialized only to measure their size. Both caches share the same detached-build gate, so cold clients cannot start unbounded independent builds.

The typed cache has no byte accounting or metrics. That is a known limitation rather than an omission in mod/cache.

Important files

  • server.go, deps.go, listener.go: assembly and lifecycle.
  • front.go, hooks.go, respond.go, errors.go: common HTTP frame.
  • funcimpl*.go: generated API handler implementation.
  • artifact.go, objcache.go, metrics.go, assets.go: artifact serving, response cache, metrics, built-in assets.
  • brother/: RPC server.
  • dataapi/, goproxy/, composer/, feedatom/, webui/: domain response builders.

Operational notes

shared mode is for a TLS-terminating reverse proxy where the public scheme is HTTPS but the process listens on one plain HTTP socket. split mode is for separate HTTP and HTTPS binds. single mode is simplest for local development. Brother RPC is mounted before the generated API router, so the OpenAPI route list does not describe it. Public fallback uses generated API and artifact routes and therefore follows the same prefix, cache, and proxy behavior as normal clients.

Artifact and key lookups are scoped to the current listener context. A web request must not fall back to a Yggdrasil listener key, or the reverse case, when selecting generated artifact URLs.

In nested mode (web.static.dir set) per-key API routes move under web.routing.prefix, but service routes stay at their well-known root paths. /info therefore stays reachable at the root and advertises route_prefix: empty when the API is served at the root, otherwise the mounted segment. A brother reads this field during discovery to derive remote keys under the prefix the node actually serves.

The byte cache stores generated metadata responses, including sitemap and Atom feed bodies. The sitemap byte cap stays well below the protocol limit of 50 MB, and the feed caps prevent large release notes from evicting unrelated metadata from the RAM cache. Changing those constants does not require an ETag format change: the cache is process-local RAM and new bodies are built on the next miss or restart.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ArtifactBuilderInterface

type ArtifactBuilderInterface = storage.ArtifactBuilderInterface

ArtifactBuilderInterface and HotFileObj are storage aliases used by overlay builders and streaming artifact serving.

type ComposerNamesInterface

type ComposerNamesInterface interface {
	ComposerPackageNames() []string
	ComposerKeyForName(name string) (string, bool)
}

ComposerNamesInterface exposes cross-key composer names and name-to-winner mapping for serve-time p2.

type DataStateInterface

type DataStateInterface interface {
	Health() state.HealthViewObj
	Snapshot() state.SnapshotObj
	KeyState(key string) (state.KeyStateObj, bool)
	KeyStates() []state.KeyStateObj
	Checksums() state.ChecksumSetObj
}

DataStateInterface exposes health, classification, and version snapshots for data APIs.

type DataStoreInterface

type DataStoreInterface interface {
	ListVersionsKeyset(ctx context.Context, key string, includeDeleted bool, afterSeq int64, afterVersion string, limit int) ([]core.VersionObj, error)
	ListVersionsKeysetBefore(ctx context.Context, key string, includeDeleted bool, beforeSeq int64, beforeVersion string, limit int) ([]core.VersionObj, error)
	ListVersionsPage(ctx context.Context, key string, includeDeleted bool, limit int, offset int) ([]core.VersionObj, error)
	CountVersions(ctx context.Context, key string) (uint64, error)
	GetVersion(ctx context.Context, key string, version string) (core.VersionObj, bool, error)
	LatestVersion(ctx context.Context, key string) (core.VersionObj, bool, error)
	GetArtifact(ctx context.Context, keyObj core.ArtifactKeyObj) (core.ArtifactObj, bool, error)
	ListArtifacts(ctx context.Context, key string, version string) ([]core.ArtifactObj, error)
	EnsureArtifactFile(ctx context.Context, keyObj core.ArtifactKeyObj, builderObj ArtifactBuilderInterface) (*HotFileObj, error)
	GetDetection(ctx context.Context, key string, version string) (core.DetectionObj, bool, error)
	RewriteSet(ctx context.Context, key string, version string) ([]core.HashObj, error)
	ReadTree(ctx context.Context, treeHashObj core.HashObj) ([]core.TreeEntryObj, error)
	ReadBlob(ctx context.Context, hashObj core.HashObj) ([]byte, error)
	ListPublishFeed(ctx context.Context, key string, limit int) ([]core.FeedEventObj, error)
}

DataStoreInterface is the storage read surface for data APIs: artifacts, detection, versions, and feeds. Signatures match storage.Obj because it is the only production provider.

type DepsObj

type DepsObj struct {
	Config    *stconf.ConfigObj
	Storage   DataStoreInterface
	State     DataStateInterface
	Overlay   *overlay.Obj
	Telemetry *telemetry.Obj
	Composer  ComposerNamesInterface
	Mesh      mesh.NodeInterface
	Log       zerolog.Logger
	// Cache is a shared RAM cache for built byte bodies. Nil disables cache and singleflight deduplication.
	Cache *cache.Obj
	// BuildGate limits detached builds shared by Cache and the typed object cache.
	BuildGate *cache.BuildGateObj
}

DepsObj contains edge server dependencies wired by main.go and passed into New.

type HotFileObj

type HotFileObj = storage.HotFileObj

ArtifactBuilderInterface and HotFileObj are storage aliases used by overlay builders and streaming artifact serving.

type Obj added in v0.4.0

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

Obj holds the edge server. A single ogen router serves per-listener http.Server instances on web and Yggdrasil sockets. Routing and response encoding stay in the router; listeners and lifecycle live here and in listener.go.

func New

func New(depsObj DepsObj) (*Obj, error)

New assembles the edge server in three steps: funcObj, sub-services, then the ogen router. The router receives telemetry, middleware, error handling and not-found pages. Listener sockets are bound separately in Start from listener.go.

func (*Obj) Handler added in v0.4.0

func (obj *Obj) Handler(lc listenerCtxObj) http.Handler

Handler is the outer handler for one entry listener. It injects listener context, applies RPC/ingress/method gates, request ID, and go-proxy host/major stripping. HEAD is routed as GET because ogen only defines GET operations here.

func (*Obj) Shutdown added in v0.4.0

func (obj *Obj) Shutdown(ctx context.Context) error

Shutdown first closes brother sessions, then drains http.Server until the ctx deadline.

func (*Obj) Start added in v0.4.0

func (obj *Obj) Start() error

Start binds sockets and serves each listener in its own goroutine.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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