swiftapi-python 1.3.1__py3-none-any.whl → 1.4.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 CHANGED
@@ -1,5 +1,5 @@
1
1
  """SwiftAPI: verified execution for explicitly mediated capabilities."""
2
- __version__ = "1.3.1"
2
+ __version__ = "1.4.0"
3
3
  __author__ = "Rayan Pal"
4
4
 
5
5
  # Core exports
swiftapi/client.py CHANGED
@@ -130,6 +130,9 @@ class SwiftAPI:
130
130
  message=reason,
131
131
  action_type=action_type,
132
132
  denial_reason=reason,
133
+ response=data,
134
+ raw_response=response.content,
135
+ status_code=response.status_code,
133
136
  )
134
137
 
135
138
  # Other errors
swiftapi/exceptions.py CHANGED
@@ -21,10 +21,12 @@ class AuthenticationError(SwiftAPIError):
21
21
  class PolicyViolation(SwiftAPIError):
22
22
  """Raised when an action violates policy and is denied."""
23
23
 
24
- def __init__(self, message: str, action_type: str = None, denial_reason: str = None):
24
+ def __init__(self, message: str, action_type: str = None, denial_reason: str = None,
25
+ response: dict = None, raw_response: bytes = None, status_code: int = None):
25
26
  self.action_type = action_type
26
27
  self.denial_reason = denial_reason
27
- super().__init__(message)
28
+ self.raw_response = raw_response
29
+ super().__init__(message, status_code=status_code, response=response)
28
30
 
29
31
 
30
32
  class SignatureVerificationError(SwiftAPIError):
swiftapi/native.py ADDED
@@ -0,0 +1,59 @@
1
+ """Native EOS client. Loopback only; no hosted-model or API-proxy claims."""
2
+ from pathlib import Path
3
+ import re
4
+ from urllib.parse import urlsplit
5
+ import uuid
6
+ import requests
7
+ from .exceptions import SwiftAPIError
8
+
9
+ class NativeEOSClient:
10
+ def __init__(self, key=None, *, key_file=None, base_url='http://127.0.0.1:8766', timeout=120):
11
+ if (key is None) == (key_file is None):
12
+ raise ValueError('Supply exactly one of key or key_file')
13
+ key = Path(key_file).read_text(encoding='ascii').strip() if key_file is not None else key
14
+ if not isinstance(key,str) or not re.fullmatch(r'swiftapi_live_[0-9a-f]{64}',key):
15
+ raise ValueError('Invalid authority key format')
16
+ u = urlsplit(base_url)
17
+ if (u.scheme != 'http' or u.hostname != '127.0.0.1' or not u.port
18
+ or u.username or u.password or u.path not in ('','/') or u.query or u.fragment):
19
+ raise ValueError('Native service URL must be http://127.0.0.1:PORT')
20
+ self.base_url, self.timeout = base_url.rstrip('/'), timeout
21
+ self.session = requests.Session()
22
+ self.session.trust_env = False
23
+ self.session.headers['X-SwiftAPI-Authority'] = key
24
+
25
+ def _post(self, path, body):
26
+ try:
27
+ response = self.session.post(self.base_url+path,json=body,timeout=self.timeout,allow_redirects=False)
28
+ except requests.RequestException:
29
+ raise SwiftAPIError('Native service unavailable') from None
30
+ try:
31
+ data = response.json()
32
+ except ValueError:
33
+ raise SwiftAPIError('Invalid native response',response.status_code) from None
34
+ if response.status_code != 200:
35
+ exc = SwiftAPIError('Native request rejected',response.status_code,data)
36
+ exc.raw_response = response.content
37
+ raise exc
38
+ return data
39
+
40
+ def check_authority(self):
41
+ return self._post('/v1/authority/check',{})
42
+
43
+ def authorize(self, question, *, request_id):
44
+ return self._post('/v1/native-eos/authorize',{'question':question,'request_id':request_id})
45
+
46
+ def generate_with_grant(self, question, grant, *, request_id):
47
+ return self._post('/v1/native-eos/generate',{'question':question,'request_id':request_id,'grant':grant})
48
+
49
+ def generate(self, question, *, request_id=None):
50
+ request_id = request_id or str(uuid.uuid4())
51
+ permission = self.authorize(question,request_id=request_id)
52
+ return self.generate_with_grant(question,permission['grant'],request_id=request_id)
53
+
54
+ def close(self):
55
+ self.session.close()
56
+ def __enter__(self):
57
+ return self
58
+ def __exit__(self,*_):
59
+ self.close()
@@ -0,0 +1,15 @@
1
+ """Append-only compatibility registry for released native classifiers.
2
+
3
+ Receipt verification uses the declared classifier hash, not the current source
4
+ file's hash. A future release may append a reviewed, compatible implementation;
5
+ it must retain this entry so existing sealed receipts remain verifiable.
6
+ The first entry is the SDK 1.4.0 release candidate's native_taxonomy.py bytes.
7
+ """
8
+
9
+ RELEASED_CLASSIFIER_HASHES = {
10
+ 'c17a7a37f0d935195b69c446da3a5ecfe36ab71539228dd9f798686d075de43c': {
11
+ 'first_sdk_version': '1.4.0',
12
+ 'receipt_schema': 'swiftapi-native-service-receipt/2',
13
+ 'rule': 'void-matrix/2dfe895bdafd35d23d6dab5f307ed560214a3843',
14
+ },
15
+ }
@@ -0,0 +1,75 @@
1
+ """July 29, 2026 Table 2, DOI 10.5281/zenodo.21696066, native adapter.
2
+
3
+ V0: Successful provider response; text container present; zero visible UTF-8
4
+ bytes; recognized normal stop.
5
+ V1: Successful provider response; zero visible UTF-8 bytes; recognized
6
+ output-budget stop.
7
+ V2: Successful provider response; text container absent; zero visible UTF-8
8
+ bytes; recognized normal stop.
9
+ VU: Successful provider response; zero visible UTF-8 bytes; termination state
10
+ not mapped by the frozen protocol.
11
+ Non-Void: Near-Voids (NV) contain at least one byte, including whitespace,
12
+ invisible Unicode, punctuation-only output, or ellipses. Visible responses (R)
13
+ contain nonempty visible text. Refusals (RF), safety blocks (SB), tool calls (TC)
14
+ come from distinct provider fields. Errors are operational outcomes, not Voids.
15
+
16
+ Normative classifier: theonlypal/void-matrix commit
17
+ 2dfe895bdafd35d23d6dab5f307ed560214a3843, void_matrix.FROZEN.py,
18
+ INVISIBLE_CODEPOINTS and ELLIPSIS_FORMS lines 213-235, near_void lines 1053-1061.
19
+ https://github.com/theonlypal/void-matrix/blob/2dfe895bdafd35d23d6dab5f307ed560214a3843/void_matrix.FROZEN.py#L1053
20
+ The frozen code governs the prose. A lone ? or ! is R, not NV. Native output
21
+ is always present. RF/SB/TC require a distinct event field, never inference
22
+ from words. Replay completion is labelled independently.
23
+ """
24
+ import hashlib
25
+ from pathlib import Path
26
+ import unicodedata
27
+
28
+ INVISIBLE = frozenset((0x200B,0x200C,0x200D,0x200E,0x200F,0xFEFF,0x2060,
29
+ 0x2061,0x2062,0x2063,0x2064,0x2066,0x2067,0x2068,0x2069,
30
+ 0x202A,0x202B,0x202C,0x202D,0x202E))
31
+ ELLIPSIS_FORMS = frozenset(('.', '..', '...', '....', '…', '….'))
32
+
33
+ def near_void(text):
34
+ if not text:
35
+ return False
36
+ if all(ord(character) in INVISIBLE or character.isspace() for character in text):
37
+ return True
38
+ return text.strip() in ELLIPSIS_FORMS
39
+
40
+ def classifier_hash():
41
+ return hashlib.sha256(Path(__file__).read_bytes()).hexdigest()
42
+
43
+ def classify(raw, *, successful, container_present=True, stop=None, event=None):
44
+ if type(raw) is not bytes or type(successful) is not bool or type(container_present) is not bool:
45
+ raise ValueError('Invalid classification evidence')
46
+ if not successful:
47
+ return 'operational'
48
+ if event is not None:
49
+ if event not in ('RF','SB','TC'):
50
+ raise ValueError('Unknown distinct provider event')
51
+ return event
52
+ text = raw.decode('utf-8', errors='strict')
53
+ if text:
54
+ return 'NV' if near_void(text) else 'R'
55
+ if stop == 'length':
56
+ return 'V1'
57
+ if stop == 'stop':
58
+ return 'V0' if container_present else 'V2'
59
+ return 'VU'
60
+
61
+ def classification_record(raw, status, result=None):
62
+ result = result or {}
63
+ complete = result.get('generation_complete') is True
64
+ finish = result.get('finish')
65
+ stop = 'stop' if complete and finish == 'NATIVE_EOS' else 'length' if finish == 'TOKEN_CAP' else None
66
+ fields = {'successful':status == 'COMPLETED', 'container_present':True,
67
+ 'stop':stop, 'output_bytes':len(raw),
68
+ 'native_eos_selected':151645 in result.get('generated_token_ids', []),
69
+ 'generation_complete':complete, 'finish':finish}
70
+ return {'class':classify(raw,successful=fields['successful'],stop=stop),
71
+ 'fields':fields,'classifier_sha256':classifier_hash(),
72
+ 'taxonomy':'Cross-Vendor Semantic Void Matrix, 2026-07-29',
73
+ 'source_doi':'10.5281/zenodo.21696066',
74
+ 'unicode_version':unicodedata.unidata_version,
75
+ 'response_scope':result.get('answer_scope','NO_COMPLETED_MODEL_RESPONSE')}
@@ -0,0 +1,292 @@
1
+ """Offline native receipt verification. No torch, weights, signing seed or network.
2
+
3
+ Signatures authenticate records, not the honesty of an unattested runtime or its
4
+ historical online revocation observation. Runtime trust is supplied out of band.
5
+ """
6
+ import argparse
7
+ import base64
8
+ from datetime import datetime
9
+ import hashlib
10
+ import json
11
+ import math
12
+ from pathlib import Path
13
+ import re
14
+ import struct
15
+ from nacl.signing import VerifyKey
16
+ from .native_taxonomy import classification_record
17
+ from .native_classifier_releases import RELEASED_CLASSIFIER_HASHES
18
+ from .verifier import SWIFTAPI_PUBLIC_KEY_B64
19
+
20
+ EOS, VOCAB = 151645, 151936
21
+ MODES = ('RECORDED_LOGITS_REPLAY','FRESH_MODEL_EXECUTION')
22
+ UNSIGNED = {'signature','signing_mode','action_data','denial_reason',
23
+ 'violated_policies','evaluated_policies','evaluation_context'}
24
+
25
+ def encoded(value):
26
+ return json.dumps(value,sort_keys=True,separators=(',',':'),ensure_ascii=True,allow_nan=False).encode()
27
+ def digest(raw):
28
+ return hashlib.sha256(raw).hexdigest()
29
+ def require(value, message):
30
+ if not value:
31
+ raise ValueError(message)
32
+ def read_json(path):
33
+ def unique(pairs):
34
+ d = {}
35
+ for k,v in pairs:
36
+ require(k not in d,'Duplicate JSON member')
37
+ d[k] = v
38
+ return d
39
+ def invalid(_):
40
+ raise ValueError('Nonfinite JSON')
41
+ return json.loads(Path(path).read_bytes(),object_pairs_hook=unique,parse_constant=invalid)
42
+ def signature(payload, sig, key):
43
+ VerifyKey(base64.b64decode(key,validate=True)).verify(encoded(payload),base64.b64decode(sig,validate=True))
44
+ def instant(value):
45
+ dt = datetime.fromisoformat(value.replace('Z','+00:00'))
46
+ require(dt.tzinfo is not None,'Naive authority timestamp')
47
+ return dt.timestamp()
48
+
49
+ def verify_revocation_entry(entry, jti, authority_key, *, decision_at=None):
50
+ fields={'cursor','jti','revoked_at','revoked_by','reason'}
51
+ require(isinstance(entry,dict) and set(entry)==fields|{'signature'},'Revocation entry shape')
52
+ require(entry['jti']==jti and type(entry['cursor']) is int and entry['cursor']>0,'Revocation JTI/cursor')
53
+ signature({k:entry[k] for k in fields},entry['signature'],authority_key)
54
+ revoked_at=instant(entry['revoked_at'])
55
+ if decision_at is not None:
56
+ require(revoked_at<=decision_at+5,'Revocation was not established at decision')
57
+ return True
58
+ def grant_check(grant, action, when, authority_key):
59
+ """Return the same ordered permission reason, independently of the service."""
60
+ try:
61
+ require(isinstance(grant,dict) and grant.get('signing_mode') == 'ed25519','Authority algorithm')
62
+ signature({k:v for k,v in grant.items() if k not in UNSIGNED},grant['signature'],authority_key)
63
+ except Exception:
64
+ return 'INVALID_AUTHORITY_SIGNATURE'
65
+ if grant.get('action_type') != action['scope'] or grant.get('action_fingerprint') != digest(encoded(action))[:32]:
66
+ return 'GRANT_BINDING_MISMATCH'
67
+ if 'action_data' in grant and grant['action_data'] != action:
68
+ return 'GRANT_ACTION_ECHO_MISMATCH'
69
+ if grant.get('approved') is not True:
70
+ return 'AUTHORITY_DENIED'
71
+ try:
72
+ issued, expires = instant(grant['issued_at']),instant(grant['expires_at'])
73
+ except Exception:
74
+ return 'INVALID_GRANT_TIME'
75
+ if issued > when+5:
76
+ return 'GRANT_FUTURE'
77
+ age_limit = action.get('profile', {}).get('max_grant_age_seconds', 60)
78
+ if type(age_limit) not in (int,float) or not 0 < age_limit <= 3600:
79
+ return 'INVALID_GRANT_AGE_PROFILE'
80
+ if when-issued > age_limit or expires <= when or expires <= issued:
81
+ return 'GRANT_EXPIRED'
82
+ if not isinstance(grant.get('jti'),str) or not re.fullmatch(r'[a-zA-Z0-9_-]{1,128}',grant['jti']):
83
+ return 'INVALID_GRANT_ID'
84
+ return None
85
+
86
+ def vector(raw):
87
+ require(len(raw) == 2*VOCAB,'Vector byte length')
88
+ vals = [struct.unpack('<f',b'\0\0'+raw[i:i+2])[0] for i in range(0,len(raw),2)]
89
+ require(all(math.isfinite(x) for x in vals),'Nonfinite vector')
90
+ return vals
91
+
92
+ def verify_receipt(path, trusted_public_key, expected_mode, *, expected_max_grant_age_seconds,
93
+ authority_key=SWIFTAPI_PUBLIC_KEY_B64,
94
+ expected_source_sha256=None):
95
+ require(expected_mode in MODES,'Explicit expected mode required')
96
+ require(type(expected_max_grant_age_seconds) in (int,float) and
97
+ 0 < expected_max_grant_age_seconds <= 3600,'Expected registered grant age required')
98
+ path = Path(path)
99
+ e = read_json(path)
100
+ require(set(e) == {'payload','signature','signer','algorithm'},'Envelope shape')
101
+ require(e['signer'] == 'LOCAL_RUNTIME' and e['algorithm'] == 'ed25519','Runtime signer')
102
+ r = e['payload']
103
+ signature(r,e['signature'],trusted_public_key)
104
+ require(r['schema'] == 'swiftapi-native-service-receipt/2','Receipt schema')
105
+ require(r['mode'] == expected_mode,'Receipt mode mismatch')
106
+ require(r['runtime_instance'] == digest(base64.b64decode(trusted_public_key)),'Runtime identity')
107
+ require(r['hardware_attested'] is False,'Hardware attestation unsupported')
108
+ require(r['authority_public_key'] == authority_key,'Authority key substituted')
109
+ require(r['source']['sha256'] == digest(encoded(r['source']['files'])),'Service source manifest')
110
+ if expected_source_sha256 is not None:
111
+ require(r['source']['sha256'] == expected_source_sha256,'Unexpected service source')
112
+ require(isinstance(r['sdk']['version'],str) and r['sdk']['version'],'Missing declared SDK version')
113
+ require(r['started_at'] <= r['finished_at'],'Receipt timeline')
114
+ assets = {}
115
+ for name, spec in r['assets'].items():
116
+ require(re.fullmatch(r'[a-zA-Z0-9_.-]+',name) and name not in ('.','..','receipt.json'),'Asset path')
117
+ p = path.parent/name
118
+ require(not p.is_symlink() and p.stat().st_size <= 4000000,'Unsafe asset')
119
+ data = p.read_bytes()
120
+ require(len(data) == spec['bytes'] and digest(data) == spec['sha256'],'Asset hash: '+name)
121
+ assets[name] = data
122
+ require('output.bin' in assets,'Missing output')
123
+ output = assets['output.bin']
124
+ require(r['output'].encode('utf-8') == output,'Raw output binding')
125
+ computed = classification_record(output,r['status'],r.get('result'))
126
+ declared_classifier = r['taxonomy']['classifier_sha256']
127
+ require(isinstance(declared_classifier,str) and declared_classifier in RELEASED_CLASSIFIER_HASHES,
128
+ 'Unreleased classifier hash')
129
+ classifier_release = RELEASED_CLASSIFIER_HASHES[declared_classifier]
130
+ require(classifier_release['receipt_schema'] == r['schema'],'Classifier receipt schema')
131
+ require(r['sdk']['classifier_sha256'] == declared_classifier,'Declared SDK classifier binding')
132
+ # Source comments and Unicode database metadata are not classification rules.
133
+ compared = set(computed) - {'unicode_version','classifier_sha256'}
134
+ require(set(r['taxonomy']) == set(computed) and all(
135
+ computed[k] == r['taxonomy'][k] for k in compared),'Taxonomy class/fields mismatch')
136
+ summary = {'status':'PASS','mode':expected_mode,'execution_id':r['execution_id'],
137
+ 'class':computed['class'],'response_scope':computed['response_scope'],
138
+ 'runtime_signature':'PASS','authority_signature':'NOT_APPLICABLE',
139
+ 'asset_hashes':'PASS','physical_execution_attested':False,
140
+ 'full_model_forward_pass_reproduced':False,
141
+ 'sdk_version_declared':r['sdk']['version'],
142
+ 'classifier_sha256_declared':declared_classifier,
143
+ 'classifier_first_sdk_version':classifier_release['first_sdk_version'],
144
+ 'expected_max_grant_age_seconds':expected_max_grant_age_seconds,
145
+ 'authority_public_key':authority_key,
146
+ 'authority_evidence_kind':r['authority_evidence_kind'],
147
+ 'production_authority':authority_key == SWIFTAPI_PUBLIC_KEY_B64,
148
+ 'numerical_boundary_replay':'NOT_PERFORMED_LIGHT_VERIFIER',
149
+ 'archived_score_source':'NOT_CHECKED_LIGHT_VERIFIER',
150
+ 'revocation_scope':'Historical runtime report, not independently authority-signed'}
151
+ action = r.get('action')
152
+ if action is not None:
153
+ age_limit = action['profile']['max_grant_age_seconds']
154
+ require(type(age_limit) in (int,float) and 0 < age_limit <= 3600,'Signed grant age limit')
155
+ require(age_limit == expected_max_grant_age_seconds,'Registered grant age mismatch')
156
+ if r['authentication'] is not None:
157
+ auth = r['authentication']
158
+ require(grant_check(auth['grant'],auth['action'],auth['observed_at'],authority_key) is None,'Known-key authentication')
159
+ require(auth['action']['scope'] == 'swiftapi.native_service.connection_check','Authentication scope')
160
+ require(auth['action']['instance_id'] == r['runtime_instance'],'Authentication instance')
161
+ require(assets.get('authentication.json') == encoded(auth),'Authentication asset')
162
+ summary['authority_signature'] = 'PASS_CONNECTION_CHECK'
163
+ if r['status'] == 'OPERATIONAL':
164
+ require(computed['class'] == 'operational' and not output and r.get('result') is None,'Operational misrepresented')
165
+ summary['execution_outcome'] = 'OPERATIONAL_NOT_A_VOID'
166
+ return summary
167
+ require(r['status'] == 'COMPLETED' and r['authentication'] is not None,'Completed without known key')
168
+ required = {'request.json','prompt.utf8.bin','input-token-ids.json','grant.json','output.bin','authentication.json'}
169
+ require(required <= set(assets),'Required assets missing')
170
+ action, grant, decision, result = r['action'],r['grant'],r['authority_decision'],r['result']
171
+ require(r['action_sha256'] == digest(encoded(action)),'Action hash')
172
+ require(action['scope'] == 'pccg2.native_decode','Native scope')
173
+ require(action['instance_id'] == r['runtime_instance'],'Action instance')
174
+ require(action['principal_sha256'] == r['authentication']['action']['principal_sha256'],'Principal binding')
175
+ require(action['service_source_sha256'] == r['source']['sha256'],'Action source binding')
176
+ require(action['profile']['mode'] == expected_mode,'Action profile mode')
177
+ req = json.loads(assets['request.json'])
178
+ require({'request_id','question'} <= set(req) <= {'request_id','question','grant'},'Raw request shape')
179
+ require({k:req[k] for k in ('request_id','question')} == action['request'],'Raw request binding')
180
+ require(req.get('grant') == grant and assets['grant.json'] == encoded(grant),'Raw grant binding')
181
+ require(digest(assets['prompt.utf8.bin']) == action['rendered_prompt_sha256'],'Prompt binding')
182
+ require(digest(assets['input-token-ids.json']) == action['input_ids_sha256'],'Input IDs binding')
183
+ require(result['input_ids'] == json.loads(assets['input-token-ids.json']),'Input IDs echo')
184
+ require(result['rendered_prompt_sha256'] == action['rendered_prompt_sha256'] and result['input_ids_sha256'] == action['input_ids_sha256'],'Backend input hashes')
185
+ when = decision['evaluated_at']
186
+ times = r['times']
187
+ require(r['started_at'] == times['request_arrival_at'] <= times['scores_ready_at'] <= when <= r['finished_at'],'Boundary timeline')
188
+ require(times['decision_at'] == when,'Decision timestamp')
189
+ require(times['grant_issued_at'] == (grant.get('issued_at') if isinstance(grant,dict) else None),'Grant timestamp binding')
190
+ current = r['boundary_authentication']
191
+ if current is not None:
192
+ require(grant_check(current['grant'],current['action'],current['observed_at'],authority_key) is None,'Boundary key authentication')
193
+ require(current['action']['scope'] == 'swiftapi.native_service.connection_check' and current['action']['instance_id'] == r['runtime_instance'] and current['action']['principal_sha256'] == action['principal_sha256'],'Boundary authentication binding')
194
+ else:
195
+ require(decision['reason'] == 'KEY_REJECTED_AT_BOUNDARY','Missing boundary authentication')
196
+ check = 'MISSING_PERMISSION' if grant is None else grant_check(grant,action,when,authority_key)
197
+ permission = decision['permission']
198
+ if permission == 'PERMISSION_VERIFIED':
199
+ require(check is None and current is not None and decision['reason'] == 'VERIFIED_EXACT_REQUEST','Unverified permission')
200
+ else:
201
+ require(permission == 'PERMISSION_NOT_VERIFIED','Permission enum')
202
+ reason = decision['reason']
203
+ if reason not in {'REPLAYED_REQUEST','REPLAYED_BOUNDARY_GRANT','AUTHORITY_REVOKED','KEY_REJECTED_AT_BOUNDARY'}:
204
+ require(reason == check or (reason == 'AUTHORITY_DENIED_UNSIGNED' and grant is None),'Denial reason mismatch')
205
+ elif reason == 'AUTHORITY_REVOKED':
206
+ require(check is None,'Revoked grant authenticity/binding')
207
+ signed_denial = check == 'AUTHORITY_DENIED'
208
+ require(decision['denial_authority_signed'] is signed_denial,'Signed denial label')
209
+ if signed_denial:
210
+ require(assets.get('authority-denial.json') == encoded(grant),'Signed denial asset')
211
+ signed_revocation=decision.get('revocation_authority_signed',False)
212
+ require(type(signed_revocation) is bool,'Signed revocation flag')
213
+ if signed_revocation:
214
+ require(decision['reason']=='AUTHORITY_REVOKED' and 'authority-revocation.json' in assets,
215
+ 'Signed revocation asset missing or not a revoked decision')
216
+ entry=json.loads(assets['authority-revocation.json'])
217
+ verify_revocation_entry(entry,grant['jti'],authority_key,decision_at=when)
218
+ require(decision['revocation_observation']=='AUTHORITY_SIGNED_ENTRY','Revocation observation label')
219
+ summary['revocation_scope']='AUTHORITY_SIGNED_ENTRY'
220
+ else:
221
+ require('authority-revocation.json' not in assets,'Unlabelled signed revocation evidence')
222
+ require(decision.get('revocation_observation')!='AUTHORITY_SIGNED_ENTRY','Unsigned revocation labelled signed')
223
+ summary['revocation_authority_signed']=signed_revocation
224
+ condition = 'EQ(485487,485487)' if permission == 'PERMISSION_VERIFIED' else 'EQ(485487,485489)'
225
+ require(r['gate_condition'] == result['gate_condition'] == condition,'Derived condition')
226
+ require(result['permission'] == permission and result['gate_condition_truth'] is (permission == 'PERMISSION_VERIFIED'),'Backend permission')
227
+ require(result['schema'] == 'swiftapi-native-backend-result/2' and result['native_eos'] == EOS,'Backend identity')
228
+ require(r['model_executed'] is (expected_mode == MODES[1]),'Content execution mode')
229
+ require(r['execution_facts'] == result['execution_facts'],'Execution facts binding')
230
+ require(r['times']['backend_timing'] == result['timing'],'Backend timing binding')
231
+ require(result['execution_facts']['model_executed'] is r['model_executed'],'Content execution facts')
232
+ tokens = result['generated_token_ids']
233
+ require(tokens and all(type(t) is int and 0 <= t < VOCAB for t in tokens),'Token list')
234
+ cap = 1 if expected_mode == MODES[0] else action['profile']['max_new_tokens']
235
+ require(type(cap) is int and 1 <= cap <= 16,'Configured token budget')
236
+ require(result['max_new_tokens'] == cap and len(tokens) <= cap and EOS not in tokens[:-1],'Token budget/termination')
237
+ complete = tokens[-1] == EOS
238
+ require(result['generation_complete'] is complete,'Completion flag')
239
+ finish = 'NATIVE_EOS' if complete else 'REPLAY_ONE_TOKEN_ONLY' if expected_mode == MODES[0] else 'TOKEN_CAP'
240
+ scope = 'COMPLETE_TO_NATIVE_EOS' if complete else 'FIRST_TOKEN_ONLY_NOT_A_COMPLETE_ANSWER' if expected_mode == MODES[0] else 'TRUNCATED_AT_TOKEN_CAP'
241
+ require(result['finish'] == finish and result['answer_scope'] == scope,'Completion scope')
242
+ require(complete or len(tokens) == cap,'Incomplete trace')
243
+ if permission == 'PERMISSION_NOT_VERIFIED':
244
+ require(tokens == [EOS] and output == b'' and computed['class'] == 'V0','Denied permission did not produce V0')
245
+ require(base64.b64decode(result['output_utf8_base64'],validate=True) == output and result['output_bytes'] == len(output) and result['output_sha256'] == digest(output),'Output evidence')
246
+ require(result['first_token_is_native_eos'] is (tokens[0] == EOS),'First token flag')
247
+ require(len(result['steps']) == len(tokens),'Step count')
248
+ require(result['execution_facts']['forward_passes_completed'] == (len(tokens) if expected_mode == MODES[1] else 0),'Forward-pass count')
249
+ names = {f'{i}-{kind}.bf16.bin' for i in range(len(tokens)) for kind in ('stock','selected')}
250
+ require(set(r['vector_files']) == names,'Vector set')
251
+ for i,step in enumerate(result['steps']):
252
+ raw, vals = [],[]
253
+ for kind in ('stock','selected'):
254
+ name = f'{i}-{kind}.bf16.bin'
255
+ p = path.parent/name
256
+ require(not p.is_symlink() and p.stat().st_size == 2*VOCAB,'Vector path/size')
257
+ data = p.read_bytes()
258
+ require(digest(data) == r['vector_files'][name],'Vector hash')
259
+ raw.append(data); vals.append(vector(data))
260
+ before, after = (data[:2*EOS]+data[2*EOS+2:] for data in raw)
261
+ require(before == after,'Non-EOS bytes changed')
262
+ require(step['index'] == i and step['selected_token'] == tokens[i] == max(range(VOCAB),key=vals[1].__getitem__),'Greedy selection')
263
+ require(step['stock_argmax_token'] == max(range(VOCAB),key=vals[0].__getitem__) and step['selected_logit'] == vals[1][tokens[i]],'Stock argmax/selected score')
264
+ require(step['stock_eos_logit'] == vals[0][EOS] and step['intervened_eos_logit'] == vals[1][EOS],'EOS score binding')
265
+ require(step['stock_vector_sha256'] == digest(raw[0]) and step['selected_vector_sha256'] == digest(raw[1]) and step['non_eos_sha256'] == digest(after),'Score hashes')
266
+ require(step['score_source'] == ('AUTHENTIC_BANK_FIRST_LOGITS' if expected_mode == MODES[0] else 'FRESH_FORWARD_PASS'),'Vector provenance label')
267
+ if i:
268
+ require(raw[0] == raw[1] and step['intervention'] == 'NONE','Later token intervention')
269
+ require(r['stop_cause'] == result['stop_cause'] and r['stop_cause'] in ('NONE','LEARNED_GATE','VETO'),'Stop cause')
270
+ summary.update(tokens=tokens,output_bytes=len(output),non_eos_unchanged=True,
271
+ authority_signature='CHECKED_PRESENTED_GRANT_AND_CONNECTIONS',
272
+ permission=permission,reason=decision['reason'])
273
+ return summary
274
+
275
+ def main():
276
+ p=argparse.ArgumentParser(description=__doc__)
277
+ p.add_argument('receipt',type=Path)
278
+ p.add_argument('--trusted-key',required=True,type=Path)
279
+ p.add_argument('--expected-mode',required=True,choices=MODES)
280
+ p.add_argument('--expected-source-sha256')
281
+ p.add_argument('--expected-max-grant-age-seconds',required=True,type=float,
282
+ help='Registered value supplied independently, never read from the receipt')
283
+ a=p.parse_args()
284
+ try:
285
+ result=verify_receipt(a.receipt,read_json(a.trusted_key)['public_key'],a.expected_mode,
286
+ expected_max_grant_age_seconds=a.expected_max_grant_age_seconds,
287
+ expected_source_sha256=a.expected_source_sha256)
288
+ except Exception as exc:
289
+ print(json.dumps({'status':'FAIL','reason':str(exc)})); raise SystemExit(2)
290
+ print(json.dumps(result,sort_keys=True))
291
+ if __name__ == '__main__':
292
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swiftapi-python
3
- Version: 1.3.1
3
+ Version: 1.4.0
4
4
  Summary: SwiftAPI Python SDK - AI Action Verification Gateway
5
5
  Author-email: Rayan Pal <rayan@swiftapi.ai>
6
6
  License-Expression: MIT
@@ -1,10 +1,14 @@
1
- swiftapi/__init__.py,sha256=S-QNoxf6SGO_VhrCogiTzm-YtmqfBwl4BnhgdKNajOM,1867
1
+ swiftapi/__init__.py,sha256=_JZGlhb75VbZRGZdkkSnxkFdNFevGNCqK5WMQSoplLs,1867
2
2
  swiftapi/__main__.py,sha256=ee5vE0xcUZM3fPmxicvcw-IJXkm6nOwxARREHBWpF8Q,47
3
3
  swiftapi/anthropic.py,sha256=f8lB4XRI4VmCqOLiQaDX5aFNZNzPiR6jheuv40f0umk,8209
4
4
  swiftapi/cli.py,sha256=hP9wcYQGQOpwe8LNjdiNQ2GETInRzHumPe5xGhQnCpo,5860
5
- swiftapi/client.py,sha256=jvAGLGeD-8evyWEWsRGKJZgb_PtQdNgztJR4OQgD48c,11094
5
+ swiftapi/client.py,sha256=Mx4cMn9FdTxPTdifwP1WRXC3KBP-Eh81O2RSuyK9zlw,11222
6
6
  swiftapi/enforcement.py,sha256=EzhR9TVbXe-NqAXxrIfJpg1hSK0gmB9rK--rt2AuL_s,8659
7
- swiftapi/exceptions.py,sha256=hBleOCc1WwDEM70WNYTy1TDOSOaBJSx3PtzFzfmVOGY,1701
7
+ swiftapi/exceptions.py,sha256=BUeOKQ9w8MgEBco6RUezSfgvrFvFX2iwdq03fqs3NYw,1877
8
+ swiftapi/native.py,sha256=gYnDHAk8vIVpcVGVG-kM7XlPdPNzYNXOlMcOuFOkMI8,2677
9
+ swiftapi/native_classifier_releases.py,sha256=Qrh3RD5RNy7VGre4KshvRMaEcXA-15SEYwCG4sfMcHc,675
10
+ swiftapi/native_taxonomy.py,sha256=wXp6N_DZNRlbacRG2jpez-NqtxU5Io3Z95hobQdd5Dw,3601
11
+ swiftapi/native_verify.py,sha256=TSDFEE43Y0Y-g5thBvxcnaT5JhosO01q8TmxYrHhddk,19257
8
12
  swiftapi/openai.py,sha256=3eI1T-hF20Apymp6Vkh9jskALmFmQCtzptH4jh9B544,8432
9
13
  swiftapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
14
  swiftapi/utils.py,sha256=Sn6ED6ltECCYc-ce9fhxZxgzBthvflPCZelXPPzskZM,3145
@@ -18,9 +22,9 @@ swiftapi/brake/mcp.py,sha256=Gt66HxySwDEEr3dDUO6qMOAZkF0dFs44jwa26uuzmU8,5695
18
22
  swiftapi/brake/observations.py,sha256=RBkkmzBEzJwq-JG9yYZR9VtsMNocRoOgxCw6iHohG_E,1963
19
23
  swiftapi/brake/qualification.py,sha256=-2KsiRRSOGsG5xJaigJoqBE9vRTykqv1QqQkKzTUmZ0,4031
20
24
  swiftapi/brake/remote.py,sha256=qPj1t0b7uQsQWdTz_3NZz1roxjXH6Mu9ORxVK8amm-4,3150
21
- swiftapi_python-1.3.1.dist-info/licenses/LICENSE,sha256=btq3h42ZP3swvtQ3ak5k3sP_Y6yRwzL5zrV7EzNeRus,1087
22
- swiftapi_python-1.3.1.dist-info/METADATA,sha256=jdF8eppa9ZxhZ5XV_1wieLS7M6PFvPkIr5g4qpZNF5w,6209
23
- swiftapi_python-1.3.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
24
- swiftapi_python-1.3.1.dist-info/entry_points.txt,sha256=rIhaU6i_Q2sPz0b8a7Hpe7crjULOwJfxdL1-AIcKRAw,47
25
- swiftapi_python-1.3.1.dist-info/top_level.txt,sha256=oio-d2BTrT91P7dmufUhlSYjOtWBh4bOYz6rsTDDvu4,9
26
- swiftapi_python-1.3.1.dist-info/RECORD,,
25
+ swiftapi_python-1.4.0.dist-info/licenses/LICENSE,sha256=btq3h42ZP3swvtQ3ak5k3sP_Y6yRwzL5zrV7EzNeRus,1087
26
+ swiftapi_python-1.4.0.dist-info/METADATA,sha256=nyEJOWsV9C2naDJ6SzBCD_G_9Oj8kW0bo7CWcwbghIo,6209
27
+ swiftapi_python-1.4.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
28
+ swiftapi_python-1.4.0.dist-info/entry_points.txt,sha256=rIhaU6i_Q2sPz0b8a7Hpe7crjULOwJfxdL1-AIcKRAw,47
29
+ swiftapi_python-1.4.0.dist-info/top_level.txt,sha256=oio-d2BTrT91P7dmufUhlSYjOtWBh4bOYz6rsTDDvu4,9
30
+ swiftapi_python-1.4.0.dist-info/RECORD,,