What This Is
A stable data contract for hosts that wrap Lash behind HTTP, queues, callbacks, workflow handlers, or another process. It is not a server implementation and it does not own auth, tenant routing, billing, rate limits, or storage.
Service Boundary
The DTOs are for host-owned service edges. If your code calls Lash in-process, use the normal facade in API basics instead.
A web service, worker, workflow activity, queue consumer, remote provider, or host-owned tool service must exchange Lash-owned fields with another process.
For Restate, Temporal, or another workflow runtime, key the handler by the stable turn_id or operation id and run the turn with .turn_id(...).effects(&controller). Do not add a second durable submitted/running work-item lifecycle around the turn; the workflow runtime owns in-flight replay/cancellation, Lash owns the session execution lease and final turn commit, and product storage owns user-visible rows.
The caller already has a LashCore and LashSession in memory. Do not serialize and deserialize just to call local turns.
Minimal Examples
Wrap Lash's DTO in your host envelope. Service-specific fields belong in the wrapper or in metadata, not as local forks of Lash sub-objects.
use lash::remote::REMOTE_PROTOCOL_VERSION;
use lash::remote::turn_input::{RemoteInputItem, RemoteTurnInput, RemoteTurnRequest};
let request = RemoteTurnRequest {
protocol_version: REMOTE_PROTOCOL_VERSION,
session_id: chat_id.clone(),
turn_id: turn_id.clone(),
idempotency_key: Some(idempotency_key.clone()),
input: RemoteTurnInput {
protocol_version: REMOTE_PROTOCOL_VERSION,
items: vec![RemoteInputItem::Text {
text: "Summarize this task.".to_string(),
}],
image_blobs_base64: Default::default(),
protocol_turn_options: None,
trace_turn_id: Some(trace_turn_id.clone()),
prompt_layer: None,
},
tool_grants: Vec::new(),
model_intent: None,
metadata: Default::default(),
};
request.validate()?;
Process starts use the same rule. The optional env_spec is a closed Lash DTO: plugin-owned options plus runtime policy. It is not a product metadata bag. A start also carries a required disposition (RemoteRecoveryDisposition) — the declared recovery contract that mirrors RecoveryDisposition across the wire (ADR 0019). It has no wire default, so an envelope that omits it does not deserialize; an external placeholder like the one below declares ExternallyOwned.
use std::collections::BTreeMap;
use lash::remote::REMOTE_PROTOCOL_VERSION;
use lash::remote::processes::{
RemoteProcessExecutionEnvSpec, RemoteProcessExecutionPolicy, RemoteProcessInput,
RemoteProcessModelLimits, RemoteProcessModelSpec, RemoteProcessOriginator,
RemoteProcessPluginOptions, RemoteProcessStartRequest, RemoteRecoveryDisposition,
};
use serde_json::json;
let request = RemoteProcessStartRequest {
protocol_version: REMOTE_PROTOCOL_VERSION,
id: "process-01".to_string(),
input: RemoteProcessInput::External {
metadata: json!({ "source": "scheduler" }),
},
disposition: RemoteRecoveryDisposition::ExternallyOwned,
env_spec: Some(RemoteProcessExecutionEnvSpec {
plugin_options: RemoteProcessPluginOptions {
plugins: BTreeMap::from([(
"snapshot-tools".to_string(),
json!({ "snapshot_ref": "tool-authority:sha256:abc123" }),
)]),
},
policy: RemoteProcessExecutionPolicy {
provider_id: "example-provider".to_string(),
model: RemoteProcessModelSpec {
id: "example-model".to_string(),
variant: None,
limits: RemoteProcessModelLimits {
context_window_tokens: 128_000,
output_token_capacity: Some(8_192),
},
},
..Default::default()
},
}),
originator: RemoteProcessOriginator::Host,
wake_target: None,
grant: None,
event_types: Vec::new(),
};
request.validate()?;
Contract Map
Use the smallest envelope that matches the boundary you are crossing.
RemoteTurnRequest carries session_id, turn_id, optional idempotency key, RemoteTurnInput, remote tool grants, optional model intent, and metadata.
RemoteTurnResult carries status, RemoteTurnOutcome, safe/raw assistant output, complete RemoteUsage buckets, execution summary, tool-call summaries, issues, collected activities, and metadata.
RemoteLlmRequest and RemoteLlmResponse mirror the provider-facing request/response shape: messages, attachments, tools, tool choice, output spec, generation options, request metadata, complete usage buckets, diagnostics, terminal reason, and provider metadata.
RemoteTurnActivity wraps semantic turn events with monotonically assigned per-stream sequence, event id, correlation id, and one flattened RemoteTurnEvent. The wire enum is a deliberately narrower mirror of the in-process TurnEvent: four host-internal variants collapse into a single RuntimeDiagnostic { kind, data } so the versioned contract does not churn on every runtime-internal signal. It is still a turn convenience item, not the reconnect cursor. See Streaming and reconnect for NDJSON and SSE framing guidance, and Reporting channels for how the mirror relates to the other reporting surfaces.
RemoteSessionCursor, RemoteSessionObservation, RemoteSessionObservationEvent, and RemoteLiveReplayGap are the remote reconnect surface. They carry opaque cursor strings, session revisions, bounded replay events, explicit remote observation snapshots, and gap recovery without serializing a full SessionReadView. See Streaming and reconnect for the host/frontend folding recipe.
RemotePromptLayer, prompt templates, slots, contributions, and gates let a service pass prompt overrides without inventing a parallel prompt schema.
RemoteTriggerOccurrenceRequest, RemoteTriggerOccurrenceRecord, RemoteTriggerEmitReport, RemoteTriggerSubscriptionFilter, RemoteTriggerRegistration, and RemoteTriggerTargetSummary mirror trigger emission, stored occurrence replay, match reports, subscription listing, and target summaries. Subscription targets use RemoteProcessDefinitionIdentity, the same full module/host/process-ref/process-name identity used by process summaries and filters.
RemoteProcessStartRequest, process records, summaries, event DTOs, work snapshots, await/cancel/signal requests, and list filters mirror durable process admin. Process summaries and list filters use RemoteProcessDefinitionIdentity when they refer to a Lashlang process definition. RemoteProcessStartRequest.env_spec is typed as RemoteProcessExecutionEnvSpec, a closed contract containing only plugin_options and policy.
Remote Tools
Remote tool grants expose callable host operations through Lash's normal tool catalog while execution happens elsewhere.
RemoteToolGrant defines the advertised name, schemas, projection overrides, output contract, examples, activation, scheduling, retry policy, and Lashlang call-path bindings. Host-owned bindings decide how granted tools execute.
The remote protocol describes granted callable tools; it does not define a remote tool execution transport. Tool execution is host-owned: implement a normal ToolProvider, and call HTTP services, queues, or callbacks inside that provider when your deployment needs remote work.
A binding in the grant's bindings map resolves to a RemoteCallPathBinding whose module_path plus operation forms the remote call path. Duplicate call paths are rejected by RemoteToolGrant::validate_all(...).
Validation Rules
The DTOs validate protocol and shape, not caller authorization. Keep auth and tenancy checks in your service layer.
Top-level envelopes reject unsupported protocol_version; nested turn input/activity versions must match the parent envelope where applicable.
Remote turn requests/results require non-empty session_id and turn_id. LLM requests/responses require non-empty request_id. Model intents require non-empty model.
LLM messages must contain at least one content block. Tool-call blocks require call_id and tool_name; tool-result blocks require call_id.
Attachments require a non-empty MIME type. Attachment references require non-empty id and mime. Image blobs require non-empty ids and base64 payloads.
RemoteGenerationOptions.output_token_cap must be greater than zero when present. Temperature and top-p are strings so provider-specific precision can round-trip.
Trigger occurrence requests require non-empty source_type, source_key, and idempotency_key. Trigger subscription filters validate any supplied RemoteProcessDefinitionIdentity target. Trigger filters and emit reports validate protocol version but leave authorization and tenant scoping to the boundary host.
Process start environments are closed: env_spec accepts only plugin_options and policy. Unknown fields, including old product metadata such as tool_grants or resolved_tool_bindings under env_spec, fail deserialization. Model limits must be positive when supplied.
Remote tool grants require a non-empty id and name, plus non-empty keys on any bindings present; schemas and bindings are optional. Duplicate module_path / operation call paths are rejected by RemoteToolGrant::validate_all(...).
Core Conversions
The standalone crate has an optional core-conversions feature for adapters that already depend on lash-core. The facade's lash::remote reexport is for DTOs; import lash-remote-protocol directly when you need conversion helpers.
Conversions cover remote-safe TurnInput fields and RemoteTurnResult::from_core(...) for completed turns plus collected activities.
RemoteLlmRequest::from_core(...), request conversion, and response conversion preserve messages, attachments, tools, output specs, terminal reasons, all usage buckets, replay metadata, and provider metadata where the core types carry them.
RemoteTurnActivitySink<W> serializes remote activities as newline-delimited JSON and records sink write errors for the host to inspect. It is a DTO adapter, not an HTTP route; the host still owns transport, auth, and reconnect policy.
Conversions cover trigger occurrence requests, stored occurrence records, emit reports, subscription filters, registrations, and target summaries when the standalone crate is built with core-conversions.
Conversions cover process inputs, records, events, summaries, admin requests, and typed RemoteProcessExecutionEnvSpec when the standalone crate is built with core-conversions.
Where Next
This page owns the remote DTO contract. Use the facade guides for local embedding behavior, process architecture docs for durable execution, and the provider docs for transport normalization.