Framework guide · LangChain.js
LangChain under Aarmos in 60 seconds.
The CLI is not publicly distributed. Commands on this page are reference examples, not instructions you can run today.
Keep your existing LangChain agent exactly as-is. No aarmos import, no framework wrapper, no monkey-patch. A thin wrapper script routes outbound HTTP calls that honor the proxy settings through the local Aarmos proxy, where the policy gate decides and a signed receipt is written.
examples/langchain/ in the repo. Mirrored below.0 · Initialize the workspace (once)
aarmos init
✓ policy.aarmos.toml
✓ avar.config.json
✓ signing key: .aarmos/keys/signing.key1 · Your existing LangChain agent — unchanged
// src/agent.mjs — stock LangChain.js. No aarmos imports.
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";
const model = new ChatOpenAI({ model: "gpt-4o-mini", temperature: 0 });
const reply = await model.invoke([new HumanMessage("what is a receipt?")]);
console.log(reply.content);2 · Wrap the launch (one small script)
Two Node realities the wrapper handles: aarmos run does not inherit shell env into the child, and Node's built-in fetch ignores HTTPS_PROXY unless you install an undici ProxyAgent.
# run-agent.sh
#!/usr/bin/env bash
set -euo pipefail
: "${OPENAI_API_KEY:?set OPENAI_API_KEY}"
export OPENAI_API_KEY
exec node --require ./setup-proxy.cjs src/agent.mjs "$@"
# setup-proxy.cjs
const proxy = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxy) {
const { setGlobalDispatcher, ProxyAgent } = require("undici");
setGlobalDispatcher(new ProxyAgent(proxy));
}3 · Run it under Aarmos
aarmos run --policy ./policy.aarmos.toml --intent "answer a question" ./run-agent.sh
▸ proxy: reusing daemon at http://127.0.0.1:7681
▸ agent: ./run-agent.sh
▸ policy: ./policy.aarmos.toml (15daa3b482ab…)
✓ AVAR receipt: .aarmos/avar/receipt-…json4 · Export and verify the evidence
aarmos keys init # once: local evidence-signing key
aarmos incident export --last 24h --out ev.aarmos
aarmos verify ev.aarmos --trusted-signer sha256:… # your key's fingerprint
signer trust: established against your supplied trusted signer sha256:…
signature scope: export-time packaging only — not event-time signing or approvalThe receipt file written by aarmos run is hash-chained, not signed. The signature is applied when you export evidence; verification checks it against the signer you trust and refuses altered records.
To review calls before you export, run aarmos activity on the same machine — it lists recent calls from the local evidence log as authorized or denied, with the rule that decided each one. Proxied calls stay on your machine: they do not appear in the web app's audit page, which lists only runs made inside the web app. The exported file is the evidence you verify and share.
What actually happens
- The undici
ProxyAgentmakes Node'sfetchhonorHTTPS_PROXYand route the OpenAI call through127.0.0.1:7681(the default loopback port; use--portto change it). - The Aarmos proxy sees the outbound
CONNECT, matches the destination hostname against your policy's FQDN allow-list, and forwards the connection to the original destination. - A signed receipt lands in
.aarmos/avar/. Verification runs locally against your workspace key — no network required.
Honest limits
- Hostname-only proxy (ADR-0001). The proxy inspects only the outbound hostname — it does not open TLS sessions or read request/response payloads. Receipts prove which hosts the agent talked to, signed by your local key — not what was said on the wire.
- JSON receipts, proxy-only sessions. Today the JSON receipt shows
0 entriesbecause per-CONNECTtuples aren't folded into the chain yet. For per-call entries, use a protocol adapter (MCP or OpenAPI) instead of the raw proxy — no LangChain-specific adapter ships today. - Proxy-honoring clients only. Node
fetch/undici, axios with env, Pythonrequestswith env. Native SDKs that ignore proxy env need per-SDK wiring. - Local tool calls bypass the proxy. LangChain
Tools that go throughfetchare gated; in-process Node function tools are not — that's on the roadmap.