lockstock-integrations 1.0.1__py3-none-any.whl → 1.1.0__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lockstock-integrations
3
- Version: 1.0.1
3
+ Version: 1.1.0
4
4
  Summary: LockStock compliance runtime integrations for AI Agent SDKs
5
5
  Project-URL: Homepage, https://d3cipher.ai
6
6
  Project-URL: Documentation, https://d3cipher.ai/docs
@@ -9,8 +9,8 @@ lockstock_langgraph/__init__.py,sha256=7fBIYNCQygvUvG7IQOVY042sNNmu4UwkJoy0VLQn4
9
9
  lockstock_langgraph/checkpointer.py,sha256=XJ2c_92OSBq4aNpOT112nTy3yI3stFcSCb8OTx04nos,6317
10
10
  lockstock_langgraph/middleware.py,sha256=ZSwoxdkn3cC6aIBnMgd1jYGHYKa1yQXRwnts3Ds4sfQ,9174
11
11
  lockstock_openai/__init__.py,sha256=Pc4phRUJEqrnREggyGJnc1HDKa7puVAR6-G9WZ4dtXM,402
12
- lockstock_openai/guardrails.py,sha256=10wYzpR900-41A0eacC2dyUpgy-6yLSJRTOZydjk_c8,7119
12
+ lockstock_openai/guardrails.py,sha256=fy84bDrhy6CVk2b4_yOl5XTxw9Lp1HT4Mw9WcaAHOsg,11920
13
13
  lockstock_openai/tracing.py,sha256=VQWzG0y6t8q5rJZFbbP-bi9tsV0KaUqqH4Kr9-vr_e8,6025
14
- lockstock_integrations-1.0.1.dist-info/METADATA,sha256=AiOMxN-VM6v3z6LAq74MkrkppjbOQt_ceLhNdxekviE,5629
15
- lockstock_integrations-1.0.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
- lockstock_integrations-1.0.1.dist-info/RECORD,,
14
+ lockstock_integrations-1.1.0.dist-info/METADATA,sha256=2oQWjepWJDR6-Q_L5tOmiaDCg4gGm6K5-JcEIRYtRSE,5629
15
+ lockstock_integrations-1.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
+ lockstock_integrations-1.1.0.dist-info/RECORD,,
@@ -1,13 +1,19 @@
1
1
  """
2
2
  LockStock OpenAI Agents SDK Guardrails
3
3
  ---------------------------------------
4
- Guardrail implementations for OpenAI Agents SDK.
4
+ Chain-based guardrail implementations for OpenAI Agents SDK.
5
+
6
+ NO SECRETS. ONLY CHAIN STATE.
7
+
8
+ The Guard daemon tracks chain state (hash, matrix, sequence).
9
+ When signing, Guard uses current_hash as the HMAC key.
5
10
 
6
11
  Usage:
7
12
  from agents import Agent
8
13
  from lockstock_openai import LockStockGuardrail
9
14
 
10
- guardrail = LockStockGuardrail(agent_id="agent_abc123")
15
+ # Chain Authority Mode (recommended)
16
+ guardrail = LockStockGuardrail.from_liberty("agent_abc123")
11
17
 
12
18
  agent = Agent(
13
19
  name="my-agent",
@@ -16,11 +22,10 @@ Usage:
16
22
  )
17
23
  """
18
24
 
19
- from typing import Any, Dict, Optional, List
25
+ from typing import Any, Dict, Optional
20
26
  from dataclasses import dataclass
21
27
 
22
- from lockstock_core import LockStockClient
23
- from lockstock_core.types import map_tool_to_capability, VerifyStatus
28
+ from lockstock_core.types import map_tool_to_capability
24
29
 
25
30
 
26
31
  @dataclass
@@ -33,41 +38,56 @@ class GuardrailResult:
33
38
 
34
39
  class LockStockGuardrail:
35
40
  """
36
- Base guardrail for OpenAI Agents SDK.
41
+ Chain-based guardrail for OpenAI Agents SDK.
42
+
43
+ NO SECRETS STORED. Chain state is the authentication.
37
44
 
38
- Validates that the agent has permission to perform requested actions.
45
+ The Guard daemon:
46
+ 1. Tracks current chain state (hash, matrix, sequence)
47
+ 2. Uses current_hash as HMAC key for signatures
48
+ 3. Returns signatures (not secrets!)
39
49
  """
40
50
 
41
51
  name = "lockstock"
42
- description = "LockStock capability authorization guardrail"
52
+ description = "LockStock chain-based authorization guardrail"
43
53
 
44
54
  def __init__(
45
55
  self,
46
56
  agent_id: str,
47
- secret: Optional[str] = None,
48
- api_key: Optional[str] = None,
57
+ guard_socket: str = "/var/run/lockstock-guard/guard.sock",
49
58
  endpoint: str = "https://lockstock-api-i9kp.onrender.com",
50
59
  block_on_failure: bool = True
51
60
  ):
52
61
  """
53
- Initialize LockStock guardrail.
62
+ Initialize chain-based guardrail.
54
63
 
55
64
  Args:
56
65
  agent_id: The agent's unique identifier
57
- secret: The agent's HMAC secret
58
- api_key: Admin API key
66
+ guard_socket: Path to Guard daemon Unix socket
59
67
  endpoint: LockStock API endpoint
60
68
  block_on_failure: If True, block execution on auth failure
69
+
70
+ NO SECRET PARAMETER! Secrets don't exist in this system.
61
71
  """
62
- self.client = LockStockClient(
63
- agent_id=agent_id,
64
- secret=secret,
65
- api_key=api_key,
66
- endpoint=endpoint
67
- )
68
72
  self.agent_id = agent_id
73
+ self.guard_socket = guard_socket
74
+ self.endpoint = endpoint
69
75
  self.block_on_failure = block_on_failure
70
76
 
77
+ # Chain state (cached locally, synced from Guard)
78
+ self.current_hash: Optional[str] = None
79
+ self.current_sequence: int = 0
80
+ self.current_matrix: Optional[Dict[str, int]] = None
81
+ self._chain_initialized = False
82
+
83
+ # Guard client (for chain operations)
84
+ from lockstock_guard.client import LibertyClient
85
+ self._guard = LibertyClient(socket_path=guard_socket)
86
+
87
+ # HTTP client (for server communication)
88
+ import httpx
89
+ self._http = httpx.AsyncClient(timeout=30.0)
90
+
71
91
  @classmethod
72
92
  def from_liberty(
73
93
  cls,
@@ -77,52 +97,77 @@ class LockStockGuardrail:
77
97
  block_on_failure: bool = True
78
98
  ):
79
99
  """
80
- Create guardrail by reading secret from Liberty Guard daemon.
100
+ Create guardrail using Chain Authority Mode.
101
+
102
+ NO SECRET RETRIEVAL. NO SECRET STORAGE.
81
103
 
82
- This is the recommended method for enterprise deployments where secrets
83
- are hardware-bound and managed by the lockstock-guard daemon.
104
+ The Guard daemon manages chain state. We just track it locally.
84
105
 
85
106
  Args:
86
- agent_id: The agent's unique identifier
87
- socket_path: Path to Liberty Guard Unix socket
107
+ agent_id: Agent identifier (from provisioning)
108
+ socket_path: Path to Guard daemon Unix socket
88
109
  endpoint: LockStock API endpoint
89
- block_on_failure: If True, block execution on auth failure
110
+ block_on_failure: Whether to block on auth failure
90
111
 
91
112
  Returns:
92
- LockStockGuardrail instance with secret loaded from Guard daemon
113
+ LockStockGuardrail instance using chain-based auth
93
114
 
94
115
  Example:
95
116
  >>> guardrail = LockStockGuardrail.from_liberty("agent_abc123")
96
117
  >>> agent = Agent(guardrails=[guardrail])
97
118
  """
98
- import json
99
- from lockstock_guard.client import LibertyClient
100
-
101
- # Retrieve secret from Guard daemon
102
- guard_client = LibertyClient(socket_path=socket_path)
103
- try:
104
- secret_data = json.loads(guard_client.get(agent_id))
105
- secret = secret_data["value"]
106
- finally:
107
- guard_client.close()
108
-
109
- # Create guardrail with retrieved secret
110
119
  return cls(
111
120
  agent_id=agent_id,
112
- secret=secret,
121
+ guard_socket=socket_path,
113
122
  endpoint=endpoint,
114
123
  block_on_failure=block_on_failure
115
124
  )
116
125
 
126
+ async def _ensure_chain_initialized(self):
127
+ """
128
+ Sync chain state from Guard daemon on first use.
129
+
130
+ Guard fetches current state from server and caches it.
131
+ We sync that cached state to our local tracking.
132
+ """
133
+ if self._chain_initialized:
134
+ return
135
+
136
+ try:
137
+ # Ask Guard to sync from server
138
+ sync_result = self._guard.sync_chain(self.agent_id)
139
+
140
+ if not sync_result.get("success"):
141
+ raise Exception(f"Chain sync failed: {sync_result}")
142
+
143
+ # Update local chain state cache
144
+ self.current_hash = sync_result.get("current_hash")
145
+ self.current_sequence = sync_result.get("sequence", 0)
146
+ self.current_matrix = sync_result.get("state_matrix")
147
+ self._chain_initialized = True
148
+
149
+ except Exception as e:
150
+ raise Exception(
151
+ f"Failed to initialize chain state for {self.agent_id}. "
152
+ f"Ensure Guard daemon is running and agent is provisioned. "
153
+ f"Error: {e}"
154
+ )
155
+
117
156
  async def validate(
118
157
  self,
119
158
  agent: Any,
120
159
  message: Dict[str, Any]
121
160
  ) -> GuardrailResult:
122
161
  """
123
- Validate a message before it's processed or sent.
162
+ Validate a message using chain-based authentication.
163
+
164
+ Flow:
165
+ 1. Check if action requires authorization
166
+ 2. Ask Guard to sign (Guard uses current_hash as HMAC key)
167
+ 3. Send Guard's signature to server for verification
168
+ 4. Update local chain state if verified
124
169
 
125
- This method is called by the OpenAI Agents SDK.
170
+ NO SECRETS are involved. Only chain state.
126
171
 
127
172
  Args:
128
173
  agent: The agent instance
@@ -134,42 +179,146 @@ class LockStockGuardrail:
134
179
  # Check for tool calls in assistant messages
135
180
  if message.get("role") == "assistant" and "tool_calls" in message:
136
181
  for tool_call in message.get("tool_calls", []):
137
- if "function" in tool_call:
138
- function_name = tool_call["function"].get("name", "")
182
+ if "function" not in tool_call:
183
+ continue
184
+
185
+ function_name = tool_call["function"].get("name", "")
186
+
187
+ # Map tool to capability
188
+ capability = map_tool_to_capability(function_name)
189
+
190
+ # Ensure chain state is synced
191
+ await self._ensure_chain_initialized()
192
+
193
+ try:
194
+ # ========================================================
195
+ # CHAIN AUTHORITY MODE
196
+ # ========================================================
197
+ # Guard daemon:
198
+ # 1. Looks up current chain state for this agent
199
+ # 2. Calculates: new_matrix = current_matrix × generator[task]
200
+ # 3. Signs with: HMAC(current_hash, message)
201
+ # ↑ current_hash IS the key! No separate secret!
202
+ # 4. Calculates: new_hash = SHA256(...)
203
+ # 5. Returns: {signature, new_hash, new_matrix, ...}
204
+ # ========================================================
205
+
206
+ sign_result = self._guard.sign_and_advance(
207
+ agent_id=self.agent_id,
208
+ task=capability,
209
+ parent_hash=self.current_hash
210
+ )
139
211
 
140
- # Map to capability
141
- capability = map_tool_to_capability(function_name)
212
+ # Guard returned:
213
+ # - signature: HMAC computed using current_hash as key
214
+ # - new_hash: Next chain head
215
+ # - new_sequence: Incremented sequence
216
+ # - new_matrix: After applying generator
217
+ # - timestamp: Unix timestamp used in signature
218
+
219
+ except Exception as e:
220
+ return GuardrailResult(
221
+ passed=False,
222
+ reason=f"Guard signing failed: {e}"
223
+ )
142
224
 
143
- # Verify with LockStock
144
- result = await self.client.verify(
225
+ # Send Guard's signature to LockStock server for verification
226
+ try:
227
+ verify_result = await self._verify_with_server(
145
228
  task=capability,
146
- tool_name=function_name
229
+ signature=sign_result["signature"],
230
+ parent_hash=self.current_hash,
231
+ state_matrix=sign_result["new_matrix"],
232
+ timestamp=sign_result["timestamp"],
233
+ sequence=sign_result["new_sequence"]
147
234
  )
148
235
 
149
- if not result.authorized:
236
+ if not verify_result["authorized"]:
150
237
  if self.block_on_failure:
151
238
  return GuardrailResult(
152
239
  passed=False,
153
240
  reason=f"LockStock DENIED: {function_name} requires {capability}. "
154
- f"Agent {self.agent_id} lacks this capability."
241
+ f"Reason: {verify_result.get('reason', 'Unknown')}"
155
242
  )
156
243
 
244
+ # Update local chain state cache
245
+ self.current_hash = sign_result["new_hash"]
246
+ self.current_sequence = sign_result["new_sequence"]
247
+ self.current_matrix = sign_result["new_matrix"]
248
+
249
+ except Exception as e:
250
+ return GuardrailResult(
251
+ passed=False,
252
+ reason=f"Server verification failed: {e}"
253
+ )
254
+
157
255
  return GuardrailResult(passed=True)
158
256
 
257
+ async def _verify_with_server(
258
+ self,
259
+ task: str,
260
+ signature: str,
261
+ parent_hash: str,
262
+ state_matrix: Dict[str, int],
263
+ timestamp: int,
264
+ sequence: int
265
+ ) -> Dict[str, Any]:
266
+ """
267
+ Send Guard-signed request to LockStock server.
268
+
269
+ The signature was created by Guard using current_hash as HMAC key.
270
+ Server will verify by:
271
+ 1. Looking up its current_hash for this agent
272
+ 2. Recalculating HMAC using that hash
273
+ 3. Comparing to signature we send
274
+
275
+ Args:
276
+ task: Capability being verified
277
+ signature: HMAC signature from Guard (signed with current_hash)
278
+ parent_hash: Parent hash in chain
279
+ state_matrix: New state matrix
280
+ timestamp: Timestamp used in signature
281
+ sequence: Sequence number
282
+
283
+ Returns:
284
+ Dict with authorized (bool) and reason (str)
285
+ """
286
+ payload = {
287
+ "client_id": self.agent_id,
288
+ "task": task.lower(),
289
+ "parent_hash": parent_hash,
290
+ "state_matrix": state_matrix,
291
+ "signature": signature, # ← From Guard, signed with current_hash
292
+ "timestamp": timestamp,
293
+ "sequence": sequence
294
+ }
295
+
296
+ response = await self._http.post(
297
+ f"{self.endpoint}/verify",
298
+ json=payload
299
+ )
300
+
301
+ data = response.json()
302
+
303
+ return {
304
+ "authorized": data.get("status") == "accepted",
305
+ "reason": data.get("reason"),
306
+ "state_hash": data.get("state_hash")
307
+ }
308
+
159
309
  async def close(self):
160
- """Close the underlying client."""
161
- await self.client.close()
310
+ """Close connections."""
311
+ await self._http.aclose()
312
+ self._guard.close()
162
313
 
163
314
 
164
315
  class LockStockInputGuardrail(LockStockGuardrail):
165
316
  """
166
- Input guardrail for OpenAI Agents SDK.
167
-
168
- Validates incoming messages/requests before processing.
317
+ Input guardrail using chain-based authentication.
169
318
  """
170
319
 
171
320
  name = "lockstock_input"
172
- description = "LockStock input validation guardrail"
321
+ description = "LockStock chain-based input validation guardrail"
173
322
 
174
323
  async def validate_input(
175
324
  self,
@@ -177,35 +326,19 @@ class LockStockInputGuardrail(LockStockGuardrail):
177
326
  user_input: str,
178
327
  context: Optional[Dict[str, Any]] = None
179
328
  ) -> GuardrailResult:
180
- """
181
- Validate user input before processing.
182
-
183
- Args:
184
- agent: The agent instance
185
- user_input: The user's input string
186
- context: Optional context dictionary
187
-
188
- Returns:
189
- GuardrailResult indicating if validation passed
190
- """
191
- # Input validation can check for:
192
- # - Sensitive data patterns
193
- # - Rate limiting
194
- # - Input size limits
195
-
196
- # For now, pass through - capability checks happen at tool use
329
+ """Validate user input."""
330
+ # Input validation - pass through for now
331
+ # Capability checks happen at tool use
197
332
  return GuardrailResult(passed=True)
198
333
 
199
334
 
200
335
  class LockStockOutputGuardrail(LockStockGuardrail):
201
336
  """
202
- Output guardrail for OpenAI Agents SDK.
203
-
204
- Validates agent outputs before returning to user.
337
+ Output guardrail using chain-based authentication.
205
338
  """
206
339
 
207
340
  name = "lockstock_output"
208
- description = "LockStock output validation guardrail"
341
+ description = "LockStock chain-based output validation guardrail"
209
342
 
210
343
  async def validate_output(
211
344
  self,
@@ -213,27 +346,7 @@ class LockStockOutputGuardrail(LockStockGuardrail):
213
346
  output: str,
214
347
  context: Optional[Dict[str, Any]] = None
215
348
  ) -> GuardrailResult:
216
- """
217
- Validate agent output before returning.
218
-
219
- Args:
220
- agent: The agent instance
221
- output: The agent's output string
222
- context: Optional context dictionary
223
-
224
- Returns:
225
- GuardrailResult indicating if validation passed
226
- """
227
- # Output validation can check for:
228
- # - Sensitive data leakage
229
- # - Compliance requirements
230
- # - Output format requirements
231
-
232
- # Log the output to audit trail
233
- await self.client.log_audit(
234
- action="output",
235
- status="COMPLETED",
236
- metadata={"output_length": len(output)}
237
- )
238
-
349
+ """Validate agent output."""
350
+ # Output validation - pass through for now
351
+ # Could log to audit trail here
239
352
  return GuardrailResult(passed=True)