langchain-nuggets
Authority middleware for LangChain / LangGraph — pre-execution trust enforcement on every tool call.
Wrap any ToolNode and the middleware calls the Nuggets authority endpoint before each tool executes. The backend evaluates a scoped delegation, returns an ALLOW or DENY decision, and signs an audit proof. Tools that aren't allowed never run.
Why Nuggets Authority?
Most agent middleware shapes prompts or guardrails outputs. Nuggets Authority governs actions — "is this agent allowed to do this, right now, on whose authority?" — before a tool runs, and leaves cryptographic proof.
- Pre-execution enforcement, not after-the-fact logging — unauthorized calls fail closed and never run.
- Cryptographic accountability — every decision is a signed, independently verifiable proof artifact; verification on by default.
- Scoped, revocable authority — delegations bound by capability, target, invocation cap, and expiry.
- Intent binding — optional
intent_resolversupport adds anintent_hashto proofs, so reviewers can distinguish the same action taken for different business intents. - Trusted agent identity — each request signed (RS256) and bound to the agent's DID, ownership verified server-side.
- Drop-in for both
ToolNodeandcreate_agent, with no changes to your tools.
Built on Nuggets, the universal trust infrastructure for autonomous AI. Nuggets governs at the point of execution.
Installation
pip install langchain-nuggets
For LangGraph Platform OIDC auth:
pip install langchain-nuggets[langgraph]
Authority Middleware
from langchain_nuggets.middleware import NuggetsAuthorityMiddleware, MiddlewareConfig
from langgraph.prebuilt import ToolNode
config = MiddlewareConfig(
api_url="https://accounts.nuggets.life",
oidc_issuer_url="https://auth.nuggets.life",
agent_id="did:web:auth.nuggets.life:your-agent-id",
controller_id="did:web:auth.nuggets.life:your-controller-id",
delegation_id="42",
agent_private_key="/secrets/agent-jwks.json",
)
middleware = NuggetsAuthorityMiddleware(config)
tool_node = ToolNode(
tools=your_tools,
wrap_tool_call=middleware.wrap_tool_call,
)
Execution model: Agent → Tool Call → Nuggets Authority Check → Allow/Deny → Emit Proof
Trust primitives enforced: Actor Identity, Authority (delegation), Policy, Intent, Consent, Accountability (provenance).
| Behaviour | Detail |
|---|---|
| ALLOW | Tool executes; cryptographic proof artifact emitted |
| DENY | Tool blocked; structured error returned with reason_code |
| ESCALATE | Human approval required; verified PENDING_APPROVAL returned, tool not executed, no proof artifact (see Payments & approvals) |
| ERROR | Fail closed — tool not executed |
| Proof binding | Proofs bind actor, controller, delegation, tool, parameters, result hash, constraints, and optional intent hash |
To provision the agent identity, private key, and delegation referenced above, see the agent provisioning runbook.
Proof verification (on by default)
Every ALLOW carries a proof signed by the authority, and the SDK verifies it before the tool runs — discovering the authority's signing identity from {api_url}/.well-known/authority-configuration, pinning the proof's issuer to that authority, verifying the signature against the published JWKS, and requiring its versioned RFC 8785 action_context_hash to match the SDK's independent hash of the exact requested action. The proof must also be addressed to the agent and carry a valid iat/exp/jti. Any failure fails closed: DENY with reason_code = PROOF_VERIFICATION_FAILED, tool not run. On by default — no config.
Every decision is therefore independently verifiable. A third party can validate an emitted proof out-of-band:
from langchain_nuggets.middleware import verify_authority_proof, discover_authority
issuer, jwks_uri = discover_authority("https://accounts.nuggets.life")
verify_authority_proof(
proof_jws,
expected={
"decision": "ALLOW",
"agent_id": expected_agent_aid,
"controller_id": expected_controller_aid,
"aud": expected_agent_aid,
"action_context_version": 1,
"action_context_hash": independently_computed_action_hash,
},
issuer=issuer,
jwks_uri=jwks_uri,
)
For a v1 proof, action_context_version, aud, and action_context_hash are mandatory in
expected; omitting any of them fails closed. Proofs declaring an unsupported version are rejected.
Opt out only deliberately (e.g. an offline harness verifying proofs separately): MiddlewareConfig(..., verify_proofs=False).
Intent binding
Set intent_resolver when the agent can identify why a tool call is being made. The SDK computes a stable, domain-separated RFC 8785 hash of the intent, sends the intent_hash to the authority, and includes it in the exact-action binding and emitted proof artifact. Transport nonce and timestamp are deliberately excluded so an approved action can be re-polled without changing its identity.
MiddlewareConfig(
...,
intent_resolver=lambda tool, args: "KYC lookup for compliance review",
)
Payments & approvals
For monetary tools, supply an action-context resolver to attach the payment amount_minor (minor units, integer) and currency (ISO-4217, uppercase) to the signed action. The resolver is the only source of money fields — they are never inferred from tool args — and the tool name must exactly match the delegation capability (e.g. nuggets.payments.send).
MiddlewareConfig(
...,
action_context_resolver=lambda tool, args: {
"amount_minor": 500, # £5.00
"currency": "GBP",
"target": "did:web:merchant", # optional; overrides the args-derived target
},
)
amount_minor and currency are validated as a pair — supply both or neither. Invalid money fields (negative/non-integer amount, non-^[A-Z]{3}$ currency, one without the other) fail closed with an ERROR ToolMessage before the tool runs.
ESCALATE (human approval). When the authority requires approval it returns ESCALATE. The middleware verifies the signed decision — exactly as it does for ALLOW — then returns a PENDING_APPROVAL ToolMessage. This is not an error, and the wrapped tool never runs:
{ "status": "PENDING_APPROVAL", "approval_id": 500, "reason_code": "APPROVAL_REQUIRED", "proof_id": "...", "signature": "..." }
Operational boundary:
- No payment handler runs on
PENDING_APPROVAL— nothing is executed or charged. - The application owns polling/redeem of the approval, out-of-band, using
approval_id. approval_idis a server-issued handle, not part of the signed receipt — treat it as an opaque identifier, not a cryptographically verified field. (The ESCALATE decision signature is verified.)
With create_agent
For the LangChain create_agent API, install the agent extra and use the AgentMiddleware adapter (same config, same enforcement):
pip install langchain-nuggets[agent]
from langchain.agents import create_agent
from langchain_nuggets.middleware import NuggetsAuthorityAgentMiddleware, MiddlewareConfig
agent = create_agent(
model="...",
tools=your_tools,
middleware=[NuggetsAuthorityAgentMiddleware(MiddlewareConfig(...))],
)
Agent private key
The accounts portal generates an RS256 keypair at agent creation and lets you download the private key as a JWKS file. MiddlewareConfig.agent_private_key accepts:
- A filesystem path to a PEM, JWK JSON, or JWKS JSON file
- A raw PEM string
- A JWK or JWKS dict
The key is never transmitted; only the signed agent_proof JWS is sent.
Keep the private JWKS in a secret store or mounted secret — never in source control, logs, or Downloads; treat any previously downloaded key as stale. For demos and smoke runs, use a disposable, scoped delegation and a freshly downloaded key, and revoke both afterwards.
Test mode
test_mode=True short-circuits the live auth flow during local development — no HTTP is made and each check returns a synthetic ALLOW with a proof artifact flagged as test-mode-unverifiable. Action-context resolution still runs first: a configured action_context_resolver that returns invalid money fields (or raises) fails closed with an ERROR ToolMessage before the short-circuit, so validation behaves identically in and out of test mode.
LangGraph Platform OIDC auth
from langchain_nuggets.langgraph import NuggetsAuth
nuggets = NuggetsAuth(
issuer_url="https://auth.nuggets.life",
audience="https://accounts.nuggets.life/agent", # this env's agent resource
)
auth = nuggets.auth # pass to langgraph.json
An audience is required. JWT verification fails closed when no audience is configured (RFC 9068), so set it to your environment's Nuggets agent resource URI:
| env | audience |
|---|---|
| dev | https://accounts-dev.internal-nuggets.life/agent |
| integration | https://accounts-integration.internal-nuggets.life/agent |
| staging | https://accounts-staging.internal-nuggets.life/agent |
| production | https://accounts.nuggets.life/agent |
Tokens are obtained via client_credentials + resource=<audience> + scope=agent.invoke. The verifier enforces: RS256 signature against the issuer JWKS (algorithm pinned — never the header's alg), exact aud match, iss, exp (15s skew tolerance), and typ == at+jwt. Scope enforcement (agent.invoke) is the application's job via require_scopes. Opaque (non-JWT) tokens are rejected — this path is JWT-only.
For a deliberate, insecure migration escape hatch you may pass allow_any_audience=True, which skips only the aud match (signature/iss/exp/typ/RS256 still apply). It is not a production setting and logs a warning.
Pre-built authorization helpers:
from langchain_nuggets.langgraph import require_scopes, ownership_filter
# Owner-scope every operation. ownership_filter stamps value["metadata"]["owner"]
# on writes and returns an {"owner": identity} filter for reads/searches; it
# fails closed (403) when there is no authenticated identity.
owned = ownership_filter()
for op in (
auth.on.threads.create, auth.on.threads.read, auth.on.threads.update,
auth.on.threads.delete, auth.on.threads.search,
):
op(owned)
Register it for every operation you want scoped — a create handler alone stamps the owner but leaves reads/updates/deletes unfiltered.
Self-hosted / private CA
Point the URLs at your own deployment and pass ca_cert to either constructor:
MiddlewareConfig(
api_url="https://nuggets.internal.example.com",
oidc_issuer_url="https://oidc.internal.example.com",
# ...
ca_cert="/etc/ssl/private-ca/nuggets-ca.pem",
)
Set verify_ssl=False to disable TLS verification (development only).
About Nuggets
Nuggets is the universal trust infrastructure for autonomous AI. Nuggets governs at the point of execution. Learn more at nuggets.life.
License
MIT
Trademarks
langchain-nuggets is an independent, community-maintained integration and is not affiliated with, sponsored by, or endorsed by LangChain, Inc. "LangChain" and "LangGraph" are trademarks of LangChain, Inc. All other trademarks are the property of their respective owners.
Metadata
Release files for langchain-nuggets 1.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| langchain_nuggets-1.4.0.tar.gz | 47.3 kB | With provenance | Details for langchain_nuggets-1.4.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| langchain_nuggets-1.4.0-py3-none-any.whl | Python 3 | none | any | Details for langchain_nuggets-1.4.0-py3-none-any.whl |
Total release size: 83.5 kB
Release files / langchain_nuggets-1.4.0.tar.gz
| Download URL | langchain_nuggets-1.4.0.tar.gz |
|---|---|
| Size | 47.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8a61da1dfc1437f0dbc3aa35e4aed6d0670dfd35b184a801f75156796d456113
|
|
BLAKE2b-256 checksum How to use checksums |
234fea5a3860f8407760f0a84f81405979a35733986900cd4395d52433d36b25
|
| 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 16, 2026.
Transparency logRelease files / langchain_nuggets-1.4.0-py3-none-any.whl
| Download URL | langchain_nuggets-1.4.0-py3-none-any.whl |
|---|---|
| Size | 36.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7c39bee8435fabbbec8811095f9742d2c9d6c98891eefd3908b1bd004f03f12f
|
|
BLAKE2b-256 checksum How to use checksums |
645c986b9f0e5824afd5a2968241bc3eb5c6ba7a8fadeff97633afcb4513966e
|
| 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 16, 2026.
Transparency logSecurity for langchain-nuggets 1.4.0
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for langchain_nuggets-1.4.0.tar.gz
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 16, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.