langchain-nuggets 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- langchain_nuggets-0.1.0/.gitignore +17 -0
- langchain_nuggets-0.1.0/PKG-INFO +196 -0
- langchain_nuggets-0.1.0/README.md +172 -0
- langchain_nuggets-0.1.0/langchain_nuggets/__init__.py +77 -0
- langchain_nuggets-0.1.0/langchain_nuggets/client/__init__.py +3 -0
- langchain_nuggets-0.1.0/langchain_nuggets/client/nuggets_api_client.py +192 -0
- langchain_nuggets-0.1.0/langchain_nuggets/langgraph/__init__.py +25 -0
- langchain_nuggets-0.1.0/langchain_nuggets/langgraph/auth.py +155 -0
- langchain_nuggets-0.1.0/langchain_nuggets/langgraph/authorization.py +110 -0
- langchain_nuggets-0.1.0/langchain_nuggets/langgraph/token_verifier.py +207 -0
- langchain_nuggets-0.1.0/langchain_nuggets/langgraph/types.py +31 -0
- langchain_nuggets-0.1.0/langchain_nuggets/middleware/__init__.py +24 -0
- langchain_nuggets-0.1.0/langchain_nuggets/middleware/authority_middleware.py +266 -0
- langchain_nuggets-0.1.0/langchain_nuggets/middleware/proof.py +65 -0
- langchain_nuggets-0.1.0/langchain_nuggets/middleware/types.py +76 -0
- langchain_nuggets-0.1.0/langchain_nuggets/toolkit.py +80 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/__init__.py +3 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/auth/__init__.py +14 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/auth/check_auth_status.py +29 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/auth/initiate_oauth_flow.py +29 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/auth/request_credential_presentation.py +29 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/auth/verify_presentation.py +29 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/base.py +29 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/kya/__init__.py +10 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/kya/get_agent_trust_score.py +29 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/kya/register_agent_identity.py +40 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/kya/verify_agent_identity.py +29 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/kyc/__init__.py +12 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/kyc/check_kyc_status.py +29 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/kyc/initiate_kyc_verification.py +28 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/kyc/verify_age.py +29 -0
- langchain_nuggets-0.1.0/langchain_nuggets/tools/kyc/verify_credential.py +29 -0
- langchain_nuggets-0.1.0/langchain_nuggets/types.py +103 -0
- langchain_nuggets-0.1.0/pyproject.toml +54 -0
- langchain_nuggets-0.1.0/tests/__init__.py +1 -0
- langchain_nuggets-0.1.0/tests/conftest.py +14 -0
- langchain_nuggets-0.1.0/tests/langgraph/__init__.py +0 -0
- langchain_nuggets-0.1.0/tests/langgraph/test_auth.py +139 -0
- langchain_nuggets-0.1.0/tests/langgraph/test_authorization.py +84 -0
- langchain_nuggets-0.1.0/tests/langgraph/test_token_verifier.py +231 -0
- langchain_nuggets-0.1.0/tests/middleware/__init__.py +0 -0
- langchain_nuggets-0.1.0/tests/middleware/test_authority_middleware.py +452 -0
- langchain_nuggets-0.1.0/tests/middleware/test_proof.py +172 -0
- langchain_nuggets-0.1.0/tests/middleware/test_types.py +164 -0
- langchain_nuggets-0.1.0/tests/test_nuggets_api_client.py +114 -0
- langchain_nuggets-0.1.0/tests/test_toolkit.py +73 -0
- langchain_nuggets-0.1.0/tests/tools/__init__.py +1 -0
- langchain_nuggets-0.1.0/tests/tools/test_auth.py +154 -0
- langchain_nuggets-0.1.0/tests/tools/test_base.py +41 -0
- langchain_nuggets-0.1.0/tests/tools/test_kya.py +101 -0
- langchain_nuggets-0.1.0/tests/tools/test_kyc.py +131 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: langchain-nuggets
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Nuggets identity verification toolkit for LangChain Python
|
|
5
|
+
Project-URL: Repository, https://github.com/NuggetsLtd/langchain-nuggets
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: ai,identity,kyc,langchain,nuggets,tools,verification
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
Requires-Dist: httpx>=0.27.0
|
|
10
|
+
Requires-Dist: langchain-core>=0.3.0
|
|
11
|
+
Requires-Dist: pydantic>=2.0.0
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: langgraph-sdk>=0.1.0; extra == 'dev'
|
|
14
|
+
Requires-Dist: mypy>=1.13.0; extra == 'dev'
|
|
15
|
+
Requires-Dist: pyjwt[crypto]>=2.8.0; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
|
|
17
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
18
|
+
Requires-Dist: respx>=0.22.0; extra == 'dev'
|
|
19
|
+
Requires-Dist: ruff>=0.8.0; extra == 'dev'
|
|
20
|
+
Provides-Extra: langgraph
|
|
21
|
+
Requires-Dist: langgraph-sdk>=0.1.0; extra == 'langgraph'
|
|
22
|
+
Requires-Dist: pyjwt[crypto]>=2.8.0; extra == 'langgraph'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# langchain-nuggets
|
|
26
|
+
|
|
27
|
+
Nuggets identity verification toolkit for LangChain Python. Provides 11 ready-to-use tools for KYC, KYA, and credential verification that plug directly into any LangChain agent.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install langchain-nuggets
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from langchain_nuggets import NuggetsToolkit
|
|
39
|
+
|
|
40
|
+
# Config via env vars: NUGGETS_API_URL, NUGGETS_PARTNER_ID, NUGGETS_PARTNER_SECRET
|
|
41
|
+
toolkit = NuggetsToolkit()
|
|
42
|
+
tools = toolkit.get_tools()
|
|
43
|
+
|
|
44
|
+
# Or pass config directly
|
|
45
|
+
toolkit = NuggetsToolkit(
|
|
46
|
+
api_url="https://api.nuggets.life",
|
|
47
|
+
partner_id="your-partner-id",
|
|
48
|
+
partner_secret="your-secret",
|
|
49
|
+
)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Tools
|
|
53
|
+
|
|
54
|
+
### KYC (Know Your Customer)
|
|
55
|
+
|
|
56
|
+
| Tool | Description |
|
|
57
|
+
|------|-------------|
|
|
58
|
+
| `initiate_kyc_verification` | Start identity verification flow, returns deeplink/QR for Nuggets app |
|
|
59
|
+
| `check_kyc_status` | Poll verification session status (pending/completed/failed/expired) |
|
|
60
|
+
| `verify_age` | Selective disclosure age proof without revealing date of birth |
|
|
61
|
+
| `verify_credential` | Request specific credential (address, email, phone, nationality) |
|
|
62
|
+
|
|
63
|
+
### KYA (Know Your Agent)
|
|
64
|
+
|
|
65
|
+
| Tool | Description |
|
|
66
|
+
|------|-------------|
|
|
67
|
+
| `register_agent_identity` | Register AI agent identity with DID and provenance signals |
|
|
68
|
+
| `verify_agent_identity` | Verify another agent's identity and provenance |
|
|
69
|
+
| `get_agent_trust_score` | Get trust score (0-1) based on verified signals |
|
|
70
|
+
|
|
71
|
+
### Auth & Credentials
|
|
72
|
+
|
|
73
|
+
| Tool | Description |
|
|
74
|
+
|------|-------------|
|
|
75
|
+
| `request_credential_presentation` | Ask user to present verifiable credentials |
|
|
76
|
+
| `verify_presentation` | Check presentation status and verify credentials |
|
|
77
|
+
| `initiate_oauth_flow` | Start OAuth 2.0/OIDC flow with Nuggets as IdP |
|
|
78
|
+
| `check_auth_status` | Check user's authentication and verification status |
|
|
79
|
+
|
|
80
|
+
## Usage with LangChain Agent
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from langchain_openai import ChatOpenAI
|
|
84
|
+
from langchain.agents import AgentExecutor, create_openai_tools_agent
|
|
85
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
86
|
+
from langchain_nuggets import NuggetsToolkit
|
|
87
|
+
|
|
88
|
+
toolkit = NuggetsToolkit()
|
|
89
|
+
tools = toolkit.get_tools()
|
|
90
|
+
|
|
91
|
+
llm = ChatOpenAI(model="gpt-4o", temperature=0)
|
|
92
|
+
prompt = ChatPromptTemplate.from_messages([
|
|
93
|
+
("system", "You are a helpful assistant that can verify user identities using Nuggets."),
|
|
94
|
+
("human", "{input}"),
|
|
95
|
+
("placeholder", "{agent_scratchpad}"),
|
|
96
|
+
])
|
|
97
|
+
|
|
98
|
+
agent = create_openai_tools_agent(llm, tools, prompt)
|
|
99
|
+
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
|
|
100
|
+
|
|
101
|
+
result = executor.invoke({"input": "Verify alice@example.com"})
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Configuration
|
|
105
|
+
|
|
106
|
+
| Parameter | Env Variable | Description |
|
|
107
|
+
|-----------|-------------|-------------|
|
|
108
|
+
| `api_url` | `NUGGETS_API_URL` | Nuggets API base URL |
|
|
109
|
+
| `partner_id` | `NUGGETS_PARTNER_ID` | Your partner ID |
|
|
110
|
+
| `partner_secret` | `NUGGETS_PARTNER_SECRET` | Your partner secret |
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
## Self-Hosted Deployment
|
|
114
|
+
|
|
115
|
+
All classes accept configurable URLs — point to your own Nuggets instance:
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
toolkit = NuggetsToolkit(
|
|
119
|
+
api_url="https://nuggets.internal.example.com/api",
|
|
120
|
+
partner_id="your-partner-id",
|
|
121
|
+
partner_secret="your-secret",
|
|
122
|
+
)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Custom CA Certificates
|
|
126
|
+
|
|
127
|
+
For self-hosted instances using a private CA:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
toolkit = NuggetsToolkit(
|
|
131
|
+
api_url="https://nuggets.internal.example.com/api",
|
|
132
|
+
partner_id="your-partner-id",
|
|
133
|
+
partner_secret="your-secret",
|
|
134
|
+
ca_cert="/etc/ssl/private-ca/nuggets-ca.pem",
|
|
135
|
+
)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The `ca_cert` and `verify_ssl` parameters are also available on:
|
|
139
|
+
- `NuggetsAuth(issuer_url=..., ca_cert=..., verify_ssl=...)`
|
|
140
|
+
- `MiddlewareConfig(api_url=..., ca_cert=..., verify_ssl=...)`
|
|
141
|
+
- `NuggetsApiClient({"api_url": ..., "ca_cert": ..., "verify_ssl": ...})`
|
|
142
|
+
|
|
143
|
+
To disable TLS verification entirely (development/staging only):
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
toolkit = NuggetsToolkit(..., verify_ssl=False)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
MIT
|
|
152
|
+
|
|
153
|
+
## Authority Middleware
|
|
154
|
+
|
|
155
|
+
Intercept every LangChain/LangGraph tool call with pre-execution trust enforcement. The middleware validates authority against the Nuggets control plane, blocks unauthorized actions, and emits cryptographic proof artifacts.
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from langchain_nuggets.middleware import NuggetsAuthorityMiddleware, MiddlewareConfig
|
|
159
|
+
from langgraph.prebuilt import ToolNode
|
|
160
|
+
|
|
161
|
+
config = MiddlewareConfig(
|
|
162
|
+
api_url="https://api.nuggets.life",
|
|
163
|
+
partner_id="your-partner-id",
|
|
164
|
+
partner_secret="your-secret",
|
|
165
|
+
agent_id="agent-001",
|
|
166
|
+
controller_id="org-001",
|
|
167
|
+
delegation_id="del-001",
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
middleware = NuggetsAuthorityMiddleware(config)
|
|
171
|
+
|
|
172
|
+
tool_node = ToolNode(
|
|
173
|
+
tools=your_tools,
|
|
174
|
+
wrap_tool_call=middleware.wrap_tool_call, # sync
|
|
175
|
+
# awrap_tool_call=middleware.awrap_tool_call, # async
|
|
176
|
+
)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Execution model:** `Agent → Tool Call → Nuggets Authority Check → Allow/Deny → Emit Proof`
|
|
180
|
+
|
|
181
|
+
**Trust primitives enforced:** Actor Identity, Authority (delegation), Policy, Intent, Consent, Accountability (provenance).
|
|
182
|
+
|
|
183
|
+
| Behaviour | Detail |
|
|
184
|
+
|-----------|--------|
|
|
185
|
+
| **ALLOW** | Tool executes, proof artifact emitted |
|
|
186
|
+
| **DENY** | Tool blocked, structured error returned |
|
|
187
|
+
| **ERROR** | Fail closed — tool not executed |
|
|
188
|
+
|
|
189
|
+
Access proof artifacts after execution:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
for proof in middleware.proofs:
|
|
193
|
+
print(f"{proof.proof_id}: {proof.tool} latency={proof.latency_ms:.0f}ms")
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
See the [demo script](../../examples/python/authority_middleware_demo.py) for a complete working example.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# langchain-nuggets
|
|
2
|
+
|
|
3
|
+
Nuggets identity verification toolkit for LangChain Python. Provides 11 ready-to-use tools for KYC, KYA, and credential verification that plug directly into any LangChain agent.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install langchain-nuggets
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from langchain_nuggets import NuggetsToolkit
|
|
15
|
+
|
|
16
|
+
# Config via env vars: NUGGETS_API_URL, NUGGETS_PARTNER_ID, NUGGETS_PARTNER_SECRET
|
|
17
|
+
toolkit = NuggetsToolkit()
|
|
18
|
+
tools = toolkit.get_tools()
|
|
19
|
+
|
|
20
|
+
# Or pass config directly
|
|
21
|
+
toolkit = NuggetsToolkit(
|
|
22
|
+
api_url="https://api.nuggets.life",
|
|
23
|
+
partner_id="your-partner-id",
|
|
24
|
+
partner_secret="your-secret",
|
|
25
|
+
)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Tools
|
|
29
|
+
|
|
30
|
+
### KYC (Know Your Customer)
|
|
31
|
+
|
|
32
|
+
| Tool | Description |
|
|
33
|
+
|------|-------------|
|
|
34
|
+
| `initiate_kyc_verification` | Start identity verification flow, returns deeplink/QR for Nuggets app |
|
|
35
|
+
| `check_kyc_status` | Poll verification session status (pending/completed/failed/expired) |
|
|
36
|
+
| `verify_age` | Selective disclosure age proof without revealing date of birth |
|
|
37
|
+
| `verify_credential` | Request specific credential (address, email, phone, nationality) |
|
|
38
|
+
|
|
39
|
+
### KYA (Know Your Agent)
|
|
40
|
+
|
|
41
|
+
| Tool | Description |
|
|
42
|
+
|------|-------------|
|
|
43
|
+
| `register_agent_identity` | Register AI agent identity with DID and provenance signals |
|
|
44
|
+
| `verify_agent_identity` | Verify another agent's identity and provenance |
|
|
45
|
+
| `get_agent_trust_score` | Get trust score (0-1) based on verified signals |
|
|
46
|
+
|
|
47
|
+
### Auth & Credentials
|
|
48
|
+
|
|
49
|
+
| Tool | Description |
|
|
50
|
+
|------|-------------|
|
|
51
|
+
| `request_credential_presentation` | Ask user to present verifiable credentials |
|
|
52
|
+
| `verify_presentation` | Check presentation status and verify credentials |
|
|
53
|
+
| `initiate_oauth_flow` | Start OAuth 2.0/OIDC flow with Nuggets as IdP |
|
|
54
|
+
| `check_auth_status` | Check user's authentication and verification status |
|
|
55
|
+
|
|
56
|
+
## Usage with LangChain Agent
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from langchain_openai import ChatOpenAI
|
|
60
|
+
from langchain.agents import AgentExecutor, create_openai_tools_agent
|
|
61
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
62
|
+
from langchain_nuggets import NuggetsToolkit
|
|
63
|
+
|
|
64
|
+
toolkit = NuggetsToolkit()
|
|
65
|
+
tools = toolkit.get_tools()
|
|
66
|
+
|
|
67
|
+
llm = ChatOpenAI(model="gpt-4o", temperature=0)
|
|
68
|
+
prompt = ChatPromptTemplate.from_messages([
|
|
69
|
+
("system", "You are a helpful assistant that can verify user identities using Nuggets."),
|
|
70
|
+
("human", "{input}"),
|
|
71
|
+
("placeholder", "{agent_scratchpad}"),
|
|
72
|
+
])
|
|
73
|
+
|
|
74
|
+
agent = create_openai_tools_agent(llm, tools, prompt)
|
|
75
|
+
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
|
|
76
|
+
|
|
77
|
+
result = executor.invoke({"input": "Verify alice@example.com"})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Configuration
|
|
81
|
+
|
|
82
|
+
| Parameter | Env Variable | Description |
|
|
83
|
+
|-----------|-------------|-------------|
|
|
84
|
+
| `api_url` | `NUGGETS_API_URL` | Nuggets API base URL |
|
|
85
|
+
| `partner_id` | `NUGGETS_PARTNER_ID` | Your partner ID |
|
|
86
|
+
| `partner_secret` | `NUGGETS_PARTNER_SECRET` | Your partner secret |
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
## Self-Hosted Deployment
|
|
90
|
+
|
|
91
|
+
All classes accept configurable URLs — point to your own Nuggets instance:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
toolkit = NuggetsToolkit(
|
|
95
|
+
api_url="https://nuggets.internal.example.com/api",
|
|
96
|
+
partner_id="your-partner-id",
|
|
97
|
+
partner_secret="your-secret",
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Custom CA Certificates
|
|
102
|
+
|
|
103
|
+
For self-hosted instances using a private CA:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
toolkit = NuggetsToolkit(
|
|
107
|
+
api_url="https://nuggets.internal.example.com/api",
|
|
108
|
+
partner_id="your-partner-id",
|
|
109
|
+
partner_secret="your-secret",
|
|
110
|
+
ca_cert="/etc/ssl/private-ca/nuggets-ca.pem",
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The `ca_cert` and `verify_ssl` parameters are also available on:
|
|
115
|
+
- `NuggetsAuth(issuer_url=..., ca_cert=..., verify_ssl=...)`
|
|
116
|
+
- `MiddlewareConfig(api_url=..., ca_cert=..., verify_ssl=...)`
|
|
117
|
+
- `NuggetsApiClient({"api_url": ..., "ca_cert": ..., "verify_ssl": ...})`
|
|
118
|
+
|
|
119
|
+
To disable TLS verification entirely (development/staging only):
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
toolkit = NuggetsToolkit(..., verify_ssl=False)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
|
128
|
+
|
|
129
|
+
## Authority Middleware
|
|
130
|
+
|
|
131
|
+
Intercept every LangChain/LangGraph tool call with pre-execution trust enforcement. The middleware validates authority against the Nuggets control plane, blocks unauthorized actions, and emits cryptographic proof artifacts.
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from langchain_nuggets.middleware import NuggetsAuthorityMiddleware, MiddlewareConfig
|
|
135
|
+
from langgraph.prebuilt import ToolNode
|
|
136
|
+
|
|
137
|
+
config = MiddlewareConfig(
|
|
138
|
+
api_url="https://api.nuggets.life",
|
|
139
|
+
partner_id="your-partner-id",
|
|
140
|
+
partner_secret="your-secret",
|
|
141
|
+
agent_id="agent-001",
|
|
142
|
+
controller_id="org-001",
|
|
143
|
+
delegation_id="del-001",
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
middleware = NuggetsAuthorityMiddleware(config)
|
|
147
|
+
|
|
148
|
+
tool_node = ToolNode(
|
|
149
|
+
tools=your_tools,
|
|
150
|
+
wrap_tool_call=middleware.wrap_tool_call, # sync
|
|
151
|
+
# awrap_tool_call=middleware.awrap_tool_call, # async
|
|
152
|
+
)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Execution model:** `Agent → Tool Call → Nuggets Authority Check → Allow/Deny → Emit Proof`
|
|
156
|
+
|
|
157
|
+
**Trust primitives enforced:** Actor Identity, Authority (delegation), Policy, Intent, Consent, Accountability (provenance).
|
|
158
|
+
|
|
159
|
+
| Behaviour | Detail |
|
|
160
|
+
|-----------|--------|
|
|
161
|
+
| **ALLOW** | Tool executes, proof artifact emitted |
|
|
162
|
+
| **DENY** | Tool blocked, structured error returned |
|
|
163
|
+
| **ERROR** | Fail closed — tool not executed |
|
|
164
|
+
|
|
165
|
+
Access proof artifacts after execution:
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
for proof in middleware.proofs:
|
|
169
|
+
print(f"{proof.proof_id}: {proof.tool} latency={proof.latency_ms:.0f}ms")
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
See the [demo script](../../examples/python/authority_middleware_demo.py) for a complete working example.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Nuggets identity verification toolkit for LangChain."""
|
|
2
|
+
from langchain_nuggets.client.nuggets_api_client import (
|
|
3
|
+
NuggetsApiClient,
|
|
4
|
+
NuggetsApiClientError,
|
|
5
|
+
)
|
|
6
|
+
from langchain_nuggets.toolkit import NuggetsToolkit
|
|
7
|
+
|
|
8
|
+
# Auth tools
|
|
9
|
+
from langchain_nuggets.tools.auth import (
|
|
10
|
+
CheckAuthStatus,
|
|
11
|
+
InitiateOAuthFlow,
|
|
12
|
+
RequestCredentialPresentation,
|
|
13
|
+
VerifyPresentation,
|
|
14
|
+
)
|
|
15
|
+
from langchain_nuggets.tools.base import NuggetsBaseTool
|
|
16
|
+
|
|
17
|
+
# KYA tools
|
|
18
|
+
from langchain_nuggets.tools.kya import (
|
|
19
|
+
GetAgentTrustScore,
|
|
20
|
+
RegisterAgentIdentity,
|
|
21
|
+
VerifyAgentIdentity,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# KYC tools
|
|
25
|
+
from langchain_nuggets.tools.kyc import (
|
|
26
|
+
CheckKycStatus,
|
|
27
|
+
InitiateKycVerification,
|
|
28
|
+
VerifyAge,
|
|
29
|
+
VerifyCredential,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# LangGraph auth (optional — requires langgraph-sdk)
|
|
33
|
+
try:
|
|
34
|
+
from langchain_nuggets.langgraph import NuggetsAuth, NuggetsAuthError
|
|
35
|
+
except ImportError:
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
# Authority Middleware (optional — requires langchain-core)
|
|
39
|
+
try:
|
|
40
|
+
from langchain_nuggets.middleware import (
|
|
41
|
+
MiddlewareConfig,
|
|
42
|
+
NuggetsAuthorityMiddleware,
|
|
43
|
+
ProofArtifact,
|
|
44
|
+
)
|
|
45
|
+
except ImportError:
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
# Toolkit
|
|
50
|
+
"NuggetsToolkit",
|
|
51
|
+
# Client
|
|
52
|
+
"NuggetsApiClient",
|
|
53
|
+
"NuggetsApiClientError",
|
|
54
|
+
# Base
|
|
55
|
+
"NuggetsBaseTool",
|
|
56
|
+
# KYC
|
|
57
|
+
"InitiateKycVerification",
|
|
58
|
+
"CheckKycStatus",
|
|
59
|
+
"VerifyAge",
|
|
60
|
+
"VerifyCredential",
|
|
61
|
+
# KYA
|
|
62
|
+
"RegisterAgentIdentity",
|
|
63
|
+
"VerifyAgentIdentity",
|
|
64
|
+
"GetAgentTrustScore",
|
|
65
|
+
# Auth
|
|
66
|
+
"RequestCredentialPresentation",
|
|
67
|
+
"VerifyPresentation",
|
|
68
|
+
"InitiateOAuthFlow",
|
|
69
|
+
"CheckAuthStatus",
|
|
70
|
+
# LangGraph (optional)
|
|
71
|
+
"NuggetsAuth",
|
|
72
|
+
"NuggetsAuthError",
|
|
73
|
+
# Middleware (optional)
|
|
74
|
+
"NuggetsAuthorityMiddleware",
|
|
75
|
+
"MiddlewareConfig",
|
|
76
|
+
"ProofArtifact",
|
|
77
|
+
]
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""Nuggets API client with automatic authentication and token caching."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import time
|
|
6
|
+
from typing import Any, Dict, Optional, Union
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class NuggetsApiClientError(Exception):
|
|
12
|
+
"""Error from the Nuggets API."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, message: str, code: str, status_code: int) -> None:
|
|
15
|
+
super().__init__(message)
|
|
16
|
+
self.code = code
|
|
17
|
+
self.status_code = status_code
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class NuggetsApiClient:
|
|
21
|
+
"""HTTP client for the Nuggets API with automatic auth token management."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, config: Dict[str, Any]) -> None:
|
|
24
|
+
self._api_url: str = config["api_url"]
|
|
25
|
+
self._partner_id: str = config["partner_id"]
|
|
26
|
+
self._partner_secret: str = config["partner_secret"]
|
|
27
|
+
self._token: Optional[Dict[str, Any]] = None
|
|
28
|
+
self._sync_client: Optional[httpx.Client] = None
|
|
29
|
+
self._async_client: Optional[httpx.AsyncClient] = None
|
|
30
|
+
|
|
31
|
+
# TLS configuration for self-hosted deployments
|
|
32
|
+
verify_ssl: bool = config.get("verify_ssl", True)
|
|
33
|
+
ca_cert: Optional[str] = config.get("ca_cert")
|
|
34
|
+
if not verify_ssl:
|
|
35
|
+
self._verify: Union[bool, str] = False
|
|
36
|
+
elif ca_cert is not None:
|
|
37
|
+
self._verify = ca_cert
|
|
38
|
+
else:
|
|
39
|
+
self._verify = True
|
|
40
|
+
|
|
41
|
+
def _get_sync_client(self) -> httpx.Client:
|
|
42
|
+
if self._sync_client is None:
|
|
43
|
+
self._sync_client = httpx.Client(verify=self._verify)
|
|
44
|
+
return self._sync_client
|
|
45
|
+
|
|
46
|
+
def close(self) -> None:
|
|
47
|
+
"""Close the sync HTTP client and release resources."""
|
|
48
|
+
if self._sync_client is not None:
|
|
49
|
+
self._sync_client.close()
|
|
50
|
+
self._sync_client = None
|
|
51
|
+
|
|
52
|
+
async def aclose(self) -> None:
|
|
53
|
+
"""Close the async HTTP client and release resources."""
|
|
54
|
+
if self._async_client is not None:
|
|
55
|
+
await self._async_client.aclose()
|
|
56
|
+
self._async_client = None
|
|
57
|
+
|
|
58
|
+
def __enter__(self) -> "NuggetsApiClient":
|
|
59
|
+
return self
|
|
60
|
+
|
|
61
|
+
def __exit__(self, *args: Any) -> None:
|
|
62
|
+
self.close()
|
|
63
|
+
|
|
64
|
+
async def __aenter__(self) -> "NuggetsApiClient":
|
|
65
|
+
return self
|
|
66
|
+
|
|
67
|
+
async def __aexit__(self, *args: Any) -> None:
|
|
68
|
+
await self.aclose()
|
|
69
|
+
|
|
70
|
+
def _authenticate_sync(self) -> str:
|
|
71
|
+
if self._token and self._token["expires_at"] > time.time():
|
|
72
|
+
return self._token["access_token"]
|
|
73
|
+
client = self._get_sync_client()
|
|
74
|
+
response = client.post(
|
|
75
|
+
f"{self._api_url}/partner/auth",
|
|
76
|
+
json={"partnerId": self._partner_id, "partnerSecret": self._partner_secret},
|
|
77
|
+
)
|
|
78
|
+
if response.status_code >= 400:
|
|
79
|
+
raise NuggetsApiClientError(
|
|
80
|
+
"Authentication failed", "AUTH_FAILED", response.status_code
|
|
81
|
+
)
|
|
82
|
+
data = response.json()
|
|
83
|
+
self._token = {
|
|
84
|
+
"access_token": data["token"],
|
|
85
|
+
"expires_at": time.time() + data["expiresIn"],
|
|
86
|
+
}
|
|
87
|
+
return self._token["access_token"]
|
|
88
|
+
|
|
89
|
+
def _request_sync(self, method: str, path: str, body: Any = None) -> Any:
|
|
90
|
+
token = self._authenticate_sync()
|
|
91
|
+
client = self._get_sync_client()
|
|
92
|
+
kwargs: Dict[str, Any] = {
|
|
93
|
+
"method": method,
|
|
94
|
+
"url": f"{self._api_url}{path}",
|
|
95
|
+
"headers": {
|
|
96
|
+
"Content-Type": "application/json",
|
|
97
|
+
"Authorization": f"Bearer {token}",
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
if body is not None:
|
|
101
|
+
kwargs["content"] = json.dumps(body)
|
|
102
|
+
response = client.request(**kwargs)
|
|
103
|
+
try:
|
|
104
|
+
data = response.json()
|
|
105
|
+
except Exception:
|
|
106
|
+
if response.status_code >= 400:
|
|
107
|
+
raise NuggetsApiClientError(
|
|
108
|
+
f"Request failed with status {response.status_code}",
|
|
109
|
+
"UNKNOWN",
|
|
110
|
+
response.status_code,
|
|
111
|
+
)
|
|
112
|
+
raise NuggetsApiClientError(
|
|
113
|
+
"Invalid JSON response", "PARSE_ERROR", response.status_code
|
|
114
|
+
)
|
|
115
|
+
if response.status_code >= 400:
|
|
116
|
+
raise NuggetsApiClientError(
|
|
117
|
+
data.get("message", "Request failed"),
|
|
118
|
+
data.get("code", "UNKNOWN"),
|
|
119
|
+
response.status_code,
|
|
120
|
+
)
|
|
121
|
+
return data
|
|
122
|
+
|
|
123
|
+
def get(self, path: str) -> Any:
|
|
124
|
+
return self._request_sync("GET", path)
|
|
125
|
+
|
|
126
|
+
def post(self, path: str, body: Any = None) -> Any:
|
|
127
|
+
return self._request_sync("POST", path, body)
|
|
128
|
+
|
|
129
|
+
# --- Async methods ---
|
|
130
|
+
async def _get_async_client(self) -> httpx.AsyncClient:
|
|
131
|
+
if self._async_client is None:
|
|
132
|
+
self._async_client = httpx.AsyncClient(verify=self._verify)
|
|
133
|
+
return self._async_client
|
|
134
|
+
|
|
135
|
+
async def _authenticate_async(self) -> str:
|
|
136
|
+
if self._token and self._token["expires_at"] > time.time():
|
|
137
|
+
return self._token["access_token"]
|
|
138
|
+
client = await self._get_async_client()
|
|
139
|
+
response = await client.post(
|
|
140
|
+
f"{self._api_url}/partner/auth",
|
|
141
|
+
json={"partnerId": self._partner_id, "partnerSecret": self._partner_secret},
|
|
142
|
+
)
|
|
143
|
+
if response.status_code >= 400:
|
|
144
|
+
raise NuggetsApiClientError(
|
|
145
|
+
"Authentication failed", "AUTH_FAILED", response.status_code
|
|
146
|
+
)
|
|
147
|
+
data = response.json()
|
|
148
|
+
self._token = {
|
|
149
|
+
"access_token": data["token"],
|
|
150
|
+
"expires_at": time.time() + data["expiresIn"],
|
|
151
|
+
}
|
|
152
|
+
return self._token["access_token"]
|
|
153
|
+
|
|
154
|
+
async def _request_async(self, method: str, path: str, body: Any = None) -> Any:
|
|
155
|
+
token = await self._authenticate_async()
|
|
156
|
+
client = await self._get_async_client()
|
|
157
|
+
kwargs: Dict[str, Any] = {
|
|
158
|
+
"method": method,
|
|
159
|
+
"url": f"{self._api_url}{path}",
|
|
160
|
+
"headers": {
|
|
161
|
+
"Content-Type": "application/json",
|
|
162
|
+
"Authorization": f"Bearer {token}",
|
|
163
|
+
},
|
|
164
|
+
}
|
|
165
|
+
if body is not None:
|
|
166
|
+
kwargs["content"] = json.dumps(body)
|
|
167
|
+
response = await client.request(**kwargs)
|
|
168
|
+
try:
|
|
169
|
+
data = response.json()
|
|
170
|
+
except Exception:
|
|
171
|
+
if response.status_code >= 400:
|
|
172
|
+
raise NuggetsApiClientError(
|
|
173
|
+
f"Request failed with status {response.status_code}",
|
|
174
|
+
"UNKNOWN",
|
|
175
|
+
response.status_code,
|
|
176
|
+
)
|
|
177
|
+
raise NuggetsApiClientError(
|
|
178
|
+
"Invalid JSON response", "PARSE_ERROR", response.status_code
|
|
179
|
+
)
|
|
180
|
+
if response.status_code >= 400:
|
|
181
|
+
raise NuggetsApiClientError(
|
|
182
|
+
data.get("message", "Request failed"),
|
|
183
|
+
data.get("code", "UNKNOWN"),
|
|
184
|
+
response.status_code,
|
|
185
|
+
)
|
|
186
|
+
return data
|
|
187
|
+
|
|
188
|
+
async def aget(self, path: str) -> Any:
|
|
189
|
+
return await self._request_async("GET", path)
|
|
190
|
+
|
|
191
|
+
async def apost(self, path: str, body: Any = None) -> Any:
|
|
192
|
+
return await self._request_async("POST", path, body)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""LangGraph auth integration with Nuggets identity provider.
|
|
2
|
+
|
|
3
|
+
Requires the ``langgraph`` extra::
|
|
4
|
+
|
|
5
|
+
pip install langchain-nuggets[langgraph]
|
|
6
|
+
"""
|
|
7
|
+
from langchain_nuggets.langgraph.auth import NuggetsAuth
|
|
8
|
+
from langchain_nuggets.langgraph.authorization import (
|
|
9
|
+
ownership_filter,
|
|
10
|
+
require_kyc,
|
|
11
|
+
require_scopes,
|
|
12
|
+
)
|
|
13
|
+
from langchain_nuggets.langgraph.token_verifier import NuggetsAuthError, NuggetsTokenVerifier
|
|
14
|
+
from langchain_nuggets.langgraph.types import NuggetsAuthConfig, NuggetsUserInfo
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"NuggetsAuth",
|
|
18
|
+
"NuggetsAuthConfig",
|
|
19
|
+
"NuggetsAuthError",
|
|
20
|
+
"NuggetsTokenVerifier",
|
|
21
|
+
"NuggetsUserInfo",
|
|
22
|
+
"ownership_filter",
|
|
23
|
+
"require_kyc",
|
|
24
|
+
"require_scopes",
|
|
25
|
+
]
|