swiftapi-python 1.0.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.
swiftapi/__init__.py ADDED
@@ -0,0 +1,91 @@
1
+ """
2
+ SwiftAPI Python SDK
3
+
4
+ No AI action executes without verification.
5
+
6
+ Usage:
7
+ from swiftapi import SwiftAPI, Enforcement
8
+
9
+ # Initialize client
10
+ api = SwiftAPI(key="swiftapi_live_...")
11
+
12
+ # Create enforcement point
13
+ guard = Enforcement(api)
14
+
15
+ # Protect dangerous operations
16
+ guard.run(
17
+ lambda: os.system("rm -rf /tmp/data"),
18
+ action="file_delete",
19
+ intent="Cleanup temporary data"
20
+ )
21
+
22
+ # Or use decorator
23
+ @guard.protect(action="api_call", intent="Send email")
24
+ def send_email(to, subject, body):
25
+ ...
26
+
27
+ # Or use context manager
28
+ with guard.guard(action="database_write", intent="Update user"):
29
+ db.update(user_id, data)
30
+ """
31
+
32
+ __version__ = "1.0.0"
33
+ __author__ = "Rayan Pal"
34
+
35
+ # Core exports
36
+ from .client import SwiftAPI
37
+ from .enforcement import Enforcement, enforce
38
+ from .verifier import verify_signature, is_valid, get_public_key
39
+
40
+ # Exceptions
41
+ from .exceptions import (
42
+ SwiftAPIError,
43
+ AuthenticationError,
44
+ PolicyViolation,
45
+ SignatureVerificationError,
46
+ AttestationExpiredError,
47
+ AttestationRevokedError,
48
+ RateLimitError,
49
+ NetworkError,
50
+ )
51
+
52
+ # UX utilities
53
+ from .utils import (
54
+ Colors,
55
+ Symbols,
56
+ print_approved,
57
+ print_denied,
58
+ print_verified,
59
+ print_error,
60
+ print_info,
61
+ )
62
+
63
+ __all__ = [
64
+ # Version
65
+ "__version__",
66
+ # Core
67
+ "SwiftAPI",
68
+ "Enforcement",
69
+ "enforce",
70
+ # Verification
71
+ "verify_signature",
72
+ "is_valid",
73
+ "get_public_key",
74
+ # Exceptions
75
+ "SwiftAPIError",
76
+ "AuthenticationError",
77
+ "PolicyViolation",
78
+ "SignatureVerificationError",
79
+ "AttestationExpiredError",
80
+ "AttestationRevokedError",
81
+ "RateLimitError",
82
+ "NetworkError",
83
+ # UX
84
+ "Colors",
85
+ "Symbols",
86
+ "print_approved",
87
+ "print_denied",
88
+ "print_verified",
89
+ "print_error",
90
+ "print_info",
91
+ ]
swiftapi/client.py ADDED
@@ -0,0 +1,282 @@
1
+ """
2
+ SwiftAPI SDK - HTTP Client
3
+
4
+ This module provides the HTTP client for communicating with SwiftAPI.
5
+ """
6
+
7
+ import requests
8
+ from typing import Dict, Any, Optional, List
9
+
10
+ from .exceptions import (
11
+ SwiftAPIError,
12
+ AuthenticationError,
13
+ PolicyViolation,
14
+ RateLimitError,
15
+ NetworkError,
16
+ )
17
+
18
+ DEFAULT_BASE_URL = "https://swiftapi.ai"
19
+ DEFAULT_TIMEOUT = 30
20
+
21
+
22
+ class SwiftAPI:
23
+ """
24
+ SwiftAPI HTTP Client.
25
+
26
+ Usage:
27
+ api = SwiftAPI(key="swiftapi_live_...")
28
+ result = api.verify("file_write", "Save config", {"path": "/etc/config"})
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ key: str,
34
+ base_url: str = DEFAULT_BASE_URL,
35
+ timeout: int = DEFAULT_TIMEOUT,
36
+ ):
37
+ """
38
+ Initialize SwiftAPI client.
39
+
40
+ Args:
41
+ key: SwiftAPI authority key (swiftapi_live_...)
42
+ base_url: API base URL (default: https://swiftapi.ai)
43
+ timeout: Request timeout in seconds
44
+ """
45
+ if not key:
46
+ raise AuthenticationError("API key is required")
47
+ if not key.startswith("swiftapi_live_"):
48
+ raise AuthenticationError("Invalid key format: must start with 'swiftapi_live_'")
49
+
50
+ self.key = key
51
+ self.base_url = base_url.rstrip("/")
52
+ self.timeout = timeout
53
+ self._session = requests.Session()
54
+ self._session.headers.update({
55
+ "X-SwiftAPI-Authority": key,
56
+ "Content-Type": "application/json",
57
+ "User-Agent": "swiftapi-python/1.0.0",
58
+ })
59
+
60
+ def _request(
61
+ self,
62
+ method: str,
63
+ endpoint: str,
64
+ json: Dict = None,
65
+ params: Dict = None,
66
+ ) -> Dict[str, Any]:
67
+ """Make HTTP request to SwiftAPI."""
68
+ url = f"{self.base_url}{endpoint}"
69
+
70
+ try:
71
+ response = self._session.request(
72
+ method=method,
73
+ url=url,
74
+ json=json,
75
+ params=params,
76
+ timeout=self.timeout,
77
+ )
78
+ except requests.exceptions.ConnectionError as e:
79
+ raise NetworkError(f"Failed to connect to SwiftAPI: {e}")
80
+ except requests.exceptions.Timeout:
81
+ raise NetworkError(f"Request timed out after {self.timeout}s")
82
+ except requests.exceptions.RequestException as e:
83
+ raise NetworkError(f"Request failed: {e}")
84
+
85
+ # Handle response
86
+ return self._handle_response(response)
87
+
88
+ def _handle_response(self, response: requests.Response) -> Dict[str, Any]:
89
+ """Handle API response and raise appropriate exceptions."""
90
+ # Rate limit
91
+ if response.status_code == 429:
92
+ retry_after = response.headers.get("Retry-After")
93
+ raise RateLimitError(int(retry_after) if retry_after else None)
94
+
95
+ # Auth errors
96
+ if response.status_code == 401:
97
+ raise AuthenticationError("Invalid API key")
98
+ if response.status_code == 403:
99
+ try:
100
+ data = response.json()
101
+ raise AuthenticationError(data.get("detail", {}).get("message", "Forbidden"))
102
+ except ValueError:
103
+ raise AuthenticationError("Forbidden")
104
+
105
+ # Parse JSON
106
+ try:
107
+ data = response.json()
108
+ except ValueError:
109
+ raise SwiftAPIError(f"Invalid JSON response: {response.text[:200]}")
110
+
111
+ # Policy violations (denied actions)
112
+ if response.status_code == 200 and data.get("approved") is False:
113
+ raise PolicyViolation(
114
+ message=data.get("reason", "Action denied by policy"),
115
+ action_type=data.get("action", {}).get("type"),
116
+ denial_reason=data.get("reason"),
117
+ )
118
+
119
+ # Other errors
120
+ if response.status_code >= 400:
121
+ detail = data.get("detail", {})
122
+ if isinstance(detail, dict):
123
+ message = detail.get("message", str(detail))
124
+ else:
125
+ message = str(detail)
126
+ raise SwiftAPIError(message, response.status_code, data)
127
+
128
+ return data
129
+
130
+ # =========================================================================
131
+ # Core Endpoints
132
+ # =========================================================================
133
+
134
+ def verify(
135
+ self,
136
+ action_type: str,
137
+ intent: str,
138
+ params: Optional[Dict[str, Any]] = None,
139
+ actor: str = "sdk",
140
+ app_id: str = "swiftapi-python",
141
+ ) -> Dict[str, Any]:
142
+ """
143
+ Submit an action for verification and get an execution attestation.
144
+
145
+ Args:
146
+ action_type: Type of action (e.g., "file_write", "api_call")
147
+ intent: Human-readable description of intent
148
+ params: Optional action parameters
149
+ actor: Identifier for the requesting agent
150
+ app_id: Application identifier
151
+
152
+ Returns:
153
+ Full verification response including execution_attestation
154
+
155
+ Raises:
156
+ PolicyViolation: If action is denied
157
+ AuthenticationError: If key is invalid
158
+ SwiftAPIError: For other errors
159
+ """
160
+ payload = {
161
+ "action": {
162
+ "type": action_type,
163
+ "intent": intent,
164
+ "params": params or {},
165
+ },
166
+ "actor": actor,
167
+ "app_id": app_id,
168
+ }
169
+ return self._request("POST", "/verify", json=payload)
170
+
171
+ def verify_attestation(self, attestation: Dict[str, Any]) -> Dict[str, Any]:
172
+ """
173
+ Verify an existing attestation with the server.
174
+
175
+ Args:
176
+ attestation: The execution_attestation to verify
177
+
178
+ Returns:
179
+ Verification result
180
+ """
181
+ return self._request("POST", "/attestation/verify", json=attestation)
182
+
183
+ def revoke(self, jti: str, reason: str = "SDK revocation") -> Dict[str, Any]:
184
+ """
185
+ Revoke an attestation by JTI.
186
+
187
+ Args:
188
+ jti: The attestation's unique identifier
189
+ reason: Reason for revocation
190
+
191
+ Returns:
192
+ Revocation confirmation
193
+ """
194
+ payload = {"jti": jti, "reason": reason}
195
+ return self._request("POST", "/attestation/revoke", json=payload)
196
+
197
+ def check_revocation(self, jti: str) -> bool:
198
+ """
199
+ Check if an attestation has been revoked.
200
+
201
+ This is a fast check - use for real-time revocation verification.
202
+
203
+ Args:
204
+ jti: The attestation's unique identifier
205
+
206
+ Returns:
207
+ True if revoked, False if valid
208
+ """
209
+ try:
210
+ result = self._request("GET", f"/attestation/revoked/{jti}")
211
+ return result.get("revoked", False)
212
+ except SwiftAPIError:
213
+ # If endpoint fails, assume not revoked (fail-open for availability)
214
+ return False
215
+
216
+ # =========================================================================
217
+ # Info Endpoints
218
+ # =========================================================================
219
+
220
+ def get_info(self) -> Dict[str, Any]:
221
+ """Get API info and public key."""
222
+ return self._request("GET", "/")
223
+
224
+ def health(self) -> bool:
225
+ """Check API health."""
226
+ try:
227
+ result = self._request("GET", "/health")
228
+ return result.get("status") == "healthy"
229
+ except Exception:
230
+ return False
231
+
232
+ def get_policies(self) -> List[Dict[str, Any]]:
233
+ """Get active policies."""
234
+ result = self._request("GET", "/policies")
235
+ return result.get("policies", [])
236
+
237
+ def get_scopes(self) -> List[str]:
238
+ """Get available authority scopes."""
239
+ result = self._request("GET", "/authority/scopes")
240
+ return result.get("scopes", [])
241
+
242
+ # =========================================================================
243
+ # Key Management (Admin Only)
244
+ # =========================================================================
245
+
246
+ def list_keys(self) -> List[Dict[str, Any]]:
247
+ """List authority keys (requires admin scope)."""
248
+ result = self._request("GET", "/authority/keys")
249
+ return result.get("keys", [])
250
+
251
+ def create_key(
252
+ self,
253
+ name: str,
254
+ scopes: List[str],
255
+ ) -> Dict[str, Any]:
256
+ """
257
+ Create a new authority key (requires admin scope).
258
+
259
+ Args:
260
+ name: Key name
261
+ scopes: List of scopes to grant
262
+
263
+ Returns:
264
+ New key details (including the key itself - shown only once!)
265
+ """
266
+ payload = {"name": name, "scopes": scopes}
267
+ return self._request("POST", "/authority/keys", json=payload)
268
+
269
+ def revoke_key(self, key_hash: str) -> Dict[str, Any]:
270
+ """Revoke an authority key by hash."""
271
+ return self._request("DELETE", f"/authority/keys/{key_hash}")
272
+
273
+ # =========================================================================
274
+ # Context Manager
275
+ # =========================================================================
276
+
277
+ def __enter__(self):
278
+ return self
279
+
280
+ def __exit__(self, exc_type, exc_val, exc_tb):
281
+ self._session.close()
282
+ return False
@@ -0,0 +1,280 @@
1
+ """
2
+ SwiftAPI SDK - Enforcement Point (The Golden Loop)
3
+
4
+ This module provides the high-level enforcement mechanism that ensures
5
+ no action executes without proper 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 verify_signature, is_valid
21
+ from .exceptions import (
22
+ PolicyViolation,
23
+ SignatureVerificationError,
24
+ AttestationRevokedError,
25
+ AttestationExpiredError,
26
+ SwiftAPIError,
27
+ )
28
+ from .utils import print_approved, print_denied, print_verified, print_error
29
+
30
+
31
+ class Enforcement:
32
+ """
33
+ SwiftAPI Enforcement Point.
34
+
35
+ This is the "ignition key" - no action executes without verification.
36
+
37
+ Usage:
38
+ api = SwiftAPI(key="swiftapi_live_...")
39
+ guard = Enforcement(api)
40
+
41
+ # Option 1: Run with enforcement
42
+ guard.run(
43
+ lambda: dangerous_operation(),
44
+ action="file_delete",
45
+ intent="Remove temp files"
46
+ )
47
+
48
+ # Option 2: Decorator
49
+ @guard.protect(action="api_call", intent="Send notification")
50
+ def send_notification(user_id, message):
51
+ ...
52
+ """
53
+
54
+ def __init__(
55
+ self,
56
+ client: SwiftAPI,
57
+ paranoid: bool = False,
58
+ verbose: bool = True,
59
+ ):
60
+ """
61
+ Initialize enforcement point.
62
+
63
+ Args:
64
+ client: SwiftAPI client instance
65
+ paranoid: If True, always check revocation online (slower but safer)
66
+ verbose: If True, print status messages
67
+ """
68
+ self.client = client
69
+ self.paranoid = paranoid
70
+ self.verbose = verbose
71
+
72
+ def run(
73
+ self,
74
+ func: Callable[[], Any],
75
+ action: str,
76
+ intent: str,
77
+ params: Optional[Dict[str, Any]] = None,
78
+ actor: str = "sdk",
79
+ ) -> Any:
80
+ """
81
+ Execute a function with SwiftAPI enforcement.
82
+
83
+ The function will ONLY execute if:
84
+ 1. SwiftAPI approves the action
85
+ 2. The attestation signature is valid (cryptographic proof)
86
+ 3. The attestation is not revoked (if paranoid mode)
87
+
88
+ Args:
89
+ func: The function to execute (takes no arguments)
90
+ action: Action type for verification
91
+ intent: Human-readable intent description
92
+ params: Optional action parameters
93
+ actor: Actor identifier
94
+
95
+ Returns:
96
+ The return value of func()
97
+
98
+ Raises:
99
+ PolicyViolation: If action is denied by policy
100
+ SignatureVerificationError: If attestation signature is invalid
101
+ AttestationRevokedError: If attestation was revoked
102
+ SwiftAPIError: For other API errors
103
+ """
104
+ # Step 1: API Call - Get attestation
105
+ try:
106
+ result = self.client.verify(
107
+ action_type=action,
108
+ intent=intent,
109
+ params=params,
110
+ actor=actor,
111
+ )
112
+ except PolicyViolation as e:
113
+ if self.verbose:
114
+ print_denied(action, intent, e.denial_reason)
115
+ raise
116
+
117
+ # Check if approved
118
+ if not result.get("approved"):
119
+ reason = result.get("reason", "Unknown denial reason")
120
+ if self.verbose:
121
+ print_denied(action, intent, reason)
122
+ raise PolicyViolation(
123
+ message=f"Action denied: {reason}",
124
+ action_type=action,
125
+ denial_reason=reason,
126
+ )
127
+
128
+ attestation = result.get("execution_attestation")
129
+ if not attestation:
130
+ raise SwiftAPIError("No attestation in response")
131
+
132
+ jti = attestation.get("jti")
133
+
134
+ # Step 2: Crypto Check - OFFLINE TRUTH
135
+ try:
136
+ verify_signature(attestation)
137
+ if self.verbose:
138
+ print_verified(jti)
139
+ except (SignatureVerificationError, AttestationExpiredError) as e:
140
+ if self.verbose:
141
+ print_error(str(e))
142
+ raise
143
+
144
+ # Step 3: Online Check - ONLINE TRUTH (optional but recommended)
145
+ if self.paranoid:
146
+ if self.client.check_revocation(jti):
147
+ if self.verbose:
148
+ print_error(f"Attestation {jti} has been revoked")
149
+ raise AttestationRevokedError(jti)
150
+
151
+ # Step 4: Execute - THE ACTION RUNS
152
+ if self.verbose:
153
+ print_approved(action, intent)
154
+
155
+ return func()
156
+
157
+ def protect(
158
+ self,
159
+ action: str,
160
+ intent: str,
161
+ params: Optional[Dict[str, Any]] = None,
162
+ ):
163
+ """
164
+ Decorator to protect a function with SwiftAPI enforcement.
165
+
166
+ Usage:
167
+ @guard.protect(action="file_write", intent="Save config")
168
+ def save_config(data):
169
+ with open("/etc/config", "w") as f:
170
+ f.write(data)
171
+
172
+ Args:
173
+ action: Action type for verification
174
+ intent: Human-readable intent description
175
+ params: Optional action parameters
176
+ """
177
+ def decorator(func: Callable) -> Callable:
178
+ @wraps(func)
179
+ def wrapper(*args, **kwargs):
180
+ return self.run(
181
+ func=lambda: func(*args, **kwargs),
182
+ action=action,
183
+ intent=intent,
184
+ params=params,
185
+ actor=func.__name__,
186
+ )
187
+ return wrapper
188
+ return decorator
189
+
190
+ def guard(
191
+ self,
192
+ action: str,
193
+ intent: str,
194
+ params: Optional[Dict[str, Any]] = None,
195
+ ) -> "AttestationGuard":
196
+ """
197
+ Context manager for guarded execution blocks.
198
+
199
+ Usage:
200
+ with guard.guard(action="file_delete", intent="Cleanup temp"):
201
+ os.remove("/tmp/file.txt")
202
+
203
+ Args:
204
+ action: Action type for verification
205
+ intent: Human-readable intent description
206
+ params: Optional action parameters
207
+ """
208
+ return AttestationGuard(self, action, intent, params)
209
+
210
+
211
+ class AttestationGuard:
212
+ """Context manager for guarded execution."""
213
+
214
+ def __init__(
215
+ self,
216
+ enforcement: Enforcement,
217
+ action: str,
218
+ intent: str,
219
+ params: Optional[Dict[str, Any]] = None,
220
+ ):
221
+ self.enforcement = enforcement
222
+ self.action = action
223
+ self.intent = intent
224
+ self.params = params
225
+ self.attestation = None
226
+
227
+ def __enter__(self):
228
+ # Get and verify attestation before entering the block
229
+ result = self.enforcement.client.verify(
230
+ action_type=self.action,
231
+ intent=self.intent,
232
+ params=self.params,
233
+ )
234
+
235
+ if not result.get("approved"):
236
+ reason = result.get("reason", "Unknown")
237
+ if self.enforcement.verbose:
238
+ print_denied(self.action, self.intent, reason)
239
+ raise PolicyViolation(
240
+ message=f"Action denied: {reason}",
241
+ action_type=self.action,
242
+ denial_reason=reason,
243
+ )
244
+
245
+ self.attestation = result.get("execution_attestation")
246
+ verify_signature(self.attestation)
247
+
248
+ if self.enforcement.paranoid:
249
+ jti = self.attestation.get("jti")
250
+ if self.enforcement.client.check_revocation(jti):
251
+ raise AttestationRevokedError(jti)
252
+
253
+ if self.enforcement.verbose:
254
+ print_approved(self.action, self.intent)
255
+
256
+ return self.attestation
257
+
258
+ def __exit__(self, exc_type, exc_val, exc_tb):
259
+ return False # Don't suppress exceptions
260
+
261
+
262
+ # Convenience function for one-off protected execution
263
+ def enforce(
264
+ client: SwiftAPI,
265
+ func: Callable[[], Any],
266
+ action: str,
267
+ intent: str,
268
+ params: Optional[Dict[str, Any]] = None,
269
+ ) -> Any:
270
+ """
271
+ One-off enforcement without creating an Enforcement instance.
272
+
273
+ Usage:
274
+ from swiftapi import SwiftAPI, enforce
275
+
276
+ api = SwiftAPI(key="...")
277
+ enforce(api, lambda: rm_rf("/"), action="file_delete", intent="Nuke it")
278
+ """
279
+ guard = Enforcement(client)
280
+ return guard.run(func, action, intent, params)
swiftapi/exceptions.py ADDED
@@ -0,0 +1,61 @@
1
+ """
2
+ SwiftAPI SDK Exceptions
3
+ """
4
+
5
+
6
+ class SwiftAPIError(Exception):
7
+ """Base exception for SwiftAPI SDK errors."""
8
+
9
+ def __init__(self, message: str, status_code: int = None, response: dict = None):
10
+ self.message = message
11
+ self.status_code = status_code
12
+ self.response = response
13
+ super().__init__(self.message)
14
+
15
+
16
+ class AuthenticationError(SwiftAPIError):
17
+ """Raised when API key is invalid or missing."""
18
+ pass
19
+
20
+
21
+ class PolicyViolation(SwiftAPIError):
22
+ """Raised when an action violates policy and is denied."""
23
+
24
+ def __init__(self, message: str, action_type: str = None, denial_reason: str = None):
25
+ self.action_type = action_type
26
+ self.denial_reason = denial_reason
27
+ super().__init__(message)
28
+
29
+
30
+ class SignatureVerificationError(SwiftAPIError):
31
+ """Raised when attestation signature verification fails."""
32
+ pass
33
+
34
+
35
+ class AttestationExpiredError(SwiftAPIError):
36
+ """Raised when an attestation has expired."""
37
+ pass
38
+
39
+
40
+ class AttestationRevokedError(SwiftAPIError):
41
+ """Raised when an attestation has been revoked."""
42
+
43
+ def __init__(self, jti: str):
44
+ self.jti = jti
45
+ super().__init__(f"Attestation {jti} has been revoked")
46
+
47
+
48
+ class RateLimitError(SwiftAPIError):
49
+ """Raised when rate limit is exceeded."""
50
+
51
+ def __init__(self, retry_after: int = None):
52
+ self.retry_after = retry_after
53
+ message = "Rate limit exceeded"
54
+ if retry_after:
55
+ message += f", retry after {retry_after}s"
56
+ super().__init__(message)
57
+
58
+
59
+ class NetworkError(SwiftAPIError):
60
+ """Raised when network connectivity fails."""
61
+ pass
swiftapi/utils.py ADDED
@@ -0,0 +1,89 @@
1
+ """
2
+ SwiftAPI SDK Utilities - UX and Display
3
+ """
4
+
5
+ import sys
6
+
7
+ try:
8
+ from colorama import Fore, Style, init
9
+ init(autoreset=True)
10
+ COLORS_AVAILABLE = True
11
+ except ImportError:
12
+ COLORS_AVAILABLE = False
13
+
14
+
15
+ # Color constants
16
+ class Colors:
17
+ """Terminal color codes for SwiftAPI output."""
18
+
19
+ if COLORS_AVAILABLE:
20
+ RED = Fore.RED
21
+ GREEN = Fore.GREEN
22
+ YELLOW = Fore.YELLOW
23
+ BLUE = Fore.BLUE
24
+ MAGENTA = Fore.MAGENTA
25
+ CYAN = Fore.CYAN
26
+ WHITE = Fore.WHITE
27
+ RESET = Style.RESET_ALL
28
+ BOLD = Style.BRIGHT
29
+ else:
30
+ RED = GREEN = YELLOW = BLUE = MAGENTA = CYAN = WHITE = RESET = BOLD = ""
31
+
32
+
33
+ # Unicode symbols
34
+ class Symbols:
35
+ """Unicode symbols for SwiftAPI output."""
36
+
37
+ LOCK = "\U0001F512" # Locked
38
+ UNLOCK = "\U0001F513" # Unlocked
39
+ CHECK = "\u2713" # Checkmark
40
+ CROSS = "\u2717" # X mark
41
+ SHIELD = "\U0001F6E1" # Shield
42
+ KEY = "\U0001F511" # Key
43
+ WARNING = "\u26A0" # Warning
44
+ LIGHTNING = "\u26A1" # Lightning bolt
45
+
46
+
47
+ def print_approved(action_type: str, intent: str):
48
+ """Print approval message with green styling."""
49
+ print(f"{Colors.GREEN}{Symbols.CHECK} APPROVED{Colors.RESET} [{action_type}] {intent}")
50
+
51
+
52
+ def print_denied(action_type: str, intent: str, reason: str = None):
53
+ """Print denial message with red styling."""
54
+ msg = f"{Colors.RED}{Symbols.LOCK} DENIED{Colors.RESET} [{action_type}] {intent}"
55
+ if reason:
56
+ msg += f"\n {Colors.YELLOW}Reason: {reason}{Colors.RESET}"
57
+ print(msg, file=sys.stderr)
58
+
59
+
60
+ def print_verified(jti: str):
61
+ """Print verification success message."""
62
+ print(f"{Colors.CYAN}{Symbols.SHIELD} VERIFIED{Colors.RESET} Attestation {jti[:16]}...")
63
+
64
+
65
+ def print_revoked(jti: str):
66
+ """Print revocation message."""
67
+ print(f"{Colors.RED}{Symbols.CROSS} REVOKED{Colors.RESET} Attestation {jti[:16]}...")
68
+
69
+
70
+ def print_error(message: str):
71
+ """Print error message."""
72
+ print(f"{Colors.RED}{Symbols.WARNING} ERROR{Colors.RESET} {message}", file=sys.stderr)
73
+
74
+
75
+ def print_info(message: str):
76
+ """Print info message."""
77
+ print(f"{Colors.BLUE}{Symbols.LIGHTNING} INFO{Colors.RESET} {message}")
78
+
79
+
80
+ def format_attestation(attestation: dict) -> str:
81
+ """Format attestation for display."""
82
+ lines = [
83
+ f"{Colors.CYAN}Execution Attestation{Colors.RESET}",
84
+ f" JTI: {attestation.get('jti', 'N/A')}",
85
+ f" Expires: {attestation.get('expires_at', 'N/A')}",
86
+ f" Fingerprint: {attestation.get('action_fingerprint', 'N/A')[:32]}...",
87
+ f" Signed: {attestation.get('signing_mode', 'N/A')}",
88
+ ]
89
+ return "\n".join(lines)
swiftapi/verifier.py ADDED
@@ -0,0 +1,122 @@
1
+ """
2
+ SwiftAPI SDK - Ed25519 Signature Verifier (The Shield)
3
+
4
+ This module provides OFFLINE cryptographic verification of execution attestations.
5
+ It does not require network connectivity - it uses the hardcoded SwiftAPI public key.
6
+ """
7
+
8
+ import base64
9
+ import json
10
+ from datetime import datetime, timezone
11
+ from typing import Dict, Any
12
+
13
+ from nacl.signing import VerifyKey
14
+ from nacl.exceptions import BadSignatureError
15
+
16
+ from .exceptions import (
17
+ SignatureVerificationError,
18
+ AttestationExpiredError,
19
+ )
20
+
21
+ # SwiftAPI's Ed25519 Public Key (Base64)
22
+ # This is the ONLY source of truth for attestation verification
23
+ SWIFTAPI_PUBLIC_KEY_B64 = "jajZXZ0R3CUWkE5/5Mxx5Y76CdsSzaPDuT7aWnooZSk="
24
+
25
+ # Decode public key once at module load
26
+ SWIFTAPI_PUBLIC_KEY = base64.b64decode(SWIFTAPI_PUBLIC_KEY_B64)
27
+ VERIFY_KEY = VerifyKey(SWIFTAPI_PUBLIC_KEY)
28
+
29
+
30
+ def verify_signature(attestation: Dict[str, Any]) -> bool:
31
+ """
32
+ Verify the Ed25519 signature of an execution attestation.
33
+
34
+ This is OFFLINE verification - no network required.
35
+ The signature proves the attestation was issued by SwiftAPI.
36
+
37
+ Args:
38
+ attestation: The execution_attestation dict from /verify response
39
+
40
+ Returns:
41
+ True if signature is valid
42
+
43
+ Raises:
44
+ SignatureVerificationError: If signature is invalid or missing
45
+ AttestationExpiredError: If attestation has expired
46
+ """
47
+ # Extract required fields
48
+ signature_b64 = attestation.get("signature")
49
+ if not signature_b64:
50
+ raise SignatureVerificationError("Missing signature in attestation")
51
+
52
+ jti = attestation.get("jti")
53
+ action_fingerprint = attestation.get("action_fingerprint")
54
+ expires_at = attestation.get("expires_at")
55
+
56
+ if not all([jti, action_fingerprint, expires_at]):
57
+ raise SignatureVerificationError("Incomplete attestation: missing required fields")
58
+
59
+ # Check expiration FIRST
60
+ try:
61
+ expiry = datetime.fromisoformat(expires_at.replace("Z", "+00:00"))
62
+ now = datetime.now(timezone.utc)
63
+ if now > expiry:
64
+ raise AttestationExpiredError(f"Attestation expired at {expires_at}")
65
+ except ValueError as e:
66
+ raise SignatureVerificationError(f"Invalid expiration format: {e}")
67
+
68
+ # Reconstruct the signed payload (deterministic serialization)
69
+ # This MUST match the server's signing logic exactly
70
+ signed_payload = _reconstruct_signed_payload(attestation)
71
+
72
+ # Decode signature
73
+ try:
74
+ signature = base64.b64decode(signature_b64)
75
+ except Exception as e:
76
+ raise SignatureVerificationError(f"Invalid signature encoding: {e}")
77
+
78
+ # Verify signature
79
+ try:
80
+ VERIFY_KEY.verify(signed_payload, signature)
81
+ return True
82
+ except BadSignatureError:
83
+ raise SignatureVerificationError(
84
+ "INVALID SIGNATURE: Attestation was not signed by SwiftAPI. "
85
+ "This could indicate a forged or tampered attestation."
86
+ )
87
+
88
+
89
+ def _reconstruct_signed_payload(attestation: Dict[str, Any]) -> bytes:
90
+ """
91
+ Reconstruct the exact payload that was signed by SwiftAPI.
92
+
93
+ The server signs the attestation BEFORE adding signature/signing_mode fields.
94
+ We must reconstruct that exact payload.
95
+ """
96
+ # Copy attestation and remove signature fields (they weren't in the signed payload)
97
+ signed_fields = {k: v for k, v in attestation.items() if k not in ("signature", "signing_mode")}
98
+
99
+ # Deterministic JSON serialization (must match server exactly)
100
+ payload_str = json.dumps(signed_fields, sort_keys=True, separators=(",", ":"))
101
+ return payload_str.encode("utf-8")
102
+
103
+
104
+ def get_public_key() -> str:
105
+ """Return SwiftAPI's public key in Base64 format."""
106
+ return SWIFTAPI_PUBLIC_KEY_B64
107
+
108
+
109
+ def is_valid(attestation: Dict[str, Any]) -> bool:
110
+ """
111
+ Check if attestation is valid without raising exceptions.
112
+
113
+ Args:
114
+ attestation: The execution_attestation dict
115
+
116
+ Returns:
117
+ True if valid, False otherwise
118
+ """
119
+ try:
120
+ return verify_signature(attestation)
121
+ except Exception:
122
+ return False
@@ -0,0 +1,256 @@
1
+ Metadata-Version: 2.4
2
+ Name: swiftapi-python
3
+ Version: 1.0.0
4
+ Summary: SwiftAPI Python SDK - AI Action Verification Gateway
5
+ Author-email: Rayan Pal <rayan@swiftapi.ai>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://swiftapi.ai
8
+ Project-URL: Documentation, https://docs.swiftapi.ai
9
+ Project-URL: Repository, https://github.com/swiftapi/swiftapi-python
10
+ Project-URL: Issues, https://github.com/swiftapi/swiftapi-python/issues
11
+ Keywords: swiftapi,ai,verification,attestation,security,governance,enforcement
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Security
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+ Requires-Dist: requests>=2.31.0
25
+ Requires-Dist: pynacl>=1.5.0
26
+ Requires-Dist: colorama>=0.4.6
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
29
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
30
+ Requires-Dist: black>=23.0.0; extra == "dev"
31
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
32
+ Requires-Dist: types-requests>=2.31.0; extra == "dev"
33
+
34
+ # SwiftAPI Python SDK
35
+
36
+ **No AI action executes without verification.**
37
+
38
+ SwiftAPI is the ignition key for AI agents. This SDK provides Python bindings for the SwiftAPI execution governance protocol.
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ pip install swiftapi
44
+ ```
45
+
46
+ ## Quick Start
47
+
48
+ ```python
49
+ from swiftapi import SwiftAPI, Enforcement
50
+
51
+ # Initialize client with your API key
52
+ api = SwiftAPI(key="swiftapi_live_...")
53
+
54
+ # Create an enforcement point
55
+ guard = Enforcement(api)
56
+
57
+ # THE LINE THAT SAVES THE COMPANY
58
+ guard.run(
59
+ lambda: os.system("rm -rf /tmp/data"),
60
+ action="file_delete",
61
+ intent="Cleanup temporary files"
62
+ )
63
+ ```
64
+
65
+ If the action is denied by policy, a `PolicyViolation` exception is raised and **nothing executes**.
66
+
67
+ ## Features
68
+
69
+ - **Cryptographic Enforcement**: Ed25519 signed attestations prove authorization
70
+ - **Offline Verification**: Verify attestation signatures without network calls
71
+ - **Policy Enforcement**: Actions blocked if they violate configured policies
72
+ - **Rate Limiting**: Built-in handling for API rate limits
73
+ - **Beautiful Output**: Color-coded terminal output for approvals/denials
74
+
75
+ ## Usage Patterns
76
+
77
+ ### 1. Direct Execution
78
+
79
+ ```python
80
+ from swiftapi import SwiftAPI, Enforcement
81
+
82
+ api = SwiftAPI(key="swiftapi_live_...")
83
+ guard = Enforcement(api)
84
+
85
+ # Execute with verification
86
+ result = guard.run(
87
+ lambda: dangerous_operation(),
88
+ action="database_write",
89
+ intent="Update user preferences"
90
+ )
91
+ ```
92
+
93
+ ### 2. Decorator
94
+
95
+ ```python
96
+ @guard.protect(action="api_call", intent="Send notification")
97
+ def send_notification(user_id: str, message: str):
98
+ # This only runs if SwiftAPI approves
99
+ notification_service.send(user_id, message)
100
+
101
+ # Usage - automatically enforced
102
+ send_notification("user123", "Hello!")
103
+ ```
104
+
105
+ ### 3. Context Manager
106
+
107
+ ```python
108
+ with guard.guard(action="file_write", intent="Save configuration"):
109
+ # This block only executes if approved
110
+ with open("/etc/myapp/config.json", "w") as f:
111
+ json.dump(config, f)
112
+ ```
113
+
114
+ ### 4. One-off Enforcement
115
+
116
+ ```python
117
+ from swiftapi import SwiftAPI, enforce
118
+
119
+ api = SwiftAPI(key="swiftapi_live_...")
120
+ enforce(api, lambda: risky_operation(), action="admin", intent="Reset system")
121
+ ```
122
+
123
+ ## Paranoid Mode
124
+
125
+ For maximum security, enable paranoid mode to check revocation status online:
126
+
127
+ ```python
128
+ guard = Enforcement(api, paranoid=True)
129
+ ```
130
+
131
+ This adds an extra network call but ensures revoked attestations are caught in real-time.
132
+
133
+ ## Offline Verification
134
+
135
+ You can verify attestation signatures without any network calls:
136
+
137
+ ```python
138
+ from swiftapi import verify_signature, is_valid
139
+
140
+ # Verify signature (raises exception if invalid)
141
+ verify_signature(attestation)
142
+
143
+ # Check validity without exceptions
144
+ if is_valid(attestation):
145
+ print("Attestation is valid")
146
+ ```
147
+
148
+ ## Error Handling
149
+
150
+ ```python
151
+ from swiftapi import (
152
+ SwiftAPI,
153
+ Enforcement,
154
+ PolicyViolation,
155
+ SignatureVerificationError,
156
+ AttestationRevokedError,
157
+ )
158
+
159
+ api = SwiftAPI(key="swiftapi_live_...")
160
+ guard = Enforcement(api)
161
+
162
+ try:
163
+ guard.run(lambda: delete_everything(), action="nuke", intent="YOLO")
164
+ except PolicyViolation as e:
165
+ print(f"Action denied: {e.denial_reason}")
166
+ except SignatureVerificationError:
167
+ print("CRITICAL: Attestation signature is invalid!")
168
+ except AttestationRevokedError as e:
169
+ print(f"Attestation {e.jti} was revoked")
170
+ ```
171
+
172
+ ## API Client
173
+
174
+ The SDK also provides direct API access:
175
+
176
+ ```python
177
+ from swiftapi import SwiftAPI
178
+
179
+ api = SwiftAPI(key="swiftapi_live_...")
180
+
181
+ # Get API info
182
+ info = api.get_info()
183
+
184
+ # Verify an action
185
+ result = api.verify(
186
+ action_type="file_write",
187
+ intent="Save user data",
188
+ params={"path": "/data/users.json"}
189
+ )
190
+
191
+ # Check attestation revocation
192
+ is_revoked = api.check_revocation(jti="attestation-id")
193
+
194
+ # List authority keys (admin only)
195
+ keys = api.list_keys()
196
+
197
+ # Create new key (admin only)
198
+ new_key = api.create_key(name="agent-1", scopes=["verify"])
199
+ ```
200
+
201
+ ## Configuration
202
+
203
+ ```python
204
+ api = SwiftAPI(
205
+ key="swiftapi_live_...",
206
+ base_url="https://swiftapi.ai", # Default
207
+ timeout=30, # Request timeout in seconds
208
+ )
209
+
210
+ guard = Enforcement(
211
+ client=api,
212
+ paranoid=False, # Enable online revocation checks
213
+ verbose=True, # Print status messages
214
+ )
215
+ ```
216
+
217
+ ## The Golden Loop
218
+
219
+ Every protected action goes through this verification chain:
220
+
221
+ ```
222
+ ┌─────────────────────────────────────────────────────────┐
223
+ │ THE GOLDEN LOOP │
224
+ ├─────────────────────────────────────────────────────────┤
225
+ │ │
226
+ │ 1. API CALL │
227
+ │ client.verify() ──────────────────────────────┐ │
228
+ │ │ │
229
+ │ 2. CRYPTO CHECK (Offline Truth) │ │
230
+ │ verifier.verify_signature() ◄─────────────────┤ │
231
+ │ │ │
232
+ │ 3. ONLINE CHECK (Optional/Paranoid) │ │
233
+ │ client.check_revocation() ◄───────────────────┤ │
234
+ │ │ │
235
+ │ 4. EXECUTE │ │
236
+ │ func() ◄──────────────────────────────────────┘ │
237
+ │ │
238
+ │ If ANY step fails → PolicyViolation raised │
239
+ │ The action NEVER executes without full verification │
240
+ │ │
241
+ └─────────────────────────────────────────────────────────┘
242
+ ```
243
+
244
+ ## License
245
+
246
+ MIT License - See LICENSE file for details.
247
+
248
+ ## Links
249
+
250
+ - **API**: https://swiftapi.ai
251
+ - **Documentation**: https://docs.swiftapi.ai
252
+ - **GitHub**: https://github.com/swiftapi/swiftapi-python
253
+
254
+ ---
255
+
256
+ *Built by Rayan Pal. No AI action executes without verification.*
@@ -0,0 +1,10 @@
1
+ swiftapi/__init__.py,sha256=u4aQrcZqV7Hjdy1eOV_-G1OvYH7_iZ1Yr4qelDXPg2c,1938
2
+ swiftapi/client.py,sha256=i7O4GmlS_7HVxKwFvdh550pP1jcNUZXAyXkhTqviCi8,9373
3
+ swiftapi/enforcement.py,sha256=L-2JKvvL5U3Q4ZU_o889RxZteuN-ELuxKGIMUXtx2sw,8725
4
+ swiftapi/exceptions.py,sha256=hBleOCc1WwDEM70WNYTy1TDOSOaBJSx3PtzFzfmVOGY,1701
5
+ swiftapi/utils.py,sha256=HVDhQq7-5dDiuifzo58BH57S1VAvXYB4hIh41PiS20U,2765
6
+ swiftapi/verifier.py,sha256=KoZWx6SIgeCs2ke3ufQYN8or5-8Uo3DeKsn9AaXBuB0,4161
7
+ swiftapi_python-1.0.0.dist-info/METADATA,sha256=pWrSZBuOKXPpkyG1cbx6_WhNNmh59prvB0TYGE72TdE,7814
8
+ swiftapi_python-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
+ swiftapi_python-1.0.0.dist-info/top_level.txt,sha256=oio-d2BTrT91P7dmufUhlSYjOtWBh4bOYz6rsTDDvu4,9
10
+ swiftapi_python-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ swiftapi