Environment sync reports include per-component cache decisions, storage
identity, rebuild reason, exact changed identity edges, and byte accounting.
Environment plan responses require adapter_identity,
adapter_implementation_version, and adapter_distribution_digest; these are
the same fields serialized by Rust, CLI JSON/NDJSON, and MCP structured content.
POST /v1/lanes/{lane_or_id}/environment/promote accepts component and
output and returns the durable publication and successor-generation report.
Artifact resolution, inspection, verification, quarantine, reachability,
workspace accounting, and source export routes serialize the same public Rust
report types used by CLI JSON/NDJSON and MCP structured content.
Runtime reconciliation resolves the configured Docker, Podman, or Colima
provider. With Colima autostart enabled, the existing reconcile route may start
the configured profile before creating lane resources, but it never downloads
runtime tools. Trail-managed provisioning is confined to the explicit local
CLI/Rust setup operation; provider setup/status reports themselves remain
CLI/Rust-only.
Lane spawn reports include the shared layered-backend prerequisite report.
The daemon serves JSON HTTP routes under /v1.
Accepted HTTP connections use 30-second read/write timeouts; slow requests
receive 408 Request Timeout without stopping the listener. Total
requests larger than 16 MiB, including headers and body, are rejected before
routing.
Trail accepts origin-form HTTP/1.0 or HTTP/1.1 request lines and
fixed-length requests only: malformed request lines, malformed or unterminated
header lines, bare CR/LF request-head line endings, header names with
surrounding whitespace, missing header terminators, duplicate or non-decimal
Content-Length, bodies without Content-Length, body length mismatches, and
Transfer-Encoding requests are rejected before routing.
Origin-form request targets must start with / and cannot contain fragments,
control characters, or backslash separators.
Requests with an Origin header are accepted only for well-formed loopback
origins such as localhost, 127.0.0.1, and ::1; invalid ports and opaque
origins such as null are rejected.
Routed requests must also include a Host header with a loopback host such as
localhost, 127.0.0.1, or [::1]; missing, malformed, non-loopback, and
invalid-port hosts are rejected before route handling.
Daemon listener connections are rate-limited per peer. The default limit is
600 requests per 60 seconds; excess requests receive 429 Too Many Requests.
Mutation request bodies are strict JSON objects. Unknown fields are rejected
with 400 Bad Request; they are not silently ignored. Top-level request
schemas in /v1/openapi.json set additionalProperties: false to match that
runtime behavior; patch edit/file variants are also emitted as strict nested
schemas.
For retry-safe mutation requests, send:
Idempotency-Key: <stable unique key>
Trail replays the first stored response when the same key is reused with the
same method, path, and body. Reusing the key for different request content
returns 400 Bad Request. Keys must be 1-200 non-control characters.
Unauthorized and forbidden responses are not cached.
Replayed mutation responses still produce audit rows, marked with
idempotency_replay: true in the audit summary.
Non-GET mutation attempts, including unauthorized and forbidden attempts, are
recorded in the local external_mutation_audit table with actor label,
method/path, status, inferred lane/ref when available, and a small redacted
result summary. Turn-scoped mutation failures can still be attributed through
the turn id in the request path. Actor labels identify the HTTP auth path, such
as http:bearer, http:x-trail-token, or http:no-auth; raw request bodies
and tokens are not stored.
GET /v1/health is unauthenticated. Other routes require a token unless the daemon starts with --no-auth.
Headers:
Authorization: Bearer <token>
x-trail-token: <token>
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/health |
Service liveness. |
| GET | /v1/openapi.json |
OpenAPI document. |
| GET | /v1/doctor |
Workspace diagnostics. |
| GET | /v1/status |
Current status. |
| POST | /v1/record |
Record workspace changes. |
| GET | /v1/diff |
Diff range, roots, or dirty worktree. |
| GET | /v1/timeline |
Recent operations. |
| GET | /v1/why |
Line provenance. |
| GET | /v1/history |
File or line history. |
| GET | /v1/code-from |
Trace code from selector. |
| GET | /v1/config |
List config. |
| POST | /v1/config |
Set config. |
| GET | /v1/config/{key} |
Get config. |
| GET | /v1/ignore |
List ignore rules. |
| POST | /v1/ignore/patterns |
Add ignore rule. |
| DELETE | /v1/ignore/patterns |
Remove ignore rule. |
| POST | /v1/ignore/check |
Check ignored path. |
| POST | /v1/guardrails/check |
Preflight guardrails. |
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/lanes |
List lanes. |
| POST | /v1/lanes |
Spawn lane. |
| GET | /v1/lanes/{lane_or_id} |
Show lane. |
| DELETE | /v1/lanes/{lane_or_id} |
Remove lane. |
| GET | /v1/lanes/{lane_or_id}/status |
Lane status. |
| GET | /v1/lanes/{lane_or_id}/review |
Review packet. |
| GET | /v1/lanes/{lane_or_id}/contribution |
Review bundle. |
| GET | /v1/lanes/{lane_or_id}/gates |
Gate history. |
| GET | /v1/lanes/{lane_or_id}/readiness |
Merge readiness. |
| GET | /v1/lanes/{lane_or_id}/refresh-preview?target=main |
Preview refreshing lane onto target branch. |
| GET | /v1/lanes/{lane_or_id}/handoff |
Handoff packet. |
| GET | /v1/lanes/{lane_or_id}/workdir |
Workdir path. |
| GET | /v1/lanes/{lane_or_id}/diff |
Lane branch diff. |
| POST | /v1/lanes/{lane_or_id}/read-file |
Read lane file. |
| POST | /v1/lanes/{lane_or_id}/hydrate |
Hydrate sparse workdir paths. |
| POST | /v1/lanes/{lane_or_id}/sync-workdir |
Sync workdir. |
| POST | /v1/lanes/{lane_or_id}/record |
Record lane workdir. |
| POST | /v1/lanes/{lane_or_id}/exec |
Run a managed command through the configured host or no-mount Colima backend and checkpoint validated source changes. |
| POST | /v1/lanes/{lane_or_id}/exec/cancel |
Cancel one owned Colima execution before candidate import; accepts optional execution_id. |
| POST | /v1/lanes/{lane_or_id}/rewind |
Rewind lane branch. |
| POST | /v1/lanes/{lane}/merge |
Dry-run or explicitly direct-merge this lane into body field into. |
| POST | /v1/lanes/{lane_or_id}/tests |
Run test gate. |
| POST | /v1/lanes/{lane_or_id}/evals |
Run eval gate. |
| POST | /v1/lanes/{lane_or_id}/patches |
Apply lane patch. |
| GET | /v1/lanes/{lane_or_id}/environment/plan |
Return the normalized component plan and exact adapter implementation/distribution identity. |
| POST | /v1/lanes/{lane_or_id}/environment/resolve |
Resolve or reuse one pinned component snapshot. Body: component, optional path and refresh. |
| POST | /v1/lanes/{lane_or_id}/environment/resolve-all |
Resolve or reuse every incomplete component snapshot. Body: optional path and refresh. |
| POST | /v1/lanes/{lane_or_id}/environment/source-export |
Export one declared generated-source subtree through normal lane source writes. Body: component and export. |
| GET | /v1/lanes/{lane_or_id}/environment/runtime/status |
Return persisted runtime state without contacting the configured provider. |
| POST | /v1/lanes/{lane_or_id}/environment/runtime/reconcile |
Resolve the configured provider, optionally autostart Colima, and reconcile lane-private OCI resources. |
| POST | /v1/lanes/{lane_or_id}/environment/runtime/stop |
Stop Trail-owned containers while retaining private networks and volumes. |
Patch requests accept either native edits or compatibility files; provide
one non-empty array, not both.
GET /v1/lanes/{lane_or_id}/status includes base_status when Trail can
resolve the workspace default branch. It reports the target branch/ref, target
change, lane base change, operations_behind, and whether the lane base is
stale.
POST /v1/lanes accepts workdir_mode values auto, virtual, sparse,
native-cow, portable-copy, fuse-cow, nfs-cow, and dokan-cow.
native-cow is strict, portable-copy permits per-file byte-copy fallback,
and auto tries the former before restarting as the latter. fuse-cow creates an empty workdir mountpoint and
records the fuse backend; a runtime such as trail agent start --workdir-mode fuse-cow mounts the FUSE view and keeps it alive while the
agent runs. The response includes requested_workdir_mode, resolved
workdir_mode, workdir_backend, optional materialization, sparse_paths,
and transparent_cow_available.
On macOS, nfs-cow reports workdir_backend: "nfs" and requires no
macFUSE installation. On Windows, dokan-cow reports workdir_backend: "dokan".
POST /v1/lanes/{lane_or_id}/exec requires a non-empty command argv and
accepts optional turn_id and timeout_secs (1 through 86400; Colima default
3600). An open same-lane turn receives the checkpoint and session/turn/trace
provenance. The response identifies the host/Colima execution backend,
succeeded/command_failed/timed_out classification, lifecycle phases,
projection/import receipts, checkpoint, and cleanup. Infrastructure and
candidate-validation failures remain HTTP errors rather than command exits:
EXECUTION_VALIDATION_FAILED uses 422 and
EXECUTION_INFRASTRUCTURE_FAILED uses 503.
The daemon dispatches this long-running route on a workspace-scoped worker so
the same authenticated server can continue accepting status and cancellation
requests while the execution is active.
POST /v1/lanes/{lane_or_id}/exec/cancel accepts an optional execution_id.
When omitted, exactly one cancellable execution must exist for the lane. A
successful response records the prior phase, profile/instance, owned
process-group termination, and namespace cleanup. The cancelled blocking
request returns HTTP 409 with EXECUTION_CANCELLED; no candidate state is
imported and unrelated profile processes are preserved.
POST /v1/lanes/{lane_or_id}/hydrate accepts the same body as path-scoped
sync-workdir, but requires at least one paths entry.
POST /v1/lanes/{lane_or_id}/sync-workdir returns rescue_workdir when
force=true overwrites dirty materialized workdir files or replaces a
non-directory file at the lane workdir path. The rescue directory contains
copied recoverable regular files plus manifest.json.
POST /v1/lanes/{lane_or_id}/record accepts {"preview": true} to return a
non-committing record preview with changed paths, ignored paths, risky workdir
entries, oversized changed files, and policy allow/block details.
GET /v1/lanes/{lane_or_id}/refresh-preview?target=<branch> returns a
non-committing refresh/rebase preview with operations-behind, conflicts, changed
paths, and next steps.
POST /v1/lanes/{lane_or_id}/merge requires a JSON into target branch and
allows dry_run=true preflight. A non-dry-run merge into the workspace default
branch requires direct=true; otherwise enqueue with
POST /v1/lanes/merges/queue
and run the queue. The former branch-scoped merge-lane endpoint was removed.
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/artifacts/space |
CAS-aware workspace artifact accounting. |
| GET | /v1/artifacts/{artifact_id} |
Inspect one artifact envelope and its bindings, attestations, quarantine, reachability, and storage evidence. |
| GET | /v1/artifacts/{artifact_id}/reachability |
Summarize authoritative objects reachable from one envelope. |
| POST | /v1/artifacts/{artifact_id}/verify |
Verify at attach, sample, full, or reproduce level. |
| GET | /v1/artifact-quarantines |
List active and resolved nondeterminism quarantines. |
| GET | /v1/artifact-quarantines/{quarantine_id} |
Show retained divergence evidence. |
| POST | /v1/artifact-quarantines/{quarantine_id}/resolve |
Apply retain_private, accept_incumbent, accept_candidate, or retire_all. |
Resolution executes only a host-owned plan with pinned source and tool
identity. refresh: false reuses an existing valid snapshot; wall-clock time
alone never advances dependencies. Source export is an explicit guarded source
mutation and is distinct from artifact promotion or mounting.
| Method | Path | Purpose |
|---|---|---|
| GET/POST | /v1/sessions |
List or start sessions. |
| GET | /v1/sessions/current |
Current sessions. |
| GET | /v1/sessions/{session_id} |
Show session. |
| GET | /v1/sessions/{session_id}/context |
Session context. |
| POST | /v1/sessions/{session_id}/end |
End session. |
| GET/POST | /v1/approvals |
List or request approvals. |
| GET | /v1/approvals/{approval_id} |
Show approval. |
| POST | /v1/approvals/{approval_id}/decision |
Decide approval. |
| GET/POST | /v1/leases |
List or acquire leases. |
| DELETE | /v1/leases/{lease_id} |
Release lease. |
| POST | /v1/lanes/{lane_or_id}/claims |
Claim path. |
| GET/POST | /v1/anchors |
List or create anchors. |
| GET/DELETE | /v1/anchors/{anchor_id} |
Resolve or delete anchor. |
| GET/POST | /v1/lanes/merges/queue |
List or queue a lane merge. |
| POST | /v1/lanes/merges/queue/run |
Run queued lane merges. |
| GET | /v1/lanes/merges/queue/{selector}/explain |
Explain a queue item by queue id, lane id, or lane name. |
| DELETE | /v1/lanes/merges/queue/{selector} |
Remove a lane queue item. |
| GET | /v1/conflicts |
List conflicts. |
| GET | /v1/conflicts/{conflict_set_id}?limit=50 |
Show conflict with explanation evidence. |
| POST | /v1/conflicts/{conflict_set_id}/resolve |
Resolve conflict. |
Conflict explanations include the stored base_root, target_root, and
source_root snapshots used to reproduce the conflict, plus per-path
conflict_class values such as modify/modify, delete/modify,
rename/modify, binary, mode, and same_insertion_gap.
They also include known_resolutions for paths whose path/content conflict
signature matches a previously resolved conflict.
POST /v1/conflicts/{conflict_set_id}/resolve requires exactly one of take
or manual. Manual file values can be plain strings or objects with only
content, delete, and executable; unknown keys are rejected.
Bodyless mutation routes reject non-empty request bodies. This applies to path-only deletes such as lane removal, lease release, anchor deletion, and lane merge-queue removal.
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/lane/turns |
Begin turn. |
| GET | /v1/lane/events |
List events. |
| GET | /v1/lane/spans |
List spans. |
| GET | /v1/lane/spans/summary |
Span summary. |
| GET/POST | /v1/lane/runs |
List or pause runs. |
| GET | /v1/lane/runs/{run_id} |
Show run. |
| POST | /v1/lane/runs/{run_id}/resume |
Resume run. |
| GET | /v1/lane/spans/{span_id} |
Show span. |
| POST | /v1/lane/spans/{span_id}/end |
End span. |
| GET | /v1/lane/turns/{turn_id} |
Show turn. |
| POST | /v1/lane/turns/{turn_id}/messages |
Add message. |
| POST | /v1/lane/turns/{turn_id}/events |
Add event. |
| POST | /v1/lane/turns/{turn_id}/spans |
Start span. |
| POST | /v1/lane/turns/{turn_id}/patches |
Apply turn patch. |
| POST | /v1/lane/turns/{turn_id}/end |
End turn. |
- OpenAPI paths:
trail/src/server/openapi/paths - Route handlers:
trail/src/server/route - Request types:
trail/src/server/request_types