Help us shape the future of Go! Take the 2026 Go Developer Survey

policy

package
v1.3.97 Latest Latest
Warning

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

Go to latest
Published: Sep 27, 2026 License: AGPL-3.0 Imports: 16 Imported by: 0

Documentation

Overview

Package policy pkg/router/policy/bridge.go c2-net-routing RoutingContext / Candidate / RouteSpec to and from Starlark values, and assembles the pre-declared module set the policy script sees on load.

Package policy pkg/router/policy/distribution.go c2-net-routing Starlark-emitted RouteSpec.Distribution descriptor into the router-domain DistributionConfig the route group's selector consumes. Keeps the parser in the policy package so pkg/router stays free of the grammar (it just sees a populated struct).

Vocabulary (RFC #2882 phase 5):

""                          → DistributionUnset (no override)
"auto"                      → DistributionAuto (latency-weighted)
"round-robin" / "equal"     → DistributionRoundRobin
"weighted: f1, f2, ..., fN" → DistributionWeighted with N weights
"size-threshold: N"         → DistributionSizeThreshold with N bytes

Whitespace is tolerant; case is not (descriptors are lowercase).

Package policy pkg/router/policy/evaluator.go c2-net-routing evaluator for routing policy scripts. See RFC #2882.

Package policy pkg/router/policy/hook.go c2-net-routing a Loader (the operator's Starlark policy file) into a router.DialHook the router can invoke per dial.

Living in the policy package keeps the router package free of the Starlark machinery; pkg/router only depends on the small DialHook interface in dial_hook.go.

Package policy pkg/router/policy/loader.go c2-net-routing field value (`"@/etc/skywire/policies/global.star"` or an inline string) into a live Evaluator, with file-watcher hot-reload so editing the file doesn't require a visor restart.

Package policy pkg/router/policy/provider.go c2-net-routing interface for the data the stdlib (geo, transports, peers) surfaces to policy scripts. The Evaluator caches Provider results per-visor-uptime so even a script that queries the same data on every dial doesn't burn CPU.

Production wires this to the visor's actual geoip / transport- tracker / hypervisor-list state. Tests inject FakeProvider with controlled return values to assert script semantics deterministically.

Package policy pkg/router/policy/types.go c2-net-routing for the visor's per-dial route selection. See RFC #2882 (docs/routing_policy_rfc.md) for the design.

The package's external surface:

  • Evaluator: loads a Starlark policy script and exposes Decide(ctx, candidates) which the router calls per dial.
  • RoutingContext: the input passed to the script.
  • Candidate: one possible route the script can choose from.
  • RouteSpec: the script's structured return value.

The policy is OPTIONAL — when the evaluator is nil (no policy configured) the router uses its built-in default selection. When configured, the evaluator runs the script under a timeout, with panics caught and translated into a fallback signal.

Index

Constants

View Source
const DefaultMaxSteps = 100_000

DefaultMaxSteps is the per-call execution-step ceiling, the hard counterpart to DefaultTimeout. Starlark's interpreter increments a counter on every fundamental operation; we cap it so that even a tight loop terminates. 100k steps comfortably covers a non-pathological policy on ARM.

View Source
const DefaultTimeout = 50 * time.Millisecond

DefaultTimeout is the per-call timeout enforced via a step counter on the Starlark thread. Generous enough that real policies won't hit it; tight enough that a buggy policy never blocks a dial. Operator-configurable via Evaluator.WithTimeout.

Variables

This section is empty.

Functions

func ParseDistribution

func ParseDistribution(s string) (router.DistributionConfig, error)

ParseDistribution translates the script's descriptor into a router.DistributionConfig. Empty / "auto" / "round-robin" / "equal" map to the existing selector modes; "weighted:..." and "size-threshold:..." carry their config fields.

Returns DistributionConfig{} (Mode=DistributionUnset) for the empty string so callers can branch on Mode and skip the override when no policy was set. Any other parse failure returns an error — the caller (the policy Hook) treats parse errors as "no override, log it" rather than failing the dial.

Types

type Candidate

type Candidate struct {
	// Hops is the ordered list of intermediate visor PKs from
	// source (excluded) to destination (excluded). Empty list means
	// a direct transport.
	Hops []string

	// HopsGeo mirrors Hops with ISO-3166 country codes for each
	// intermediate, resolved via the embedded geoip database.
	// Unknown / unresolvable hops appear as "??". Policies use
	// this for geographic filtering ("only routes through ID").
	HopsGeo []string

	// EstLatencyMs is the visor's best estimate of round-trip
	// latency along this route, from the transport-tracker's
	// recent measurements. Zero when no recent measurement is
	// available — policies should treat zero as "unknown" rather
	// than "fast."
	EstLatencyMs int

	// TransportKinds is the set of transport types along the
	// route (e.g. ["stcpr"], ["dmsg"], ["stcpr", "sudph"] for
	// mixed-transport routes). Used by policies that want to
	// prefer direct-tcp paths over relayed-dmsg paths.
	TransportKinds []string
}

Candidate is one possible route from the visor to the peer that the policy can examine and choose from. The router constructs the candidate list before invoking the policy; the policy's job is to pick one (or filter / sort the list and return the head).

type Clock

type Clock interface {
	Now() time.Time
}

Clock is the time source the datetime stdlib reads from. The production evaluator uses systemClock; tests inject a deterministic clock so they can assert "what does the policy decide at 5pm on a Friday" without manipulating system time.

func NewSystemClock

func NewSystemClock() Clock

NewSystemClock returns a Clock backed by time.Now(). Most callers use this; tests use a fake.

type Engine

type Engine interface {
	Decide(ctx context.Context, rctx RoutingContext, candidates []Candidate) (RouteSpec, error)
	OnLegChange(ctx context.Context, rctx RoutingContext, legs []LegInfo, change LegChange) (RouteSpec, error)
	OnTick(ctx context.Context, rctx RoutingContext, legs []LegInfo) (RotationAction, error)
	IsActive() bool
	Source() string
	Close() error
}

Loader manages an Evaluator constructed from a config field. It encapsulates two patterns:

  1. Inline string — config field starts with anything other than "@". The string is the script. No file involved; no watching.
  2. File reference — config field starts with "@". The string after "@" is a path. The Loader reads the file, constructs the Evaluator, and (optionally) watches the file for changes; on change it re-loads and atomic-swaps the Evaluator.

Engine is the surface the Hook needs from any policy backend. Both the Starlark Loader (this package) and the WASM Loader (pkg/router/policy/wasm) implement it, so the Hook can dispatch to either without caring which backend the operator chose.

type EngineSnapshot

type EngineSnapshot struct {
	Source string
	Active bool
}

EngineSnapshot describes one Engine instance: the source identifier (file path or "<inline>" / "<noop>") and whether it's actively serving (evaluator loaded).

type Evaluator

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

Evaluator holds a parsed Starlark policy and applies it to dial contexts. Safe for concurrent Decide calls — the parsed program is immutable; each Decide creates a fresh thread.

func NewEvaluator

func NewEvaluator(name, src string, opts ...Option) (*Evaluator, error)

NewEvaluator parses the supplied Starlark source and returns an Evaluator ready for Decide calls. The source's top-level is executed once at construction time; the decide_route function must be defined there.

Returns an error if the source fails to parse or top-level execution panics. A nil error guarantees that decide_route exists with the expected signature.

func (*Evaluator) Decide

func (e *Evaluator) Decide(ctx context.Context, rctx RoutingContext, candidates []Candidate) (RouteSpec, error)

Decide applies the policy to one dial. Always returns a usable RouteSpec; on script failure the result is the configured fallback (and the failure is logged).

Execution is bounded by the Evaluator's MaxSteps — the Starlark interpreter increments a counter per fundamental operation and aborts if it exceeds the cap. With the default 100k step cap, even a tight loop terminates inside a millisecond on commodity hardware, so we don't need a goroutine-based wall-clock timeout (which had a race: the cancel-watch goroutine could fire Cancel before starlark.Call even started executing, surfacing as a spurious "computation canceled" error on scripts that actually take microseconds).

ctx is checked once at entry — if the caller's dial ctx is already canceled, we short-circuit to the fallback without attempting evaluation. We don't propagate ctx cancellation mid-script because the step cap already gives us a tight upper bound on execution time.

func (*Evaluator) OnLegChange

func (e *Evaluator) OnLegChange(ctx context.Context, rctx RoutingContext, legs []LegInfo, change LegChange) (RouteSpec, error)

OnLegChange invokes the script's optional `on_leg_change(ctx, legs, change)` function. Returns the empty RouteSpec when the script doesn't define the function (the common case for scripts that don't care about post-setup leg events) — the route group treats that as "no distribution update."

Same step ceiling as Decide; failures are non-fatal (logged, then return empty spec).

func (*Evaluator) OnTick

func (e *Evaluator) OnTick(ctx context.Context, rctx RoutingContext, legs []LegInfo) (RotationAction, error)

OnTick invokes the script's optional `on_tick(ctx, legs)` function. Returns the zero-value RotationAction when the script doesn't define on_tick — the router treats that as "no rotation this tick." Same step ceiling as Decide; failures are non-fatal (logged, then return zero action).

type FailureMode

type FailureMode int

FailureMode dictates what the evaluator returns when a script panics, times out, returns the wrong type, or fails to load.

const (
	// FailureFallback returns RouteSpec{Fallback: ""} (use visor
	// built-in default) and logs the violation. Production default.
	FailureFallback FailureMode = iota
	// FailureDrop returns the original error so the caller can
	// fail the dial loudly. For operators who want to notice when
	// their policy is broken.
	FailureDrop
)

type FakeProvider

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

FakeProvider is a test helper. Construct via NewFakeProvider with the visor state the test wants to simulate.

func NewFakeProvider

func NewFakeProvider() *FakeProvider

NewFakeProvider returns an empty FakeProvider. Tests call the Set* methods to populate it before passing to the Evaluator.

func (*FakeProvider) Geo

func (p *FakeProvider) Geo(pk string) string

Geo implements Provider.

func (*FakeProvider) HasTransport

func (p *FakeProvider) HasTransport(pk string) bool

HasTransport implements Provider.

func (*FakeProvider) IsHypervisor

func (p *FakeProvider) IsHypervisor(pk string) bool

IsHypervisor implements Provider.

func (*FakeProvider) IsTrusted

func (p *FakeProvider) IsTrusted(pk string) bool

IsTrusted implements Provider.

func (*FakeProvider) Kind

func (p *FakeProvider) Kind(pk string) string

Kind implements Provider.

func (*FakeProvider) Latency

func (p *FakeProvider) Latency(pk string) int

Latency implements Provider.

func (*FakeProvider) SetGeo

func (p *FakeProvider) SetGeo(pk, country string) *FakeProvider

SetGeo sets the country code for a peer PK.

func (*FakeProvider) SetHasTransport

func (p *FakeProvider) SetHasTransport(pk string) *FakeProvider

SetHasTransport marks a peer as one the visor already holds a live transport to.

func (*FakeProvider) SetHypervisor

func (p *FakeProvider) SetHypervisor(pk string) *FakeProvider

SetHypervisor marks a peer as a configured hypervisor.

func (*FakeProvider) SetKind

func (p *FakeProvider) SetKind(pk, kind string) *FakeProvider

SetKind sets the transport family for a peer PK.

func (*FakeProvider) SetLatency

func (p *FakeProvider) SetLatency(pk string, ms int) *FakeProvider

SetLatency sets the cached latency for a peer PK.

func (*FakeProvider) SetTrusted

func (p *FakeProvider) SetTrusted(pk string) *FakeProvider

SetTrusted marks a peer as trusted.

type Hook

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

Hook adapts one default Loader plus optional per-app Loaders to the router.DialHook interface. Pass to router.Config.DialHook to plug a routing policy into the dial path.

Per-app loaders take precedence over the default; an app with a configured RoutingPolicy gets its own evaluator, every other app falls through to the visor-wide default. An app whose per-app loader is registered but inactive still uses that loader (which returns a no-op spec) rather than escaping to the default — the per-app policy is the operator's intent for that app.

func NewHook

func NewHook(l Engine, opts ...HookOption) *Hook

NewHook wraps an Engine (e.g. the Starlark Loader or the WASM Loader from pkg/router/policy/wasm) as a DialHook. Passing nil is legal — the hook becomes a no-op until RegisterApp adds a per-app engine.

func (*Hook) BeforeDial

func (h *Hook) BeforeDial(ctx context.Context, info router.DialInfo) (router.DialAdjustment, error)

BeforeDial implements router.DialHook. Constructs a RoutingContext from the per-dial DialInfo, invokes the loader, projects the returned RouteSpec into a DialAdjustment the router can consume.

The loader's failure-mode wiring (Fallback vs. Drop) determines what the policy script's failure looks like to the router:

  • FailureFallback (default): a broken script returns an empty RouteSpec → empty DialAdjustment → router proceeds with the caller's original opts. No error surfaced.
  • FailureDrop: errors propagate up here as a real error; the router sees an error from BeforeDial and logs it but still proceeds (failure-safe at the router layer too).

func (*Hook) OnLegChange

func (h *Hook) OnLegChange(info router.DialInfo, legs []router.LegInfo, change router.LegChange) router.DistributionConfig

OnLegChange implements router.LegChangeHook. Called by the route group whenever its leg set mutates. Translates the router-side LegInfo / LegChange to the policy-side equivalents, invokes the script's optional on_leg_change function, and projects any returned distribution descriptor into a DistributionConfig for the route group to apply.

Returns the zero DistributionConfig (Mode == DistributionUnset) when the script doesn't define on_leg_change, when the loader is inactive, or when the parse fails — the route group treats that as "leave distribution unchanged."

func (*Hook) OnTick

func (h *Hook) OnTick(info router.DialInfo, legs []router.LegInfo) router.RotationAction

OnTick fires the policy's optional on_tick hook for periodic rotation. The router calls this from the per-RouteGroup rotation goroutine (cadence comes from RouteSpec.RotationIntervalSeconds set at dial time). Returns the zero-value action when no engine is registered, no engine is active, or the engine's on_tick is undefined — the router treats that as "no rotation this tick."

Errors from the engine fall through to a zero-value action + the logger, matching the failure-safe pattern used elsewhere in the Hook. A misbehaving rotation script can't cause the route group to drop legs unexpectedly.

func (*Hook) RegisterApp

func (h *Hook) RegisterApp(appName string, l Engine) Engine

RegisterApp attaches a per-app Engine that overrides the default for dials originating from the named app. Safe to call concurrently with the dial path: writes are serialized by h.mu and the read path in loaderFor takes an RLock.

Returns the previously-registered Engine (or nil) so the caller can Close it if it owns the lifecycle. Runtime swaps (e.g. `cli visor app start --routing-policy <path>`) should Close the previous engine after a successful swap to release the wazero runtime / fsnotify watcher.

func (*Hook) SelectRoute

func (h *Hook) SelectRoute(ctx context.Context, info router.DialInfo, forward, reverse []router.CandidateInfo) (router.RouteSelection, error)

SelectRoute implements router.RouteSelectingHook. Enriches the router's bare CandidateInfo list with HopsGeo and TransportKinds (via the Hook's Provider), passes the enriched candidates to the script's decide_route function, and translates the returned RouteSpec.Chosen back into an index for the router.

Index translation: the script's Chosen is a *Candidate; we match it back to the input candidates by Hops equality. Hops is the stable key because it's the only field the script can't fabricate (provider-derived fields are read-only inside Starlark).

"drop" Fallback maps to RouteSelection.Drop. An empty Chosen with no drop signal maps to Chosen=-1 (defer to router's pick).

func (*Hook) SelfHealTarget

func (h *Hook) SelfHealTarget() (int, bool)

SelfHealTarget implements the optional router.SelfHealTargeter by delegating to the default engine when it exposes a live self-heal degree. The adaptive wasm preset does (its pool size = AdaptRevActive()+AdaptStandbyMax(), both runtime-tunable over the mux-control RPC); every other engine returns ok=false, leaving the route group's fixed dial-time target in place.

Without this the production RotationHook (a *policy.Hook wrapping the wasm Loader) did NOT satisfy router.SelfHealTargeter, so the route group's per-tick re-cap was skipped on every preset:* visor and the self-heal kept storming toward its dial-time degree (513) regardless of `cli proxy mux standby`. presethook.Hook implemented this for the native path only.

func (*Hook) SetDefault

func (h *Hook) SetDefault(l Engine) Engine

SetDefault swaps the visor-wide (default) Engine. Returns the previous engine for caller-side cleanup. Same concurrency rules as RegisterApp.

func (*Hook) Snapshot

func (h *Hook) Snapshot() HookSnapshot

Snapshot returns the Hook's current engine state. Takes the RLock for the duration so it's safe to call concurrently with the dial path and with SetDefault / RegisterApp.

type HookOption

type HookOption func(*Hook)

HookOption configures a Hook at construction.

func WithHookLogger

func WithHookLogger(f func(format string, args ...interface{})) HookOption

WithHookLogger supplies the log function the hook uses for non-fatal events (e.g. malformed distribution descriptor that can be safely ignored). Default is a no-op so silent operation stays the default in tests.

func WithHookProvider

func WithHookProvider(p Provider) HookOption

WithHookProvider supplies the Provider used to enrich per-route candidate metadata (HopsGeo, TransportKinds) before the script's decide_route function sees the candidates slice. Without this the hook's SelectRoute still works, but every HopsGeo entry is "??" and TransportKinds is empty — geographic / transport-kind filters in the script would match nothing.

type HookSnapshot

type HookSnapshot struct {
	Default *EngineSnapshot
	PerApp  map[string]*EngineSnapshot
}

HookSnapshot is a point-in-time copy of the Hook's engine state, for observability — hypervisor UI, `cli visor routing- policies`, etc. Safe to serialize as JSON.

type LegChange

type LegChange struct {
	Event    string
	LegIndex int
}

type LegInfo

type LegInfo struct {
	Index       int
	Kind        string
	TransportID string
	LatencyMs   int
	Alive       bool
	// Standby mirrors router.LegInfo.Standby: true when the leg is a
	// warm standby (rules kept alive, not selected for sending).
	Standby bool
	// Direct mirrors router.LegInfo.Direct: true when the leg is the
	// forward-only DIRECT (1-hop) leg of a unidirectional route group, so a
	// download-sizing controller keeps it out of the reverse-active budget.
	Direct bool
	// Flipped mirrors router.LegInfo.Flipped: the route group's current
	// unidirectional flip state (same on every leg). A leg carries the download
	// direction when Direct == Flipped.
	Flipped     bool
	SentBytes   uint64
	RecvBytes   uint64
	Retransmits uint64
	Hops        []string
}

LegInfo and LegChange mirror the router package's types so scripts can reason about them without the policy package pulling in the router. The bridge layer converts between the two when the hook fires.

type Loader

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

Callers go through Loader.Decide rather than holding an Evaluator directly so the hot-reload path is transparent.

func NewLoader

func NewLoader(raw string, opts ...Option) (*Loader, error)

NewLoader parses the supplied raw config-field value and returns a Loader. If raw is "" or "none", the Loader runs in no-op mode (every Decide returns the visor default). If raw starts with "@", the suffix is treated as a file path; the file's contents are loaded and watched. Otherwise raw is treated as an inline script.

Returns an error only on a load-time failure (file unreadable, script unparseable). Runtime failures during Decide flow through the Evaluator's FailureMode.

func (*Loader) Close

func (l *Loader) Close() error

Close stops the file-watcher goroutine and releases resources.

func (*Loader) Decide

func (l *Loader) Decide(ctx context.Context, rctx RoutingContext, candidates []Candidate) (RouteSpec, error)

Decide is the loader's transparent entry point. Forwards to the currently-loaded Evaluator. If the loader is in noop mode (empty config field), returns an empty RouteSpec without invoking any script — the router treats this as "use built-in default."

func (*Loader) IsActive

func (l *Loader) IsActive() bool

IsActive reports whether the loader has an evaluator loaded (i.e. not noop mode). Useful for the router to skip the call path entirely when no policy is configured.

func (*Loader) OnLegChange

func (l *Loader) OnLegChange(ctx context.Context, rctx RoutingContext, legs []LegInfo, change LegChange) (RouteSpec, error)

OnLegChange forwards to the currently-loaded Evaluator's OnLegChange. Returns empty spec when no evaluator is loaded.

func (*Loader) OnTick

func (l *Loader) OnTick(ctx context.Context, rctx RoutingContext, legs []LegInfo) (RotationAction, error)

OnTick forwards to the active Evaluator's OnTick. Returns the zero-value RotationAction when no evaluator is loaded — the router treats that as "no rotation this tick."

func (*Loader) Source

func (l *Loader) Source() string

Source returns the human-readable source identifier (file path or "<inline>" or "<noop>"). Used by callers for logging.

func (*Loader) Watch

func (l *Loader) Watch(logger func(format string, args ...interface{})) error

Watch starts a goroutine that reloads the script when the file changes. No-op for inline scripts. Returns immediately; the goroutine runs until Close is called.

On reload failure the watcher logs the error and keeps the previous Evaluator active. This way a transient editor (vi-style atomic-rename) doesn't break the running policy.

type Option

type Option func(*Evaluator)

Option configures an Evaluator at construction.

func WithClock

func WithClock(c Clock) Option

WithClock injects a custom Clock. Used by tests to assert time-dependent policy behavior deterministically.

func WithFailureMode

func WithFailureMode(m FailureMode) Option

WithFailureMode overrides the default failure handling.

func WithLogger

func WithLogger(f func(format string, args ...interface{})) Option

WithLogger injects a log function. Default is a no-op; callers who want failures surfaced wire in their package logger.

func WithMaxSteps

func WithMaxSteps(n int64) Option

WithMaxSteps overrides the default per-call step ceiling.

func WithProvider

func WithProvider(p Provider) Option

WithProvider injects the data source the stdlib (geo, transports, peers) reads from. When unset, the Evaluator uses NopProvider — every Geo lookup returns "??", every Latency 0, etc. Tests use FakeProvider; production wires the visor's actual geoip / transport-tracker / hypervisor-list state.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout overrides the default per-call timeout.

type Provider

type Provider interface {
	// Geo returns the ISO-3166 country code for a peer PK, or
	// "??" when unknown. Operators use this for geographic
	// routing filters ("only routes through ID").
	Geo(pk string) string

	// Latency returns the most recent measured round-trip in
	// milliseconds to this peer. Zero means "no recent
	// measurement" — scripts should treat zero as unknown, not
	// fast.
	Latency(pk string) int

	// Kind returns the transport family of the most recent
	// connection to this peer ("stcpr", "sudph", "dmsg"). Empty
	// when unknown.
	Kind(pk string) string

	// IsTrusted reports whether the peer's PK is in the
	// operator-configured trust list. Used by policies that want
	// "trusted peers get direct routes, untrusted peers go
	// through 2+ hops" style rules.
	IsTrusted(pk string) bool

	// IsHypervisor reports whether the peer's PK is one of the
	// visor's configured hypervisors. Policies typically want to
	// dial hypervisors with minimum hops + maximum mux for low-
	// latency control-plane traffic.
	IsHypervisor(pk string) bool

	// HasTransport reports whether the visor already holds a live
	// transport to this peer (of any type). Policies use it to
	// prefer routing over peers we're already connected to —
	// reusing existing plumbing instead of paying the setup cost of
	// a fresh transport (handshake + AR register + route-finder) on
	// the dial hot path. False when the peer is only reachable
	// through intermediates, or unknown.
	HasTransport(pk string) bool
}

Provider is the contract the Evaluator uses to surface visor state to policy scripts. Methods are called per script invocation (memoized — same PK queried twice during one Decide returns the cached value), so they should be near-instant. Implementations that need to hit a network or do I/O should pre-cache before NewEvaluator returns.

func NopProvider

func NopProvider() Provider

NopProvider returns a Provider that knows nothing — Geo returns "??", Latency 0, Kind "", IsTrusted/IsHypervisor false. Used as the default when no Provider is wired up, so policies that don't depend on visor state still work.

type RotationAction

type RotationAction struct {
	DropLegs    []int
	AddLeg      bool
	ExcludeHops []string
	// DemoteToStandby / PromoteFromStandby move legs to/from warm standby
	// without teardown (kept alive, just not selected for sending). See
	// router.RotationAction and docs/warm_standby_legs_rfc.md.
	DemoteToStandby    []int
	PromoteFromStandby []int
	// AddForwardLeg requests one more FORWARD-ONLY aux leg (router appends it
	// addFwd=true / addRev=false): extra upstream send capacity that does not
	// enlarge the reverse/download set. The forward-direction mirror of AddLeg,
	// emitted by the adaptive preset on sustained upload saturation.
	AddForwardLeg bool
}

RotationAction is the structured return value from a policy's on_tick hook. The router fires the hook periodically per active route group (see RouteSpec.RotationIntervalSeconds) and applies this action against the group's leg set:

  • DropLegs is a list of current-tick leg indices to close. Indices reference the slice passed to on_tick — they are NOT stable across ticks because leg-prune compacts the slice when a leg closes. Policies must re-read on each tick.
  • AddLeg requests the router to dial one more aux forward leg, using ExcludeHops as the disjoint-intermediate filter. The add is best-effort — if no disjoint path exists the action is logged and the group keeps its current legs.

Drops apply before adds, so a "drop oldest + add new" pattern produces one new leg replacing one old one. A no-op action (zero-value) leaves the group unchanged.

type RouteSpec

type RouteSpec struct {
	// Chosen is the forward-direction candidate the policy picked.
	// Nil means "fall back to the visor's built-in default" —
	// either because the policy returned an explicit None or
	// because every candidate was filtered out. For directional-
	// asymmetric policies, set ReverseChosen separately.
	Chosen *Candidate

	// ReverseChosen is the reverse-direction candidate, when the
	// policy wants different forward and reverse routes (e.g.
	// "stcpr upstream, sudph downstream"). Nil means "no override;
	// let the router pick the lowest-latency reverse independently."
	// The script accesses reverse candidates via ctx.reverse_candidates
	// in SelectRoute.
	ReverseChosen *Candidate

	// Mux is the symmetric number of parallel mux legs to open.
	// Zero or 1 means a single route. The router caps it at the
	// number of disjoint candidates the route-finder returned —
	// scripts that want to know that count up-front should read
	// `len(candidates)` in SelectRoute. Overridden per-direction
	// by ForwardMux / ReverseMux when those are non-zero.
	Mux int

	// ForwardMux and ReverseMux are per-direction overrides for
	// Mux. The common use case is asymmetric workloads — a
	// download-heavy app benefits from ReverseMux=N (multiple
	// download paths aggregated) and ForwardMux=1 (one cheap
	// upstream). Zero falls back to Mux.
	ForwardMux int
	ReverseMux int

	// MinHops is the symmetric minimum acceptable intermediate
	// count. Honored as a hint by the router. Overridden per-
	// direction by ForwardMinHops / ReverseMinHops when those
	// are non-zero.
	MinHops int

	// ForwardMinHops and ReverseMinHops are per-direction
	// overrides for MinHops. Useful when one direction needs more
	// hops than the other for compliance or trust reasons. Zero
	// falls back to MinHops.
	ForwardMinHops int
	ReverseMinHops int

	// Fallback names the backup strategy when Chosen is nil or
	// rejected. Recognized values:
	//   ""       — use the visor's built-in default (router's
	//              disjoint-path pick on the unfiltered candidates,
	//              or direct-transport short-circuit if one exists)
	//   "drop"   — fail the dial loudly with ErrDialPolicyDropped
	//              (operator notices; app sees a connection error)
	//   "direct" — skip the route-finder, force the direct-transport
	//              short-circuit. If no direct transport exists,
	//              the dial fails. Useful when a script's filter
	//              wipes all multihop candidates but a direct
	//              transport would still satisfy the operator
	//              (e.g. "Indonesia transit only, else direct").
	//
	// Any other value is treated as "".
	Fallback string

	// Distribution is the per-packet distribution descriptor for
	// the per-packet code path (deferred Phase 5 in the RFC).
	// Recognized values: "" (round-robin, default),
	// "weighted: a,b,c", "size-threshold:N". The per-packet path
	// stays compiled Go; this field configures it from Starlark.
	Distribution string

	// RotationIntervalSeconds, when > 0, configures the router to
	// fire the policy's on_tick hook on this route group every
	// N seconds. The hook returns a RotationAction describing
	// which legs to drop and whether to add a fresh one — the
	// mechanism behind continuous bandwidth-spreading policies
	// that rotate through the eligible-peer set. Set at dial time
	// via decide_route; cannot be changed mid-flight (drop + redial
	// to reconfigure).
	RotationIntervalSeconds int
}

RouteSpec is the structured return value from the policy script. The router consumes this to drive route selection + per-dial knob settings.

type RoutingContext

type RoutingContext struct {
	// App is the registered app name making this dial — "skychat",
	// "vpn-client", etc. Policies can branch on this to apply
	// different rules per app.
	App string

	// PeerPK is the destination visor's public key (66-hex-char).
	PeerPK string

	// Port is the routing port being dialed on the remote.
	Port uint16

	// Now is the current wall-clock time at policy invocation, in
	// the operator's configured timezone. Used by datetime stdlib;
	// injectable for tests via the Evaluator's Clock.
	Now time.Time

	// CLIOverrides surfaces any per-invocation flags the operator
	// passed on the CLI (--routes N, --min-hops K, etc.). The
	// policy can choose to honor or override them. Empty map when
	// the dial came from a launched app rather than a CLI command.
	CLIOverrides map[string]string

	// IsDirectDial signals the dial is going through the
	// sky_forward_conn / VStreamMux short-circuit (an existing
	// direct transport) rather than the overlay route-finder.
	// Mux / distribution are no-ops on direct dials; the most
	// useful return from the script in this case is
	// fallback="drop" to refuse, or a bare RouteSpec() to allow.
	// Surfaced as `ctx.is_direct_dial` in Starlark.
	IsDirectDial bool

	// TransportKind is the network type ("stcpr"/"sudph"/"stcp"/
	// "dmsg") of the direct transport when IsDirectDial is true.
	// Surfaced as `ctx.transport_kind`.
	TransportKind string

	// ReverseCandidates is the reverse-direction candidate list
	// when SelectRoute is invoked, exposed to Starlark as
	// `ctx.reverse_candidates`. Empty during BeforeDial (no
	// candidates yet) and during SelectRoute when the dial used
	// a symmetric route-finder query (forward and reverse share
	// the same path candidates — same list). Scripts that want
	// per-direction filtering iterate this list separately and
	// set RouteSpec.reverse_chosen.
	ReverseCandidates []Candidate
}

RoutingContext is the input the visor passes to the policy script per dial. Fields are the union of (a) the dial-time inputs (app name, peer PK, target port) and (b) visor-state snapshots that the operator's policy might reference (current time, recent failure history). Expensive lookups (geoip, transport-tracker queries) are memoized per-visor-uptime; the fields populated here are the cheap ones plus the few that are genuinely per-dial.

Directories

Path Synopsis
Package preset pkg/router/policy/preset/names.go c2-net-routing enumerates the built-in preset names Decide / Engine.OnTick dispatch on, so callers (the wasm-visor's config selection, the CLI, tests) can validate a requested preset without importing the wazero-side manifest.
Package preset pkg/router/policy/preset/names.go c2-net-routing enumerates the built-in preset names Decide / Engine.OnTick dispatch on, so callers (the wasm-visor's config selection, the CLI, tests) can validate a requested preset without importing the wazero-side manifest.
Package presethook pkg/router/policy/presethook/presethook.go c2-net-routing adapts a built-in routing-policy preset (pkg/router/policy/preset) to the router.DialHook / RouteSelectingHook / RotationHook interfaces WITHOUT the starlark evaluator or the wazero runtime.
Package presethook pkg/router/policy/presethook/presethook.go c2-net-routing adapts a built-in routing-policy preset (pkg/router/policy/preset) to the router.DialHook / RouteSelectingHook / RotationHook interfaces WITHOUT the starlark evaluator or the wazero runtime.
Package presets pkg/router/policy/presets/presets.go c2-net-routing embedded in the skywire binary, selectable by name without writing or compiling a policy file.
Package presets pkg/router/policy/presets/presets.go c2-net-routing embedded in the skywire binary, selectable by name without writing or compiling a policy file.
Package wasm pkg/router/policy/wasm/abi.go c2-net-routing for the WASM-host ABI.
Package wasm pkg/router/policy/wasm/abi.go c2-net-routing for the WASM-host ABI.
presets
Package presets pkg/router/policy/wasm/presets/presets.go c2-net-routing is the WASM analog of pkg/router/policy/presets: a registry of precompiled TinyGo routing-policy modules embedded in the skywire binary and selectable by name via a config value of the form "preset:<name>" (see pkg/visor/policy_loader.go), exactly like the Starlark presets.
Package presets pkg/router/policy/wasm/presets/presets.go c2-net-routing is the WASM analog of pkg/router/policy/presets: a registry of precompiled TinyGo routing-policy modules embedded in the skywire binary and selectable by name via a config value of the form "preset:<name>" (see pkg/visor/policy_loader.go), exactly like the Starlark presets.

Jump to

Keyboard shortcuts

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