swiftapi-python 1.2.2__py3-none-any.whl → 1.3.1__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.
- swiftapi/__init__.py +75 -96
- swiftapi/__main__.py +2 -0
- swiftapi/anthropic.py +236 -222
- swiftapi/brake/__init__.py +45 -0
- swiftapi/brake/benchmarks.py +111 -0
- swiftapi/brake/broker.py +274 -0
- swiftapi/brake/core.py +420 -0
- swiftapi/brake/install.py +268 -0
- swiftapi/brake/mcp.py +160 -0
- swiftapi/brake/observations.py +61 -0
- swiftapi/brake/qualification.py +116 -0
- swiftapi/brake/remote.py +90 -0
- swiftapi/cli.py +150 -0
- swiftapi/client.py +328 -309
- swiftapi/enforcement.py +281 -280
- swiftapi/openai.py +243 -225
- swiftapi/py.typed +0 -0
- swiftapi/verifier.py +182 -130
- swiftapi_python-1.3.1.dist-info/METADATA +144 -0
- swiftapi_python-1.3.1.dist-info/RECORD +26 -0
- {swiftapi_python-1.2.2.dist-info → swiftapi_python-1.3.1.dist-info}/WHEEL +1 -1
- swiftapi_python-1.3.1.dist-info/entry_points.txt +2 -0
- swiftapi_python-1.2.2.dist-info/METADATA +0 -255
- swiftapi_python-1.2.2.dist-info/RECORD +0 -13
- {swiftapi_python-1.2.2.dist-info → swiftapi_python-1.3.1.dist-info}/licenses/LICENSE +0 -0
- {swiftapi_python-1.2.2.dist-info → swiftapi_python-1.3.1.dist-info}/top_level.txt +0 -0
swiftapi/enforcement.py
CHANGED
|
@@ -1,280 +1,281 @@
|
|
|
1
|
-
"""
|
|
2
|
-
SwiftAPI SDK - Enforcement Point (The Golden Loop)
|
|
3
|
-
|
|
4
|
-
This module provides the high-level enforcement mechanism that ensures
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
The Golden Loop:
|
|
8
|
-
1. API Call: client.verify() -> Get Attestation
|
|
9
|
-
2. Crypto Check: verifier.verify_signature() -> OFFLINE TRUTH
|
|
10
|
-
3. Online Check: client.check_revocation() -> ONLINE TRUTH (optional)
|
|
11
|
-
4. Execute: Run the protected function
|
|
12
|
-
|
|
13
|
-
If any step fails, the action is BLOCKED.
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
from typing import Callable, Any, Dict, Optional
|
|
17
|
-
from functools import wraps
|
|
18
|
-
|
|
19
|
-
from .client import SwiftAPI
|
|
20
|
-
from .verifier import
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
)
|
|
28
|
-
from .
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
# Option
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"""
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
The
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
1
|
+
"""
|
|
2
|
+
SwiftAPI SDK - Enforcement Point (The Golden Loop)
|
|
3
|
+
|
|
4
|
+
This module provides the high-level enforcement mechanism that ensures
|
|
5
|
+
callbacks mediated by this class require verification and attestation.
|
|
6
|
+
|
|
7
|
+
The Golden Loop:
|
|
8
|
+
1. API Call: client.verify() -> Get Attestation
|
|
9
|
+
2. Crypto Check: verifier.verify_signature() -> OFFLINE TRUTH
|
|
10
|
+
3. Online Check: client.check_revocation() -> ONLINE TRUTH (optional)
|
|
11
|
+
4. Execute: Run the protected function
|
|
12
|
+
|
|
13
|
+
If any step fails, the action is BLOCKED.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from typing import Callable, Any, Dict, Optional
|
|
17
|
+
from functools import wraps
|
|
18
|
+
|
|
19
|
+
from .client import SwiftAPI
|
|
20
|
+
from .verifier import (
|
|
21
|
+
verify_signature,
|
|
22
|
+
claim_attestation,
|
|
23
|
+
is_valid as is_valid,
|
|
24
|
+
_action_fingerprint,
|
|
25
|
+
_snapshot_action_data,
|
|
26
|
+
_validate_action_binding,
|
|
27
|
+
)
|
|
28
|
+
from .exceptions import (
|
|
29
|
+
PolicyViolation,
|
|
30
|
+
SignatureVerificationError,
|
|
31
|
+
AttestationRevokedError,
|
|
32
|
+
AttestationExpiredError,
|
|
33
|
+
SwiftAPIError,
|
|
34
|
+
)
|
|
35
|
+
from .utils import print_approved, print_denied, print_verified, print_error
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Enforcement:
|
|
39
|
+
"""
|
|
40
|
+
SwiftAPI Enforcement Point.
|
|
41
|
+
|
|
42
|
+
This is the "ignition key" - no action executes without verification.
|
|
43
|
+
|
|
44
|
+
Usage:
|
|
45
|
+
api = SwiftAPI(key="swiftapi_live_...")
|
|
46
|
+
guard = Enforcement(api)
|
|
47
|
+
|
|
48
|
+
# Option 1: Run with enforcement
|
|
49
|
+
guard.run(
|
|
50
|
+
lambda: dangerous_operation(),
|
|
51
|
+
action="file_delete",
|
|
52
|
+
intent="Remove temp files"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Option 2: Decorator
|
|
56
|
+
@guard.protect(action="api_call", intent="Send notification")
|
|
57
|
+
def send_notification(user_id, message):
|
|
58
|
+
...
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
client: SwiftAPI,
|
|
64
|
+
paranoid: bool = False,
|
|
65
|
+
verbose: bool = True,
|
|
66
|
+
):
|
|
67
|
+
"""
|
|
68
|
+
Initialize enforcement point.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
client: SwiftAPI client instance
|
|
72
|
+
paranoid: If True, always check revocation online (slower but safer)
|
|
73
|
+
verbose: If True, print status messages
|
|
74
|
+
"""
|
|
75
|
+
self.client = client
|
|
76
|
+
self.paranoid = paranoid
|
|
77
|
+
self.verbose = verbose
|
|
78
|
+
|
|
79
|
+
def run(
|
|
80
|
+
self,
|
|
81
|
+
func: Callable[[], Any],
|
|
82
|
+
action: str,
|
|
83
|
+
intent: str,
|
|
84
|
+
params: Optional[Dict[str, Any]] = None,
|
|
85
|
+
actor: str = "sdk",
|
|
86
|
+
) -> Any:
|
|
87
|
+
"""
|
|
88
|
+
Execute a function with SwiftAPI enforcement.
|
|
89
|
+
|
|
90
|
+
The function will ONLY execute if:
|
|
91
|
+
1. SwiftAPI approves the action
|
|
92
|
+
2. The attestation signature is valid (cryptographic proof)
|
|
93
|
+
3. The attestation is not revoked (if paranoid mode)
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
func: The function to execute (takes no arguments)
|
|
97
|
+
action: Action type for verification
|
|
98
|
+
intent: Human-readable intent description
|
|
99
|
+
params: Optional action parameters
|
|
100
|
+
actor: Actor identifier
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
The return value of func()
|
|
104
|
+
|
|
105
|
+
Raises:
|
|
106
|
+
PolicyViolation: If action is denied by policy
|
|
107
|
+
SignatureVerificationError: If attestation signature is invalid
|
|
108
|
+
AttestationRevokedError: If attestation was revoked
|
|
109
|
+
SwiftAPIError: For other API errors
|
|
110
|
+
"""
|
|
111
|
+
self._authorize(action, intent, params, actor)
|
|
112
|
+
return func()
|
|
113
|
+
|
|
114
|
+
def _authorize(self, action, intent, params=None, actor="sdk"):
|
|
115
|
+
"""Verify the authority's signed decision for this exact action."""
|
|
116
|
+
action_data = _snapshot_action_data({
|
|
117
|
+
"action_type": action,
|
|
118
|
+
"intent": intent,
|
|
119
|
+
"params": params or {},
|
|
120
|
+
})
|
|
121
|
+
expected_fingerprint = _action_fingerprint(action_data)
|
|
122
|
+
|
|
123
|
+
# Step 1: API Call - Get attestation
|
|
124
|
+
try:
|
|
125
|
+
result = self.client.verify(
|
|
126
|
+
action_type=action,
|
|
127
|
+
intent=intent,
|
|
128
|
+
params=action_data["params"],
|
|
129
|
+
actor=actor,
|
|
130
|
+
)
|
|
131
|
+
except PolicyViolation as e:
|
|
132
|
+
if self.verbose:
|
|
133
|
+
print_denied(action, intent, e.denial_reason)
|
|
134
|
+
raise
|
|
135
|
+
|
|
136
|
+
# Check if approved
|
|
137
|
+
if not isinstance(result, dict):
|
|
138
|
+
raise SwiftAPIError("Invalid verification response")
|
|
139
|
+
if result.get("approved") is not True:
|
|
140
|
+
reason = result.get("reason", "Unknown denial reason")
|
|
141
|
+
if self.verbose:
|
|
142
|
+
print_denied(action, intent, reason)
|
|
143
|
+
raise PolicyViolation(
|
|
144
|
+
message=f"Action denied: {reason}",
|
|
145
|
+
action_type=action,
|
|
146
|
+
denial_reason=reason,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
attestation = result.get("execution_attestation")
|
|
150
|
+
if not attestation:
|
|
151
|
+
raise SwiftAPIError("No attestation in response")
|
|
152
|
+
|
|
153
|
+
# Step 2: Crypto Check - OFFLINE TRUTH
|
|
154
|
+
try:
|
|
155
|
+
verify_signature(attestation)
|
|
156
|
+
if not _validate_action_binding(attestation, action, expected_fingerprint):
|
|
157
|
+
raise PolicyViolation(
|
|
158
|
+
"Signed attestation denies the action", action_type=action,
|
|
159
|
+
denial_reason="Signed attestation denies the action",
|
|
160
|
+
)
|
|
161
|
+
jti = attestation["jti"]
|
|
162
|
+
if self.verbose:
|
|
163
|
+
print_verified(jti)
|
|
164
|
+
except (SignatureVerificationError, AttestationExpiredError) as e:
|
|
165
|
+
if self.verbose:
|
|
166
|
+
print_error(str(e))
|
|
167
|
+
raise
|
|
168
|
+
|
|
169
|
+
# Step 3: Online Check - ONLINE TRUTH (optional but recommended)
|
|
170
|
+
if self.paranoid:
|
|
171
|
+
if self.client.check_revocation(jti):
|
|
172
|
+
if self.verbose:
|
|
173
|
+
print_error(f"Attestation {jti} has been revoked")
|
|
174
|
+
raise AttestationRevokedError(jti)
|
|
175
|
+
|
|
176
|
+
claim_attestation(attestation)
|
|
177
|
+
|
|
178
|
+
# Step 4: Execute - THE ACTION RUNS
|
|
179
|
+
if self.verbose:
|
|
180
|
+
print_approved(action, intent)
|
|
181
|
+
|
|
182
|
+
return attestation
|
|
183
|
+
|
|
184
|
+
def protect(
|
|
185
|
+
self,
|
|
186
|
+
action: str,
|
|
187
|
+
intent: str,
|
|
188
|
+
params: Optional[Dict[str, Any]] = None,
|
|
189
|
+
):
|
|
190
|
+
"""
|
|
191
|
+
Decorator to protect a function with SwiftAPI enforcement.
|
|
192
|
+
|
|
193
|
+
Usage:
|
|
194
|
+
@guard.protect(action="file_write", intent="Save config")
|
|
195
|
+
def save_config(data):
|
|
196
|
+
with open("/etc/config", "w") as f:
|
|
197
|
+
f.write(data)
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
action: Action type for verification
|
|
201
|
+
intent: Human-readable intent description
|
|
202
|
+
params: Optional action parameters
|
|
203
|
+
"""
|
|
204
|
+
def decorator(func: Callable) -> Callable:
|
|
205
|
+
@wraps(func)
|
|
206
|
+
def wrapper(*args, **kwargs):
|
|
207
|
+
return self.run(
|
|
208
|
+
func=lambda: func(*args, **kwargs),
|
|
209
|
+
action=action,
|
|
210
|
+
intent=intent,
|
|
211
|
+
params=params,
|
|
212
|
+
actor=func.__name__,
|
|
213
|
+
)
|
|
214
|
+
return wrapper
|
|
215
|
+
return decorator
|
|
216
|
+
|
|
217
|
+
def guard(
|
|
218
|
+
self,
|
|
219
|
+
action: str,
|
|
220
|
+
intent: str,
|
|
221
|
+
params: Optional[Dict[str, Any]] = None,
|
|
222
|
+
) -> "AttestationGuard":
|
|
223
|
+
"""
|
|
224
|
+
Context manager for guarded execution blocks.
|
|
225
|
+
|
|
226
|
+
Usage:
|
|
227
|
+
with guard.guard(action="file_delete", intent="Cleanup temp"):
|
|
228
|
+
os.remove("/tmp/file.txt")
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
action: Action type for verification
|
|
232
|
+
intent: Human-readable intent description
|
|
233
|
+
params: Optional action parameters
|
|
234
|
+
"""
|
|
235
|
+
return AttestationGuard(self, action, intent, params)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class AttestationGuard:
|
|
239
|
+
"""Context manager for guarded execution."""
|
|
240
|
+
|
|
241
|
+
def __init__(
|
|
242
|
+
self,
|
|
243
|
+
enforcement: Enforcement,
|
|
244
|
+
action: str,
|
|
245
|
+
intent: str,
|
|
246
|
+
params: Optional[Dict[str, Any]] = None,
|
|
247
|
+
):
|
|
248
|
+
self.enforcement = enforcement
|
|
249
|
+
self.action = action
|
|
250
|
+
self.intent = intent
|
|
251
|
+
self.params = params
|
|
252
|
+
self.attestation = None
|
|
253
|
+
|
|
254
|
+
def __enter__(self):
|
|
255
|
+
# Get and verify attestation before entering the block
|
|
256
|
+
self.attestation = self.enforcement._authorize(self.action, self.intent, self.params)
|
|
257
|
+
return self.attestation
|
|
258
|
+
|
|
259
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
260
|
+
return False # Don't suppress exceptions
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
# Convenience function for one-off protected execution
|
|
264
|
+
def enforce(
|
|
265
|
+
client: SwiftAPI,
|
|
266
|
+
func: Callable[[], Any],
|
|
267
|
+
action: str,
|
|
268
|
+
intent: str,
|
|
269
|
+
params: Optional[Dict[str, Any]] = None,
|
|
270
|
+
) -> Any:
|
|
271
|
+
"""
|
|
272
|
+
One-off enforcement without creating an Enforcement instance.
|
|
273
|
+
|
|
274
|
+
Usage:
|
|
275
|
+
from swiftapi import SwiftAPI, enforce
|
|
276
|
+
|
|
277
|
+
api = SwiftAPI(key="...")
|
|
278
|
+
enforce(api, lambda: rm_rf("/"), action="file_delete", intent="Nuke it")
|
|
279
|
+
"""
|
|
280
|
+
guard = Enforcement(client)
|
|
281
|
+
return guard.run(func, action, intent, params)
|