Skip to content

Remote APIs, Embedded Runtimes, and Inference Services

There is no universally best place to run a model.

Embedding a runtime in the Rust process removes an inference-network boundary but couples model memory, startup and failure to every application replica. A private inference service centralizes accelerators and batching but adds a protocol, queue and operational service. A remote API offers managed capacity and access to models you may not be able to host, while widening the data/trust boundary and adding an external dependency.

“Local is private,” “remote is scalable,” and “a service is production-ready” are slogans. The real decision depends on stated requirements and measured implementations.

This chapter adds a constraint-based planner to mosaic-runtime. It calculates model-memory copies and rejects topologies that violate data-boundary, external-network, scaling, runtime-isolation, memory or hop constraints. It deliberately does not produce invented latency, price or quality scores. Those require benchmarks for a concrete model, hardware, provider and traffic distribution.

By the end you will be able to:

  • distinguish embedded inference, a private inference service and a remote model API;
  • map each topology’s process, network, trust and failure boundaries;
  • calculate lower-bound local model-memory duplication from process ownership;
  • state whether a memory budget is per process, host or placement group;
  • express allowed data egress as an ordered boundary;
  • separate external-network independence from private-network availability;
  • explain why embedded runtimes do not scale independently from application replicas;
  • explain why a service can batch across callers but adds queue/transport failure;
  • treat remote-provider capacity as external, not infinite;
  • design deadlines, cancellation, backpressure and retry rules end to end;
  • preserve release identity across process/provider boundaries;
  • secure private services with workload identity, authorization and encryption;
  • benchmark quality, TTFT, throughput, memory and failure behavior under the real workload;
  • choose hybrid task placement without collapsing capabilities;
  • recognize when constraints leave no feasible topology;
  • state what the runnable planner simplifies.

Run:

Terminal window
cd rust/rust-ai-engineering
cargo run -q -p mosaic-runtime --example inference_topology

The scenario declares:

application replicas 4
private inference-service replicas 2
resident model bytes per copy 6 GiB
local model-memory budget 16 GiB across the placement group
maximum data boundary private network
independence from external network required
independent model scaling required
runtime failure isolation required
maximum logical inference hops 1

The planner returns:

TopologyLocal copiesLocal model bytesFeasible?Why
Embedded424 GiBNomemory, independent scaling, failure isolation
Private inference service212 GiBYessatisfies declared constraints
Remote API00Noexternal data boundary and network dependency

This does not declare the private service fastest or cheapest. It proves it is the only topology among the three that satisfies this exact constraint set. When the tests relax data, scaling, isolation and memory requirements, all three become feasible.

application process
├─ request/harness/business logic
├─ tokenizer/media processor
├─ inference runtime
└─ model state + caches

Inference is an in-process function or library call. The runtime may use CPU, GPU, Metal, CUDA, ONNX Runtime, Candle, llama.cpp bindings or another native backend.

application process(es)
→ private protocol/network
→ separately deployed inference process(es)
├─ admission queue
├─ batch/scheduler
├─ runtime
└─ model state + caches

The service may be a sidecar, daemon on one host, cluster service or dedicated accelerator fleet. This chapter’s planner models a private-network service. A same-host Unix socket sidecar has different hop and failure details and should be represented as another profile rather than quietly relabelled.

application
→ egress/proxy/public or private provider link
→ external provider control/data plane
→ provider-selected serving runtime

You manage the client and product harness. The provider manages opaque portions of model delivery, scheduling and hardware. Private connectivity can reduce exposure to the public Internet, but data still crosses an organizational/provider trust boundary.

PropertyEmbeddedPrivate serviceRemote API
Minimum logical inference hops011
Model bytes owned locallyyesyesno
Shares model across app replicasno, unless runtime architecture does soyesprovider-managed
Independent model scalingnoyesprovider-managed
Runtime crash isolationnoyesyes from app process
External network dependencynono for self-contained private deploymentyes
Data boundarysame processprivate networkexternal provider
Direct access to internalsstrongeststrong if self-hostedlimited by provider
Operational burdencoupled to appseparate serving platformvendor/client integration

Each cell needs qualification. “No network hop” does not mean no queueing or device contention. “Runtime crash isolated” does not mean product impact is isolated; the dependency can still be unavailable. “No local model bytes” does not mean no memory, tokenization or media-processing cost.

The runnable type is:

pub struct TopologyRequirements {
pub application_replicas: u32,
pub inference_service_replicas: u32,
pub model_resident_bytes: u64,
pub local_model_memory_budget_bytes: u64,
pub maximum_data_boundary: DataBoundary,
pub external_network_independence_required: bool,
pub independent_model_scaling_required: bool,
pub runtime_failure_isolation_required: bool,
pub maximum_network_hops: u8,
}

Every count/byte budget must be non-zero. Arithmetic uses checked_mul.

These fields are constraints, not preferences. If remote inference is legally forbidden, a price discount should not override it. If no topology passes, return no feasible result and change the requirements, model or architecture explicitly.

pub enum DataBoundary {
SameProcess,
PrivateNetwork,
ExternalProvider,
}

The enum is ordered by widening trust scope for this planner:

same_process < private_network < external_provider

This is still coarse. Production policy may distinguish:

  • same memory space;
  • same host/Unix socket;
  • same namespace;
  • same VPC/region;
  • same legal entity;
  • approved subprocessor;
  • geographic residency;
  • content versus derived embeddings;
  • encrypted versus confidential-compute boundary.

Extend the type and policy. Do not encode these as comments beside a Boolean named private.

The planner asks whether operation must be independent of an external provider/network. A private service satisfies this narrow condition, but it still depends on internal networking, DNS/service discovery, certificates, accelerator hosts and artifact availability.

“Offline” is ambiguous. Define the failure you must survive:

public Internet outage?
provider outage?
entire private network outage?
single host disconnected?
control plane unavailable but data plane warm?

Then test that event.

The example treats model_resident_bytes as six GiB per runtime copy:

embedded copies = application replicas = 4
embedded bytes = 4 × 6 GiB = 24 GiB
service copies = inference-service replicas = 2
service bytes = 2 × 6 GiB = 12 GiB
remote local copies = 0
remote local model bytes = 0

The 16 GiB budget is explicitly total local model memory across the compared placement group. If your budget is per host, the arithmetic must model placement and copies per host. Summing memory across four different hosts does not prove any individual host is over capacity.

model_resident_bytes must include the measured steady-state components relevant to capacity:

  • weights after loading/conversion;
  • runtime graph/allocator state;
  • tokenizer/processor if material;
  • KV cache reservation;
  • scratch/workspace buffers;
  • batch-dependent activations;
  • device-driver/context overhead;
  • fragmentation and safety headroom.

The planner uses a fixed number as a transparent lower-level input. It does not estimate these components. Feed it measurements from the target runtime.

Operating-system page cache or memory mapping may share physical read-only pages between processes. GPU contexts, converted weights, runtime arenas and caches may still duplicate. Measure proportional set size and device memory; do not simply multiply file size or assume perfect sharing.

0 means no local model residency. The provider still allocates model/cache memory and charges or rate-limits accordingly. Your client still uses memory for requests, streaming buffers, media and retries.

Step 3: model topology properties honestly

Section titled “Step 3: model topology properties honestly”

The code derives:

pub struct TopologyProperties {
pub local_model_copies: u32,
pub local_model_memory_bytes: u64,
pub minimum_network_hops: u8,
pub data_boundary: DataBoundary,
pub depends_on_external_network: bool,
pub independently_scalable: bool,
pub runtime_failure_isolated: bool,
}

The hop is a minimum logical application-to-inference hop. Real paths can include:

client middleware
→ service mesh sidecar
→ load balancer
→ gateway
→ inference router
→ worker

Retries multiply attempts. TLS, serialization, queueing and streaming flush behavior contribute latency. Measure them separately rather than assigning “1 ms per hop.”

Embedded inference scales with the app:

more web replicas → more model copies
fewer web replicas → less model capacity

That can be ideal for a small CPU classifier with aligned traffic. It is wasteful when stateless web requests scale differently from a six-GiB generator.

A service separates:

application replicas based on HTTP/business load
inference replicas based on token/pair/media workload and accelerator capacity

Independent scaling is not automatically effective. Model cold start may take seconds or minutes; accelerators may be unavailable; scale-to-zero can destroy latency. Maintain warm capacity based on measured startup and demand.

An embedded native-runtime panic, abort, allocator failure or device fault can terminate or poison the application process. Rust memory safety does not make foreign kernels, drivers or model parsers infallible.

A separate process isolates address space and restart policy. It also creates dependency failures: connection refusal, timeout, overload, version skew and partial streaming. Isolation changes the failure shape; it does not eliminate failure.

Embedded is compelling when:

  • the model is small enough for each required process/host;
  • same-process/same-device data locality is important;
  • zero inference-network hops materially helps;
  • offline or edge execution is required;
  • traffic is modest or aligned with application replicas;
  • one release can ship with the app;
  • the runtime has a safe supported Rust/native binding.

Design requirements:

  1. Verify the model bundle before activation.
  2. Load once into shared immutable state such as Arc<Model>.
  3. Bound concurrent inference with semaphores.
  4. move blocking/accelerator work off async executor workers.
  5. serialize access if the runtime/session is not thread safe.
  6. enforce context/media/output bounds before allocation.
  7. make cancellation semantics explicit; dropping a future may not stop a native kernel.
  8. distinguish startup/readiness from liveness.
  9. expose release identity and device/runtime metadata.
  10. drain work before process shutdown where possible.

An embedded CPU runtime may create its own intra-op/inter-op pools. If every web process and runtime session creates many threads, the host can oversubscribe dramatically. Configure and measure:

application workers
× model sessions per process
× inference threads per session

ONNX Runtime documents session/thread-pool and allocator controls because defaults interact with deployment shape. There is no universal best thread count.

Every application restart reloads the model. Rolling out unrelated web code can churn accelerator memory. Readiness must stay false until verification and warm-up pass. Large embedded artifacts can make horizontal autoscaling too slow for bursts.

A service is compelling when:

  • many callers share one model;
  • accelerators should be pooled;
  • continuous/dynamic batching improves utilization;
  • model/app scaling and releases differ;
  • runtime faults need process isolation;
  • one platform serves multiple language stacks;
  • centralized admission and observability are valuable.

The API is a compatibility boundary. Version:

  • request/response schema;
  • capability-specific endpoint;
  • release identity;
  • token/media/byte limits;
  • streaming event format;
  • usage and stop reasons;
  • error taxonomy;
  • idempotency/retry semantics.

The service needs bounded:

request bytes
decoded media
input tokens
reserved output
queue length
active sequences
KV/cache memory
batch tokens/pixels/frames
tenant concurrency and usage

Return overload honestly. An unbounded queue changes overload into latency and OOM.

  • embeddings/classification can batch complete requests with similar shapes;
  • rerankers batch query/candidate pairs;
  • generators need prefill/decode scheduling and per-sequence KV state;
  • image/video generation has long iterative steps and large output buffers.

A universal Vec<Request> batch hides the resource unit. Admission should account in tokens, pixels, frames, samples, steps and expected resident bytes.

Carry one absolute deadline through every hop. If each layer starts a fresh 30-second timeout, a request can exceed the user’s budget.

Client disconnect is evidence of lost interest, not proof that compute stopped. Propagate cancellation to the scheduler, remove queued work, and define whether active accelerator kernels are interruptible. Record wasted work.

Retries are safe only when:

  • the operation has no side effect, or has an idempotency key;
  • attempt budgets are bounded;
  • the original deadline remains;
  • overload responses use backoff/jitter;
  • partial streamed output is not duplicated.

Never retry every timeout immediately; that amplifies overload.

“Private network” is not authorization. Use:

  • workload identity and mutually authenticated transport;
  • per-tenant capability authorization;
  • quotas and cost accounting;
  • request/body/media bounds before decoding;
  • model-route allowlists;
  • no arbitrary filesystem/URL inputs;
  • redacted structured logs;
  • least-privilege artifact access;
  • network policy;
  • signed/verified images and model artifacts.

Remote APIs are compelling when:

  • a managed/frontier capability is required;
  • self-hosting hardware/runtime expertise is not justified;
  • demand is bursty and provider limits fit;
  • time to first implementation matters;
  • the provider’s data, residency and retention terms satisfy policy;
  • multiple providers can be evaluated/routed.

The application still owns:

  • input validation and minimization;
  • secrets and credential rotation;
  • authorization to invoke a model/tool;
  • deadlines, rate limits, retries and circuit breaking;
  • schema validation and safe output handling;
  • release metadata/change detection;
  • evaluation and incident response;
  • cost controls;
  • fallback truthfulness.

Providers expose quotas, per-minute units, concurrency limits, overload and regional incidents. Design a local admission queue that is bounded and fair. Separate retryable transport failures from invalid requests, safety refusals and exhausted budgets.

Before sending:

  • remove fields the task does not need;
  • avoid secrets and unrelated conversation history;
  • use tenant-approved provider/region;
  • understand retention/training controls;
  • hash or pseudonymize only when the task still works and policy allows;
  • treat embeddings and outputs as potentially sensitive derived data.

Encryption in transit does not change who receives plaintext at the endpoint.

Record requested and resolved model plus runtime fingerprint when available. A stable API schema does not prove stable model behavior. Apply the release audit from the previous chapter and maintain canaries.

One product can use all three:

embedded small classifier
→ route/abstain locally
private embedding + reranking service
→ keep corpus inside private boundary
remote generator
→ only approved minimized evidence

Or:

remote primary for highest quality
local verified fallback for outage

Fallback is a different release and often a different capability. Do not report success if the fallback cannot meet the output/safety contract. Re-evaluate routing and fallback paths, including the transition itself.

Avoid per-request arbitrary topology choice. Policy should consider:

  • tenant/data classification;
  • required capability;
  • latency/quality target;
  • current capacity;
  • external-provider permission;
  • release approval;
  • cost budget.

Record the selected route and reason without sensitive content.

Once constraints identify feasible options, benchmark each on the same cases and load shape.

  • task success and instruction following;
  • calibration/abstention;
  • retrieval/reranking metrics;
  • multimodal fidelity;
  • shifted/adversarial slices.
  • client-observed end-to-end;
  • queue time;
  • serialization/upload;
  • preprocessing;
  • prefill/first token;
  • inter-token latency;
  • terminal validation;
  • items/tokens/pairs/frames per second;
  • p50/p95/p99 under steady and burst load.
  • host/device memory and fragmentation;
  • model cold start/warm-up;
  • power/cost per successful task;
  • failure/retry/wasted-compute rate;
  • autoscaling lag;
  • deployment/rollback time;
  • operator burden.

Warm-cache microbenchmarks do not answer cold-start or overload behavior. Test realistic input lengths, concurrency, cancellation and provider limits.

SymptomLikely topology checks
App replicas OOM during rolloutembedded model copies, concurrent old/new pods, runtime arena/cache
Private service p99 spikesbounded queue, prefill/decode contention, batch policy, network retries
Remote costs doubleretry amplification, output length, fallback duplication, provider route
Cancellation does not free GPUscheduler/kernel interruptibility and queued-vs-active state
Local mode corrupts async latencyblocking inference running on executor workers
Remote works, private fails qualitytokenizer/template/release mismatch, not topology alone
Service is “healthy” but rejects all workreadiness/admission capacity versus liveness
Sensitive tenant reaches remoteroute authorization/data classification failed before adapter

Diagnose by stage and release identity. “Network issue” and “model slow” are not root causes.

Change application replicas to two and local budget to 16 GiB. Disable independent scaling and fault-isolation requirements but retain private data and external-network independence. Which topologies pass?

Solution

Embedded uses 2 × 6 = 12 GiB, stays same-process, and no longer violates scaling/isolation, so it passes. The private service uses 12 GiB and passes. Remote still violates data boundary and external network independence.

Extend the planner with host count, copies per host and per-host memory budget. Reject a plan where fleet total fits but one host is overcommitted. Include old+new replicas during rolling update.

Add SameHostSidecar with one Unix-socket hop, same-host data boundary, independent process failure but scaling coupled to the app. Explain which existing Boolean properties are insufficient.

Implement a Deadline based on monotonic time. Simulate client → gateway → service → worker and prove each hop uses remaining time rather than resetting the budget.

Model 100 clients timing out together against an overloaded service. Compare immediate retry, exponential backoff with jitter, and no retry. Count attempts and useful completions.

Design a typed policy that allows public text to a remote generator, private documents only to a private reranker, and secrets nowhere. Test an indirect prompt instruction attempting to override the route.

  • Why can embedded inference multiply model memory with app replicas?
  • What does the planner’s memory budget aggregate?
  • Why is a private network not an authorization boundary?
  • What new failure modes does process isolation introduce?
  • Why can a larger inference queue reduce reliability?
  • Which operations can safely retry after partial streaming?
  • Why does remote zero local model memory not mean zero resource cost?
  • What measurements must follow constraint filtering?
  • When should a hybrid route fail instead of falling back?
  • topology requirements are explicit and reviewed;
  • data boundaries distinguish process, private network and external provider;
  • memory ownership/copies use checked arithmetic and measured resident bytes;
  • per-host versus fleet budgets are not confused;
  • embedded blocking work is isolated from async executor workers;
  • service queues, batches, active work and tenants are bounded;
  • absolute deadlines and cancellation propagate end to end;
  • retries preserve idempotency, attempt budgets and original deadlines;
  • private services authenticate and authorize every workload;
  • remote inputs are minimized and provider policy is approved;
  • release identity travels across every boundary;
  • overload/readiness/fallback states remain truthful;
  • feasible candidates are benchmarked on quality, tails, resources and failure;
  • rollback covers runtime, route, model, processor and capacity.
  • Hugging Face, Candle: a native Rust ML framework focused on performance and lightweight deployments, with CPU/GPU support and model examples.
  • ONNX Runtime, Performance tuning: latency, throughput, memory and application/model size as deployment measurement dimensions.
  • ONNX Runtime, Thread management: intra/inter-op thread pools and avoiding contention across sessions.
  • ONNX Runtime, Memory consumption: allocators and sharing arenas across sessions.
  • vLLM, official project documentation: a model-serving system with scheduling, continuous batching and an API boundary.
  • KServe, official documentation: inference services, autoscaling, model serving and staged deployment features.
  • The earlier blocking/media/accelerator scheduling chapter: isolating blocking work and bounding permits in an async Rust service.
  • The earlier release-identity chapter: preserving actual model/provider identity through topology changes.

The planner models one embedded process, one private-network service shape and one external-provider shape. It does not model per-host placement, shared pages, multi-GPU sharding, service meshes, sidecars, confidential computing, price, latency, quality or availability probability. Its “network hops” are minimum logical boundaries. The six-GiB resident input must come from measurement. The planner’s value is that it rejects architecture by declared invariants before subjective optimization begins.

The next chapter derives memory effects from numeric formats, quantization, batching and device placement, giving the topology planner better measured inputs.