Skip to content

Core Concepts

This page defines the core abstractions in LatchGate: protected actions, the execution pipeline, and the key artifacts produced at each stage. For the higher-level motivation, see the Overview. For the system diagram and crate layout, see Architecture.

A protected action is any operation whose side effect matters outside the agent process: an HTTP call, a database write, an email, a queue publish, a file store write.

If an operation is declared as a protected action in LatchGate, its side effect cannot occur outside LatchGate’s control path. This is the core no-bypass guarantee. It applies only to declared protected actions — LatchGate does not claim to sandbox the entire host.

Every protected action follows the same deterministic path. If any step fails, the action is denied.

Request in
├─ Drain guard
├─ Authentication (Lease + DPoP proof + jti replay check)
├─ Registry lookup (ActionSpec by action_id)
├─ Trust verification (provider module digest check)
├─ Input schema validation
├─ Canonicalization + request_hash (JCS / RFC 8785)
├─ Domain pre-check (non-blocking optimization — early reject for unknown domains)
├─ Budget snapshot + policy evaluation (OPA/Rego)
├─ Approval hold (if policy returns PendingApproval, HTTP 202)
├─ Budget debit (atomic — Redis Lua or SQLite exclusive lock)
├─ ExecutionGrant construction + Ed25519 signing
├─ Secret resolution + template expansion + RunTask build
├─ Grant re-validation + signature verification
├─ ExecutionIntent write (pre-dispatch evidence marker)
├─ WASM provider dispatch (fresh sandbox)
├─ Response schema validation
├─ Effect verification
├─ ExecutionReceipt construction + signing
├─ Evidence finalization (receipt + final audit, single SQLite txn)
Response out

The execution tail — grant re-validation through evidence finalization — is shared: both the auto-allow path and the human-approved path converge into it. There is exactly one WASM dispatch code path in the kernel (execute_authorized_plan) — approval and auto-allow cannot diverge at dispatch time.

A short-lived JWT issued at POST /v1/leases. The lease is bound to a caller-controlled key via DPoP (RFC 9449), meaning a stolen token without the private key is useless. The caller principal is determined server-side (never client-asserted), and scopes are constrained to the intersection of what the caller requests and what the identity provider permits.

The immutable, versioned definition of a protected action. It specifies the provider WASM module (by SHA-256 digest), the required I/O imports, input/output schemas, risk level, budget and approval policies, resource limits, and verifier kind. Actions are append-only once published — you create new versions, never mutate existing ones.

Most HTTP actions use the built-in http_api provider with a template manifest. The manifest declares URL pattern, method, headers, and body templates with {{variable}} placeholders. The kernel resolves them from the validated request before dispatching:

# github_create_issue — high risk, requires approval
action_id: "github_create_issue"
provider_module_digest: "builtin:http_api"
template:
method: POST
url_template: "https://api.github.com/repos/{{owner}}/{{repo}}/issues"
headers:
Accept: "application/vnd.github.v3+json"
body_template:
title: "{{title}}"
body: "{{body}}"
risk_level: "high"
secrets:
- name: "GITHUB_TOKEN"
required: true
egress:
profile: "proxy_allowlist"
allowed_domains: ["api.github.com"]

One provider binary, many actions. http_fetch (GET, low risk, no approval), github_read (GET template), and github_create_issue (POST template, high risk, approval required) all use the same http_api WASM module — each manifest configures it differently.

A short-lived authorization artifact issued by the kernel after all pre-dispatch checks pass. The grant binds the exact approved execution plan: specific targets, secrets, egress rules, budget snapshot, provider module digest, and expiry. It is Ed25519-signed immediately after construction; any field mutation between construction and dispatch is detectable via signature verification.

For approved actions, the grant additionally carries:

  • approved_by — operator identity, integrity-bound into the signature
  • operator_binding — JWK thumbprint of the operator’s DPoP key (cryptographic proof-of-possession)
  • approval_hash — SHA-256 over (approval_id, plan_hash) tying the grant to the exact approved plan

A pre-dispatch durable evidence marker. Written to SQLite before the WASM provider dispatches. If the process crashes between dispatch and receipt write, operators can detect intents without matching receipts and investigate whether the side effect occurred.

This is the difference between “we lost evidence” (detectable) and “we lost knowledge of what happened” (invisible). LatchGate makes it the former.

The durable result envelope. It records the grant it was issued under, what the provider returned, whether verification succeeded, and any effect evidence. Receipts are SHA-256-hashed, Ed25519-signed, and written transactionally with the final audit event. Receipts are chained via the audit event prev_hash for tamper evidence.

The client receives a success response only if the receipt + audit transaction commits. If evidence cannot be persisted, the client receives evidence_persistence_failed — never a false success.

Every provider executes as a .wasm module in a fresh wasmtime instance. The sandbox has zero ambient capabilities: no filesystem, no network, no syscalls. The provider communicates with external systems exclusively through host-mediated I/O imports. Secrets never enter the sandbox — the host I/O layer injects credentials into outgoing requests at the transport level.

Resource consumption is bounded by:

  • Fuel metering (CPU) — deterministic per-instruction cost, terminates runaway computation
  • Memory caps (linear memory) — allocation attempts beyond the cap fail
  • I/O call budget — per-execution limit on host import invocations
  • Epoch-based wall-clock deadline — interrupts pure-computation loops that have no .await points, enforced by a dedicated OS thread ticker (works even when the tokio runtime is blocked)

The host I/O layer is the bridge between a sandboxed WASM provider and external systems. It enforces:

  • Sink validation — every target URL/address checked against the grant’s allowlist before executing, with proper domain matching (allowed = "api.com" does NOT match evil-api.com)
  • Credential injection — secrets read from the kernel’s store, injected into outgoing requests at the transport level
  • SSRF protection — private IP blocking (loopback, RFC-1918, link-local, CGNAT, IPv6 loopback/ULA, IPv4-mapped IPv6), DNS pinning via reqwest::ClientBuilder::resolve() to close the DNS TOCTOU window while preserving TLS SNI, redirect blocking at transport
  • Per-call timeouts and response size limits

Available I/O interfaces (defined in WIT):

InterfaceTransportMaturityExample
latchgate:io/httpHTTP via reqwestv0.1 (supported)REST APIs, webhooks
latchgate:io/fsFilesystem via openat2v0.1 (supported)File read, write, delete
latchgate:io/logStructured loggingv0.1 (supported)Provider-side logging (always allowed)
latchgate:io/smtpSMTP via lettreplanned for future releasesEmail delivery
latchgate:io/databaseSQL via sqlxplanned for future releasesPostgreSQL queries
latchgate:io/queueAMQP via lapinplanned for future releasesMessage publishing
latchgate:io/storageObject storeplanned for future releasesS3, GCS, Azure Blob

In v0.1, io/http, io/fs, and io/log are linked at runtime. The other WIT interfaces are committed for forward compatibility and will activate when their host I/O backends ship in future releases.

A provider can only use the imports declared in its action manifest. Undeclared imports cause instantiation failure at the linker. Import gating is also enforced at runtime in check_import_allowed() as defense in depth — even if the linker had all interfaces available, a provider that did not declare an interface cannot call it.

After a provider executes, the kernel runs a verifier appropriate to the action. Verifier output is one of:

  • verified — the effect was independently confirmed
  • verification_failed — the effect could not be confirmed (for high-risk actions, this denies the response)
  • unverifiable_declared — no verifier was configured (the action explicitly opts out)
  • provider_failed_before_verification — the provider failed before reaching a verifiable state
  • skipped — verifier was skipped (e.g., provider returned non-success)

LatchGate never labels an action as verified if verification did not actually happen.

All decisions, results, and verification outcomes are recorded in an append-only, hash-chained SQLite ledger. Each event’s prev_hash is the SHA-256 of the preceding event’s JSON bytes, enabling tamper detection without a blockchain.

Receipts are Ed25519-signed with key rotation support via signing_key_id (kid). Historical verifying keys are persisted in a JWKS file so old receipts remain verifiable after rotation. The grant signing key is separate from the receipt signing key (defense-in-depth).

JSONL export is available for SIEM / WORM integration.

Northbound (above LatchGate): agent runtimes, IDE agents, MCP hosts, orchestrators. They integrate via the Action API (POST /v1/actions/{id}/execute), the MCP adapter, or thin SDKs.

Southbound (below LatchGate): WASM provider modules executing in sandboxed wasmtime instances, communicating through host-mediated I/O to external systems. This is where LatchGate’s core differentiation lives.

For the system layer diagram and crate layout, see Architecture. For the full threat model, see Security Model. For built-in actions and manifests, see Actions & Manifests. For the provider sandbox model and host I/O interfaces, see WASM Providers.