Skip to content

Smart Routing Governance

A router enrolled in Continuum Hub can receive a smart-routing policy as part of its policy envelope. In this model, Continuum Hub defines routing intent (tiers, domain/complexity rules, model profiles, and classifier method), while the local router retains full ownership of infrastructure and execution (backend URLs, credentials, enablement switches, and runtime limits).

For the underlying local configuration schema, see Smart Routing.

Prerequisites

Hub-governed smart routing requires all three of the following conditions. If any condition is not met, the router uses its local smart_routing: section exclusively:

  • Compiled feature: The binary is built with the control-plane Cargo feature (included in all official release binaries).
  • Control-plane policy enabled: Both control_plane.enabled: true and control_plane.policy.enabled: true are set.
  • Configuration manager attached: The router is started from a configuration file (standard CLI mode) or built with an attached ConfigManager. Embedded library builds (ContinuumRouter::from_config, serve_embedded) or builders with hot reload disabled do not attach the governance reconcilers and do not advertise smart_routing_policy_v1 or guardrail_policy_v1; see Library Usage.

Version compatibility

Hub-governed smart routing is supported starting in router v1.32.0:

  • Router older than v1.32.0: Ignores the envelope's smart_routing field and does not advertise smart_routing_policy_v1. The Hub immediately marks the router as unsupported rather than waiting for an acknowledgment.
  • Hub older than v1.32.0: Omits the smart_routing field. A v1.32.0+ router advertises smart_routing_policy_v1 and continues running its local smart_routing: configuration.

See the Governance compatibility matrix for mixed-version fleet behavior across both smart routing and guardrails.

Hub-governed policy fields

The Hub policy schema is closed and strongly typed (capped at 256 KiB of canonical JSON). Every top-level field is optional; omitting a field (or passing an empty list) leaves the corresponding local setting in effect:

Field Allowed values Limits
routing_policies Ordered rules containing name, when (complexity, domain, requires, language, header), and route_to (tier, prefer_domains, require_capabilities; explicit model targets are declined by the router) Up to 64 rules; rule names up to 128 bytes
model_profiles Profile entries specifying model or model_pattern, tier, and domains Up to 256 profiles
classifier method (rule, llm, hybrid, system_one, hybrid_system_one), model, and backend (logical backend name) Names up to 256 bytes
virtual_model Model name that triggers smart routing (e.g., "auto") Up to 256 bytes
intercept_all Boolean flag

Wire format notes:

  • Named tiers: On the wire, tiers are represented by name (flagship, standard, lightweight) rather than local numeric IDs (1, 2, 3).
  • No per-key/per-org conditions: Hub rules cannot use the local-only when.key_tier or when.org conditions.

Local-only settings (never controlled by the Hub)

Infrastructure wiring and local safety bounds are never represented on the governance wire. A Hub policy cannot set, override, or inspect any of the following:

  • Master enable switch (smart_routing.enabled): A local enabled: false always takes precedence; the Hub can never turn smart routing on when it is disabled locally.
  • Backend URLs and credentials (backends[]): Every backend's url, api_key, auth, type, and transport settings remain strictly local. The Hub can reference a classifier backend only by its logical name, which the router resolves against its own backends[] list.
  • Load management and cost metadata: load_management, default_tier, debug_headers, and model profile cost fields (cost_per_1k_input_tokens, cost_per_1k_output_tokens).
  • Classifier tuning and endpoints: classifier.llm operational settings (timeouts, retries, temperature, prompts, cache), classifier.rule keyword/length parameters, and classifier.system_one target endpoints and credentials (endpoint, api_key_env).
  • Tenant-scoped rule conditions: when.key_tier and when.org.

How Hub and local policies compose

The router merges the Hub policy with the local smart_routing: configuration according to the following rules:

Setting Effective value
enabled Always uses the local value (enabled: false locally cannot be overridden).
routing_policies Uses the Hub's rules when non-empty; otherwise uses the local rules.
model_profiles Uses the Hub's profiles when non-empty; otherwise uses the local profiles. Each Hub profile inherits the cost fields of any matching local profile (model or model_pattern).
virtual_model Uses the Hub's value when specified; otherwise uses the local value.
intercept_all true only when both the Hub policy and the local section set true (logical AND).
classifier.method, classifier.llm.model, classifier.llm.backend Uses the Hub's value when specified; otherwise uses the local value.
All other settings Always local (load_management, classifier tuning, System One target, debug_headers, default_tier).

Classifier backend resolution rules

When the composed policy uses an LLM-based classifier (llm, hybrid, or hybrid_system_one):

  • Name resolution: classifier.llm.backend (whether specified by the Hub or pinned locally) is resolved against the router's active backends[] list (including backends synced via control_plane.config_sync.backends).
  • Eligibility: Backends marked internal: true are permitted. Backends that do not exist or have enabled: false are rejected, and the router never substitutes a different backend automatically.
  • Mandatory pin: If a Hub statement selects an LLM-based classifier method (llm, hybrid, hybrid_system_one) but neither the Hub statement nor the local configuration pins a classifier.llm.backend, the statement is declined so prompt text is never sent to an unpinned default backend.

Atomic recomputation

Composition is recomputed automatically whenever either the local configuration reloads or a new Hub envelope arrives. Updates are applied atomically across the tier registry, smart router, classifiers, and load monitor so in-flight requests never observe a partially updated policy.

Worked example

Local configuration:

backends:
  - name: primary
    type: vllm
    url: http://vllm.internal:8000
    models: [big-model, small-model]
  - name: guard-classifier
    type: vllm
    url: http://classifier.internal:8000
    api_key: ${CLASSIFIER_API_KEY}
    internal: true
    models: [judge]

smart_routing:
  enabled: true
  virtual_model: auto
  intercept_all: true
  classifier:
    method: rule
  routing_policies:
    - name: local_all_light
      when: {}
      route_to:
        tier: 3
  model_profiles:
    - model: big-model
      tier: 1
      cost_per_1k_input_tokens: 0.010
    - model: small-model
      tier: 3
      cost_per_1k_input_tokens: 0.001

Hub statement delivered in the policy envelope:

{
  "schema_version": 1,
  "routing_policies": [
    { "name": "hub_code_to_flagship", "when": { "domain": ["code"] }, "route_to": { "tier": "flagship" } },
    { "name": "hub_default", "when": {}, "route_to": { "tier": "standard" } }
  ],
  "model_profiles": [
    { "model": "big-model", "tier": "flagship", "domains": ["code", "reasoning"] },
    { "model": "small-model", "tier": "standard" }
  ],
  "classifier": { "backend": "guard-classifier", "model": "judge", "method": "hybrid" },
  "intercept_all": false
}

Composed configuration executed by the router:

Setting Local Hub Effective Reason
enabled true cannot be stated true Always local
routing_policies local_all_light 2 rules hub_code_to_flagship, hub_default Hub rules replace local rules
model_profiles big-model (tier 1), small-model (tier 3) big-model (flagship), small-model (standard) big-model tier 1 (0.010), small-model tier 2 (0.001) Hub profiles apply while preserving matching local cost fields
virtual_model auto omitted auto Falls back to local value
intercept_all true false false Logical AND (true && false)
classifier.method rule hybrid hybrid Hub value applies
classifier.llm.model default judge judge Hub value applies
classifier.llm.backend unset guard-classifier guard-classifier Resolved in local backends[] (internal: true permitted)
Classifier URL & credential http://classifier.internal:8000, ${CLASSIFIER_API_KEY} not representable Local URL and key Wiring is strictly local

If the same Hub statement arrives while smart_routing.enabled: false locally, smart routing remains inactive (auto passes through untouched, no classifier runs) and the router reports smart_routing_unavailable until the operator enables smart routing locally.

Tri-state envelope delivery

The smart_routing field in the policy envelope has three distinct states:

Envelope representation Meaning Router behavior
Field omitted No change Retains the last received Hub smart-routing policy
Field contains a policy object Replace policy Replaces the previous Hub policy with the new statement
Field is an empty object ({}) Clear policy Removes the Hub policy layer and runs the local smart_routing: section alone

Delivery and persistence semantics:

  • Explicit clear acknowledgment: When a policy is cleared ({}), the router acknowledges with the canonical cleared-policy digest so the Hub's propagation state converges to active rather than remaining pending or stale.
  • In-memory only: The Hub smart-routing policy is stored in memory only. Across a router restart, the router starts with its local smart_routing: configuration and restores the Hub policy on the next heartbeat/sync cycle.

Policy rejection and fallback behavior

When a Hub statement is rejected, the router logs a warn message and reports one of three rejection codes to the Hub. How the router falls back depends on whether the failure occurred during wire validation or local composition:

Rejection code Reported by Trigger conditions Fallback behavior
invalid_smart_routing_policy Policy store (on envelope receipt) Schema version mismatch, size/count bounds exceeded, rule missing a route target, or unknown tier/classifier enum from a newer Hub version. Keeps previous policy: Composition does not run; the previously active policy remains in effect.
digest_mismatch Policy store (on envelope receipt) Envelope digest does not match the router's canonical digest recomputation (for either a replacement or a clear). Keeps previous policy: The malformed envelope is ignored and the previously active policy remains in effect.
smart_routing_unavailable Smart-routing reconciler (during composition) Envelope is valid on the wire, but cannot be executed by this deployment (see list below), or smart_routing.enabled: false locally. Reverts to local configuration: Drops any prior Hub smart-routing layer and runs the local smart_routing: section alone.

Causes of smart_routing_unavailable

A syntactically valid Hub policy is declined with smart_routing_unavailable when:

  • smart_routing.enabled is false locally.
  • The named classifier.llm.backend does not exist in backends[] or has enabled: false, or an LLM-using method is selected with no backend pinned in either the Hub statement or local config.
  • A routing rule targets an explicit model ID rather than a tier.
  • The composed configuration fails local smart_routing validation (for example, method: system_one without a local classifier.system_one target block).
  • The composed configuration violates cross-section validation rules (for example, a Hub virtual_model that collides with a local model_experiments[].model entry).

Because the policy store retains the latest wire-valid statement in memory even when declined with smart_routing_unavailable, fixing the local prerequisite (such as adding the missing classifier backend or setting enabled: true via hot reload) automatically activates the Hub policy without waiting for a new envelope.

Hub propagation status

The router advertises smart_routing_policy_v1 whenever the smart-routing reconciler is attached (even if smart_routing.enabled: false locally). It acknowledges smart_routing_policy_digest only after the reconciler has applied the composed policy to the live components.

Continuum Hub derives each router's propagation status from the heartbeat report:

Hub status Condition reported by router
unsupported smart_routing_policy_v1 is absent from capabilities (router older than v1.32.0 or embedded build without ConfigManager).
pending smart_routing_policy_v1 is advertised, but the reconciler has not yet acknowledged or rejected the latest digest.
active Acknowledged smart_routing_policy_digest matches the digest authored by the Hub (including the canonical cleared-policy digest).
stale Acknowledged digest corresponds to an older policy revision than the Hub's current target.
error The router reported invalid_smart_routing_policy, smart_routing_unavailable, or digest_mismatch.

The heartbeat carries a single rejection slot shared between guardrails and smart routing. If both tracks fail simultaneously, the guardrail rejection code takes precedence in the shared slot; see What the hub sees.

Local edits and Admin API behavior

When a Hub policy is active, local configuration reloads and Admin API endpoints interact with the Hub layer as follows:

  • File hot reload and PUT /admin/smart-routing/{policies,model-profiles}:
    • Writes update the local configuration layer and trigger recomposition; they never mutate the Hub layer or copy Hub values into local config.
    • Edits to fields not set by the Hub take effect immediately; edits to fields governed by the Hub remain overridden by the Hub statement, and PUT responses include "hub_governed": true.
    • Fields omitted from a PUT /admin/smart-routing/policies request (virtual_model, intercept_all) are populated from the local configuration rather than the live composed state.
    • If a Hub-governed router was built without hot reload, PUT requests return HTTP 503 with {"error": {"code": "hub_governed_no_config_channel", "section": "smart_routing", ...}}.
  • Inspection (config show vs. GET /admin/smart-routing/*):
    • continuum-router config show and the on-disk config file always reflect only what the operator wrote locally.
    • GET /admin/smart-routing/policies and GET /admin/smart-routing/model-profiles inspect the live runtime state and therefore return the composed rules, profiles, virtual_model, and intercept_all.

Read-only Hub inventory (smart_routing_inventory)

Whenever the control-plane agent is enabled, enrollment and heartbeat payloads include a read-only smart_routing_inventory summary of the live routing state (regardless of whether smart_routing_policy_v1 is advertised):

Field Description
enabled Whether smart routing is currently active (true / false).
policy_count Number of active routing policies.
model_profile_count Number of explicit exact and pattern model profiles in effect (including Admin API additions; inferred profiles are excluded).
classifier_method Active classifier method (rule, llm, hybrid, system_one, or hybrid_system_one).
classifier_backend Configured classifier.llm.backend name (reported only when set and the active method uses the LLM classifier).
virtual_model Model name that triggers smart routing.
intercept_all Whether all requests are intercepted for smart routing.

Privacy and disabled-state guarantees:

  • Disabled state: When smart routing is disabled, the router sends enabled: false, zero counts, no classifier_backend, and intercept_all: false rather than omitting the block (an omitted block indicates an older router version or an invalid >256-byte identifier).
  • Metadata only: The inventory reports only counts and top-level identifiers. Policy rule conditions, model lists, classifier prompts, System One endpoints, and api_key_env names are never transmitted.