langchain-emilia
Before your LangChain agent does anything irreversible, a named human approves that exact action on their own device. Face ID / Touch ID / passkey. Fail-closed. Every approval mints a Trust Receipt that verifies offline — years later, with no account and no EMILIA server.
pip install langchain-emilia
export EP_API_KEY=ep_live_... EP_ORG_ID=your-org
from langchain_emilia import EmiliaGuard, guard_tools
guard = EmiliaGuard() # enforce mode
tools = guard_tools([transfer_funds, send_email, calculator], guard)
# hand `tools` to your agent exactly as before — nothing else changes
When the agent calls transfer_funds(amount=82000, beneficiary="Northwind"):
- Gate — the call is held pre-execution; EP policy returns
allow,require_signoff, ordeny. - Signoff — on
require_signoff, a named human approves on their own device. The approval is cryptographically bound to the exact action parameters — change one digit and it is invalid. - Consume — the exact receipt is atomically consumed before the tool body; an already-consumed receipt cannot authorize another executor.
- Attest — after the tool returns, the adapter reports executor-observed
invocation fields re-derived from the detached arguments actually handed to
the tool; it does not reuse the approved plan as its own observation. This is
attributable execution evidence, not proof of real-world effects. If the
evidence write cannot be confirmed, the result is
INDETERMINATEand the adapter says DO NOT RETRY.
Denials and pending holds are returned to the model as the tool's output
("EMILIA — BLOCKED … transfer_funds was NOT executed."), so the agent loop
explains itself instead of crashing. The tool body never runs unless the
gate allows it.
Why this is an executor-side precondition, not an "approval tool"
Approval tools the model calls have three failure modes: the model forgets to
call them, approves action A then executes action B, or barrels past an error.
langchain-emilia instead wraps the tool itself: the action digest is computed
from the actual arguments at execution time and the gate runs before the
tool body, unconditionally. The model-facing schema is unchanged; only the
executor gains the gate.
Zero-setup dry run (observe mode)
No account, no network, nothing blocked — see what enforcement would cover:
guard = EmiliaGuard(mode="observe")
tools = guard_tools(my_tools, guard)
# ... run your agent, then:
for r in guard.records:
print(r["tool"], r["digest"][:16], r["note"])
Configuration
| Option | Default | Meaning |
|---|---|---|
mode |
"enforce" |
"observe" = log-only local dry run, keyless |
match |
money/external-action regex | Callable[[str], bool] — which tool names are gated |
action_types |
auto | map tool name → EP action_type (see ACTION_TYPES) |
wait_for_approval |
True |
block (≤ timeout) while the human approves; False = surface the signoff URL immediately |
return_errors |
True |
denials become tool output for the model; False = raise EmiliaDenied / EmiliaApprovalPending |
on_event |
None |
callback for observed/allowed/executed/denied/pending/unreachable/indeterminate events (SIEM hook) |
EmiliaGateClient(api_key, org_id, base_url, signoff_timeout_s=280, poll_interval_s=3)
reads EP_API_KEY / EP_ORG_ID / EP_BASE_URL from the environment by default.
Fail-closed semantics
| Situation | What happens |
|---|---|
| Policy denies | Tool not executed; model told why |
| Human rejects on device | Tool not executed; receipt records the rejection |
| Signoff window times out | Tool not executed; signoff URL surfaced for retry |
| EMILIA unreachable / network error | Tool not executed — never fail open |
| Consume is not confirmed | Tool not executed — never fail open |
| Tool returns but execution attestation fails | Tool may have executed; INDETERMINATE, do not retry |
Tool name doesn't match match |
Runs ungated (scope your match deliberately) |
Verify the evidence
Every allowed action carries a receipt_id. Anyone can verify it with zero
trust in us or in you:
- In a browser (nothing uploads): https://www.emiliaprotocol.ai/verify
- Offline CLI:
npx @emilia-protocol/verify receipt.json - Spec: draft-schrock-ep-authorization-receipts (IETF)
Try the human side yourself (no signup): https://www.emiliaprotocol.ai/try
Development
cd integrations/langchain-emilia
python3 -m venv .venv && .venv/bin/pip install -e '.[dev]'
.venv/bin/pytest -q
Apache-2.0. The digest layer is pinned byte-for-byte to the JS verifier by
cross-language vectors in tests/test_digest.py.
Building with LangChain.js instead? The JS sibling is
@emilia-protocol/langchain — a thin gate Proxy
for .invoke()-style tools on npm.
Metadata
Release files for langchain-emilia 0.2.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| langchain_emilia-0.2.2.tar.gz | 17.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| langchain_emilia-0.2.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 30.7 kB
Release files / langchain_emilia-0.2.2.tar.gz
| Download URL | langchain_emilia-0.2.2.tar.gz |
|---|---|
| Size | 17.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3d900ce2d561775542fb1a47def620f9cf1b4da59519e0486d83b67da233975c
|
|
BLAKE2b-256 checksum How to use checksums |
11ea86cf3c7a2467de8a6cbf8eb357aac12e622e56e919b0a9029429c3f00086
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.
Transparency logRelease files / langchain_emilia-0.2.2-py3-none-any.whl
| Download URL | langchain_emilia-0.2.2-py3-none-any.whl |
|---|---|
| Size | 13.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
681bf1dd40179a7a3b40f92db95cd0440787d4a704fe1de9d97485422b9865e0
|
|
BLAKE2b-256 checksum How to use checksums |
ca259f30c4fd59e45d871b8254360fec4b2e0636581b25288aded512f2b93d83
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.
Transparency log