lockstock-integrations 1.0.0__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.0
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=nZVN3URkqPS-oB3XGM18BE5VXLo7HzUCMnN0Pen599o,5566
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.0.dist-info/METADATA,sha256=c5Qfegd46b5QNfzDTNkNY74xWSG2Fbl4FGQzraIPeqU,5629
15
- lockstock_integrations-1.0.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
- lockstock_integrations-1.0.0.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,50 +38,136 @@ 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
+
91
+ @classmethod
92
+ def from_liberty(
93
+ cls,
94
+ agent_id: str,
95
+ socket_path: str = "/var/run/lockstock-guard/guard.sock",
96
+ endpoint: str = "https://lockstock-api-i9kp.onrender.com",
97
+ block_on_failure: bool = True
98
+ ):
99
+ """
100
+ Create guardrail using Chain Authority Mode.
101
+
102
+ NO SECRET RETRIEVAL. NO SECRET STORAGE.
103
+
104
+ The Guard daemon manages chain state. We just track it locally.
105
+
106
+ Args:
107
+ agent_id: Agent identifier (from provisioning)
108
+ socket_path: Path to Guard daemon Unix socket
109
+ endpoint: LockStock API endpoint
110
+ block_on_failure: Whether to block on auth failure
111
+
112
+ Returns:
113
+ LockStockGuardrail instance using chain-based auth
114
+
115
+ Example:
116
+ >>> guardrail = LockStockGuardrail.from_liberty("agent_abc123")
117
+ >>> agent = Agent(guardrails=[guardrail])
118
+ """
119
+ return cls(
120
+ agent_id=agent_id,
121
+ guard_socket=socket_path,
122
+ endpoint=endpoint,
123
+ block_on_failure=block_on_failure
124
+ )
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
+
71
156
  async def validate(
72
157
  self,
73
158
  agent: Any,
74
159
  message: Dict[str, Any]
75
160
  ) -> GuardrailResult:
76
161
  """
77
- 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
78
169
 
79
- This method is called by the OpenAI Agents SDK.
170
+ NO SECRETS are involved. Only chain state.
80
171
 
81
172
  Args:
82
173
  agent: The agent instance
@@ -88,42 +179,146 @@ class LockStockGuardrail:
88
179
  # Check for tool calls in assistant messages
89
180
  if message.get("role") == "assistant" and "tool_calls" in message:
90
181
  for tool_call in message.get("tool_calls", []):
91
- if "function" in tool_call:
92
- 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
+ )
93
211
 
94
- # Map to capability
95
- 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
+ )
96
224
 
97
- # Verify with LockStock
98
- 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(
99
228
  task=capability,
100
- 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"]
101
234
  )
102
235
 
103
- if not result.authorized:
236
+ if not verify_result["authorized"]:
104
237
  if self.block_on_failure:
105
238
  return GuardrailResult(
106
239
  passed=False,
107
240
  reason=f"LockStock DENIED: {function_name} requires {capability}. "
108
- f"Agent {self.agent_id} lacks this capability."
241
+ f"Reason: {verify_result.get('reason', 'Unknown')}"
109
242
  )
110
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
+
111
255
  return GuardrailResult(passed=True)
112
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
+
113
309
  async def close(self):
114
- """Close the underlying client."""
115
- await self.client.close()
310
+ """Close connections."""
311
+ await self._http.aclose()
312
+ self._guard.close()
116
313
 
117
314
 
118
315
  class LockStockInputGuardrail(LockStockGuardrail):
119
316
  """
120
- Input guardrail for OpenAI Agents SDK.
121
-
122
- Validates incoming messages/requests before processing.
317
+ Input guardrail using chain-based authentication.
123
318
  """
124
319
 
125
320
  name = "lockstock_input"
126
- description = "LockStock input validation guardrail"
321
+ description = "LockStock chain-based input validation guardrail"
127
322
 
128
323
  async def validate_input(
129
324
  self,
@@ -131,35 +326,19 @@ class LockStockInputGuardrail(LockStockGuardrail):
131
326
  user_input: str,
132
327
  context: Optional[Dict[str, Any]] = None
133
328
  ) -> GuardrailResult:
134
- """
135
- Validate user input before processing.
136
-
137
- Args:
138
- agent: The agent instance
139
- user_input: The user's input string
140
- context: Optional context dictionary
141
-
142
- Returns:
143
- GuardrailResult indicating if validation passed
144
- """
145
- # Input validation can check for:
146
- # - Sensitive data patterns
147
- # - Rate limiting
148
- # - Input size limits
149
-
150
- # 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
151
332
  return GuardrailResult(passed=True)
152
333
 
153
334
 
154
335
  class LockStockOutputGuardrail(LockStockGuardrail):
155
336
  """
156
- Output guardrail for OpenAI Agents SDK.
157
-
158
- Validates agent outputs before returning to user.
337
+ Output guardrail using chain-based authentication.
159
338
  """
160
339
 
161
340
  name = "lockstock_output"
162
- description = "LockStock output validation guardrail"
341
+ description = "LockStock chain-based output validation guardrail"
163
342
 
164
343
  async def validate_output(
165
344
  self,
@@ -167,27 +346,7 @@ class LockStockOutputGuardrail(LockStockGuardrail):
167
346
  output: str,
168
347
  context: Optional[Dict[str, Any]] = None
169
348
  ) -> GuardrailResult:
170
- """
171
- Validate agent output before returning.
172
-
173
- Args:
174
- agent: The agent instance
175
- output: The agent's output string
176
- context: Optional context dictionary
177
-
178
- Returns:
179
- GuardrailResult indicating if validation passed
180
- """
181
- # Output validation can check for:
182
- # - Sensitive data leakage
183
- # - Compliance requirements
184
- # - Output format requirements
185
-
186
- # Log the output to audit trail
187
- await self.client.log_audit(
188
- action="output",
189
- status="COMPLETED",
190
- metadata={"output_length": len(output)}
191
- )
192
-
349
+ """Validate agent output."""
350
+ # Output validation - pass through for now
351
+ # Could log to audit trail here
193
352
  return GuardrailResult(passed=True)