shieldcortex 0.1.1__tar.gz → 0.3.0__tar.gz

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.
Files changed (46) hide show
  1. shieldcortex-0.3.0/CHANGELOG.md +57 -0
  2. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/PKG-INFO +46 -8
  3. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/README.md +44 -6
  4. shieldcortex-0.3.0/examples/audit_export.py +48 -0
  5. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/pyproject.toml +1 -1
  6. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/src/shieldcortex/__init__.py +120 -0
  7. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/src/shieldcortex/_http.py +68 -8
  8. shieldcortex-0.3.0/src/shieldcortex/_payloads.py +341 -0
  9. shieldcortex-0.3.0/src/shieldcortex/_version.py +1 -0
  10. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/src/shieldcortex/async_client.py +456 -5
  11. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/src/shieldcortex/client.py +446 -5
  12. shieldcortex-0.3.0/src/shieldcortex/types.py +1256 -0
  13. shieldcortex-0.3.0/tests/conftest.py +23 -0
  14. shieldcortex-0.3.0/tests/endpoint_manifest.py +133 -0
  15. shieldcortex-0.3.0/tests/test_audit_exports.py +571 -0
  16. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/tests/test_client.py +109 -2
  17. shieldcortex-0.3.0/tests/test_incidents_recall.py +257 -0
  18. shieldcortex-0.3.0/tests/test_license.py +146 -0
  19. shieldcortex-0.3.0/tests/test_parity.py +100 -0
  20. shieldcortex-0.3.0/tests/test_skills_threats.py +243 -0
  21. shieldcortex-0.3.0/tests/test_sync.py +551 -0
  22. shieldcortex-0.3.0/tests/test_verify.py +446 -0
  23. shieldcortex-0.1.1/examples/audit_export.py +0 -29
  24. shieldcortex-0.1.1/src/shieldcortex/_version.py +0 -1
  25. shieldcortex-0.1.1/src/shieldcortex/types.py +0 -512
  26. shieldcortex-0.1.1/tests/conftest.py +0 -12
  27. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/.github/workflows/publish.yml +0 -0
  28. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/.github/workflows/test.yml +0 -0
  29. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/.gitignore +0 -0
  30. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/LICENSE +0 -0
  31. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/examples/basic_scan.py +0 -0
  32. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/examples/batch_scanning.py +0 -0
  33. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/examples/crewai_memory.py +0 -0
  34. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/examples/langchain_callback.py +0 -0
  35. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/src/shieldcortex/errors.py +0 -0
  36. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/src/shieldcortex/integrations/__init__.py +0 -0
  37. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/src/shieldcortex/integrations/crewai.py +0 -0
  38. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/src/shieldcortex/integrations/langchain.py +0 -0
  39. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/src/shieldcortex/pagination.py +0 -0
  40. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/tests/__init__.py +0 -0
  41. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/tests/fixtures.py +0 -0
  42. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/tests/integrations/__init__.py +0 -0
  43. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/tests/integrations/test_crewai.py +0 -0
  44. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/tests/test_errors.py +0 -0
  45. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/tests/test_pagination.py +0 -0
  46. {shieldcortex-0.1.1 → shieldcortex-0.3.0}/tests/test_types.py +0 -0
@@ -0,0 +1,57 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0 — 2026-08-11
4
+
5
+ SDK lockstep release: the Python SDK now covers the full customer surface
6
+ documented in the SDK lockstep contract, in lockstep with the TypeScript SDK.
7
+
8
+ ### Added
9
+
10
+ - **Verification** — `submit_verification()`, `list_verifications()`,
11
+ `get_verification()`, `get_verification_stats()`, `delete_verification()`
12
+ - **Skills** — `ingest_skill_scans()`, `list_skill_scans()`
13
+ - **Threats** — `report_threat()` (OpenClaw realtime compat shim)
14
+ - **Incidents** — `replay_incidents()`
15
+ - **Recall** — `explain_recall()`
16
+ - **Sync** — `get_sync_health()`, `push_memories()`, `list_synced_memories()`,
17
+ `push_memory_graph()`. Note: `list_synced_memories()` omits the
18
+ `include_deleted` param unless truthy — the server Boolean-coerces any
19
+ present value (even `"false"`) as enabling it. Every other boolean query
20
+ param was audited for the same coercion trap; none affected.
21
+ - **Licence** — `get_license()`, `regenerate_license()`
22
+ - **Audit** — `ingest_audit_events()`, `get_iron_dome_stats()`,
23
+ `get_iron_dome_events()`, and the export-manifest surface
24
+ (`list_audit_exports()`, `get_audit_export_manifest()`,
25
+ `verify_audit_export()`, `list_audit_export_verifications()`)
26
+ - Cross-SDK endpoint parity manifest and drift guard
27
+ (`tests/endpoint_manifest.py`, kept in lockstep with the TS SDK's
28
+ `tests/endpoint-manifest.ts`), including a sync/async surface parity test
29
+ - Shared request builders (`_payloads.py`) so the sync and async clients
30
+ construct identical wire payloads and cannot drift
31
+
32
+ ### Changed
33
+
34
+ - **BREAKING:** `export_audit_logs()` (both clients) now returns
35
+ `AuditExportResult` — the raw file body in `.content` plus the parsed
36
+ `X-ShieldCortex-Export-*` integrity headers in `.headers` — instead of a
37
+ bare `str`. Previously the integrity headers were discarded, so exports
38
+ structurally could not be verified. An absent `sha256`/`signature` header
39
+ is `None` (never `""`): the export is unverifiable. Migration:
40
+ `client.export_audit_logs(...)` → `client.export_audit_logs(...).content`.
41
+
42
+ ### Deprecated
43
+
44
+ - `create_checkout_session()` / `create_portal_session()` now emit
45
+ `DeprecationWarning`: self-serve plans were retired 2026-07 (Free +
46
+ Enterprise model); the endpoints remain for grandfathered licence holders.
47
+
48
+ ## 0.1.1 — 2026-03-12
49
+
50
+ - Added Iron Dome patterns/policies client support; fixed the default-policy
51
+ verb (PUT).
52
+
53
+ ## 0.1.0 — 2026-02-15
54
+
55
+ - Initial release: scanning, audit, quarantine, keys, teams, invites,
56
+ billing, devices, alerts, webhooks, firewall rules; sync + async clients;
57
+ CrewAI and LangChain integrations; Python 3.9+ compatibility fixes.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: shieldcortex
3
- Version: 0.1.1
3
+ Version: 0.3.0
4
4
  Summary: Official Python SDK for the ShieldCortex API — AI memory security scanning
5
5
  Project-URL: Homepage, https://shieldcortex.ai
6
6
  Project-URL: Documentation, https://shieldcortex.ai/docs
@@ -166,8 +166,10 @@ logs = client.get_audit_logs(AuditQuery(level="BLOCK", limit=10))
166
166
  for entry in client.iter_audit_logs():
167
167
  print(entry.id, entry.firewall_result)
168
168
 
169
- # Export as CSV
170
- csv = client.export_audit_logs(format="csv")
169
+ # Export as CSV — returns the raw body plus integrity headers
170
+ export = client.export_audit_logs(format="csv")
171
+ print(export.content)
172
+ print(export.headers.sha256) # None ⇒ export unverifiable
171
173
  ```
172
174
 
173
175
  ## Error Handling
@@ -185,24 +187,60 @@ except ValidationError:
185
187
  print("Invalid request")
186
188
  ```
187
189
 
188
- ## Full API Coverage
190
+ ## API Coverage
189
191
 
190
- The SDK covers all ShieldCortex API endpoints:
192
+ The SDK covers the full customer surface documented in the SDK lockstep contract:
191
193
 
192
194
  | Category | Methods |
193
195
  |----------|---------|
194
196
  | **Scanning** | `scan()`, `scan_batch()`, `scan_skill()` |
195
- | **Audit** | `get_audit_logs()`, `get_audit_entry()`, `get_audit_stats()`, `get_audit_trends()`, `export_audit_logs()`, `iter_audit_logs()` |
197
+ | **Audit** | `get_audit_logs()`, `get_audit_entry()`, `get_audit_stats()`, `get_audit_trends()`, `export_audit_logs()`, `iter_audit_logs()`, `ingest_audit_events()`, `get_iron_dome_stats()`, `get_iron_dome_events()` |
198
+ | **Audit Export Manifests** | `list_audit_exports()`, `get_audit_export_manifest()`, `verify_audit_export()`, `list_audit_export_verifications()` |
196
199
  | **Quarantine** | `get_quarantine()`, `get_quarantine_item()`, `review_quarantine_item()` |
197
200
  | **API Keys** | `create_api_key()`, `list_api_keys()`, `revoke_api_key()` |
198
201
  | **Teams** | `get_team()`, `update_team()`, `get_team_members()`, `get_usage()` |
199
202
  | **Invites** | `create_invite()`, `list_invites()`, `delete_invite()`, `resend_invite()` |
200
- | **Billing** | `create_checkout_session()`, `create_portal_session()` |
203
+ | **Billing** (deprecated) | `create_checkout_session()`, `create_portal_session()` — self-serve plans retired 2026-07; retained for grandfathered licence holders |
201
204
  | **Devices** | `get_devices()`, `register_device()`, `update_device()`, `device_heartbeat()` |
202
205
  | **Alerts** | `get_alerts()`, `create_alert()`, `update_alert()`, `delete_alert()` |
203
206
  | **Webhooks** | `get_webhooks()`, `create_webhook()`, `update_webhook()`, `delete_webhook()`, `test_webhook()`, `get_webhook_deliveries()` |
204
207
  | **Firewall Rules** | `get_firewall_rules()`, `get_active_firewall_rules()`, `create_firewall_rule()`, `update_firewall_rule()`, `delete_firewall_rule()` |
205
208
  | **Iron Dome** | `get_injection_patterns()`, `get_injection_patterns_sync()`, `create_injection_pattern()`, `update_injection_pattern()`, `test_injection_pattern()`, `delete_injection_pattern()`, `get_iron_dome_policies()`, `get_iron_dome_policy_sync()`, `create_iron_dome_policy()`, `update_iron_dome_policy()`, `set_default_iron_dome_policy()`, `delete_iron_dome_policy()` |
209
+ | **Verification** | `submit_verification()`, `list_verifications()`, `get_verification()`, `get_verification_stats()`, `delete_verification()` |
210
+ | **Skills** | `ingest_skill_scans()`, `list_skill_scans()` |
211
+ | **Threats** | `report_threat()` |
212
+ | **Incidents** | `replay_incidents()` |
213
+ | **Recall** | `explain_recall()` |
214
+ | **Sync** | `get_sync_health()`, `push_memories()`, `list_synced_memories()`, `push_memory_graph()` |
215
+ | **Licence** | `get_license()`, `regenerate_license()` |
216
+
217
+ ### Deferred endpoints
218
+
219
+ The audit **verification-export download chain** (`GET .../verifications/export`,
220
+ `GET .../verification-exports`, `GET .../verification-exports/{id}` and its
221
+ `/download`) is deliberately deferred — it serves CLI tooling rather than SDK
222
+ consumers. See `tests/endpoint_manifest.py` for the canonical list.
223
+
224
+ ### Out of scope
225
+
226
+ - `/v1/platform/*` — platform-owner operations
227
+ - `/v1/auth/*` — magic-link login flows (browser/dashboard, not API-key auth)
228
+ - `/v1/customizer/*` — retired 2026-07
229
+
230
+ Additional dashboard/session-scoped `/v1` routes exist server-side; they sit
231
+ outside the SDK's API-key surface.
232
+
233
+ ### Cross-SDK policy notes
234
+
235
+ The Python and TypeScript SDKs cover the same endpoint manifest, with two
236
+ deliberate differences:
237
+
238
+ 1. **Deletes** — the TypeScript SDK returns `void` from every delete method;
239
+ the Python SDK returns the typed response body where the server sends one
240
+ (per-language internal consistency).
241
+ 2. **Audit export** — both SDKs return the raw file content together with the
242
+ parsed `X-ShieldCortex-Export-*` integrity headers. An absent `sha256` or
243
+ `signature` header means the export is unverifiable.
206
244
 
207
245
  ## Documentation
208
246
 
@@ -127,8 +127,10 @@ logs = client.get_audit_logs(AuditQuery(level="BLOCK", limit=10))
127
127
  for entry in client.iter_audit_logs():
128
128
  print(entry.id, entry.firewall_result)
129
129
 
130
- # Export as CSV
131
- csv = client.export_audit_logs(format="csv")
130
+ # Export as CSV — returns the raw body plus integrity headers
131
+ export = client.export_audit_logs(format="csv")
132
+ print(export.content)
133
+ print(export.headers.sha256) # None ⇒ export unverifiable
132
134
  ```
133
135
 
134
136
  ## Error Handling
@@ -146,24 +148,60 @@ except ValidationError:
146
148
  print("Invalid request")
147
149
  ```
148
150
 
149
- ## Full API Coverage
151
+ ## API Coverage
150
152
 
151
- The SDK covers all ShieldCortex API endpoints:
153
+ The SDK covers the full customer surface documented in the SDK lockstep contract:
152
154
 
153
155
  | Category | Methods |
154
156
  |----------|---------|
155
157
  | **Scanning** | `scan()`, `scan_batch()`, `scan_skill()` |
156
- | **Audit** | `get_audit_logs()`, `get_audit_entry()`, `get_audit_stats()`, `get_audit_trends()`, `export_audit_logs()`, `iter_audit_logs()` |
158
+ | **Audit** | `get_audit_logs()`, `get_audit_entry()`, `get_audit_stats()`, `get_audit_trends()`, `export_audit_logs()`, `iter_audit_logs()`, `ingest_audit_events()`, `get_iron_dome_stats()`, `get_iron_dome_events()` |
159
+ | **Audit Export Manifests** | `list_audit_exports()`, `get_audit_export_manifest()`, `verify_audit_export()`, `list_audit_export_verifications()` |
157
160
  | **Quarantine** | `get_quarantine()`, `get_quarantine_item()`, `review_quarantine_item()` |
158
161
  | **API Keys** | `create_api_key()`, `list_api_keys()`, `revoke_api_key()` |
159
162
  | **Teams** | `get_team()`, `update_team()`, `get_team_members()`, `get_usage()` |
160
163
  | **Invites** | `create_invite()`, `list_invites()`, `delete_invite()`, `resend_invite()` |
161
- | **Billing** | `create_checkout_session()`, `create_portal_session()` |
164
+ | **Billing** (deprecated) | `create_checkout_session()`, `create_portal_session()` — self-serve plans retired 2026-07; retained for grandfathered licence holders |
162
165
  | **Devices** | `get_devices()`, `register_device()`, `update_device()`, `device_heartbeat()` |
163
166
  | **Alerts** | `get_alerts()`, `create_alert()`, `update_alert()`, `delete_alert()` |
164
167
  | **Webhooks** | `get_webhooks()`, `create_webhook()`, `update_webhook()`, `delete_webhook()`, `test_webhook()`, `get_webhook_deliveries()` |
165
168
  | **Firewall Rules** | `get_firewall_rules()`, `get_active_firewall_rules()`, `create_firewall_rule()`, `update_firewall_rule()`, `delete_firewall_rule()` |
166
169
  | **Iron Dome** | `get_injection_patterns()`, `get_injection_patterns_sync()`, `create_injection_pattern()`, `update_injection_pattern()`, `test_injection_pattern()`, `delete_injection_pattern()`, `get_iron_dome_policies()`, `get_iron_dome_policy_sync()`, `create_iron_dome_policy()`, `update_iron_dome_policy()`, `set_default_iron_dome_policy()`, `delete_iron_dome_policy()` |
170
+ | **Verification** | `submit_verification()`, `list_verifications()`, `get_verification()`, `get_verification_stats()`, `delete_verification()` |
171
+ | **Skills** | `ingest_skill_scans()`, `list_skill_scans()` |
172
+ | **Threats** | `report_threat()` |
173
+ | **Incidents** | `replay_incidents()` |
174
+ | **Recall** | `explain_recall()` |
175
+ | **Sync** | `get_sync_health()`, `push_memories()`, `list_synced_memories()`, `push_memory_graph()` |
176
+ | **Licence** | `get_license()`, `regenerate_license()` |
177
+
178
+ ### Deferred endpoints
179
+
180
+ The audit **verification-export download chain** (`GET .../verifications/export`,
181
+ `GET .../verification-exports`, `GET .../verification-exports/{id}` and its
182
+ `/download`) is deliberately deferred — it serves CLI tooling rather than SDK
183
+ consumers. See `tests/endpoint_manifest.py` for the canonical list.
184
+
185
+ ### Out of scope
186
+
187
+ - `/v1/platform/*` — platform-owner operations
188
+ - `/v1/auth/*` — magic-link login flows (browser/dashboard, not API-key auth)
189
+ - `/v1/customizer/*` — retired 2026-07
190
+
191
+ Additional dashboard/session-scoped `/v1` routes exist server-side; they sit
192
+ outside the SDK's API-key surface.
193
+
194
+ ### Cross-SDK policy notes
195
+
196
+ The Python and TypeScript SDKs cover the same endpoint manifest, with two
197
+ deliberate differences:
198
+
199
+ 1. **Deletes** — the TypeScript SDK returns `void` from every delete method;
200
+ the Python SDK returns the typed response body where the server sends one
201
+ (per-language internal consistency).
202
+ 2. **Audit export** — both SDKs return the raw file content together with the
203
+ parsed `X-ShieldCortex-Export-*` integrity headers. An absent `sha256` or
204
+ `signature` header means the export is unverifiable.
167
205
 
168
206
  ## Documentation
169
207
 
@@ -0,0 +1,48 @@
1
+ """Audit log querying and export example."""
2
+
3
+ from shieldcortex import ShieldCortex, AuditQuery
4
+
5
+ client = ShieldCortex(api_key="sc_live_YOUR_KEY_HERE")
6
+
7
+ # Get summary stats
8
+ stats = client.get_audit_stats(time_range="7d")
9
+ print(f"Total operations: {stats.total_operations}")
10
+ print(f"Allowed: {stats.allowed_count}")
11
+ print(f"Blocked: {stats.blocked_count}")
12
+ print(f"Quarantined: {stats.quarantined_count}")
13
+
14
+ # Query blocked entries
15
+ logs = client.get_audit_logs(AuditQuery(level="BLOCK", limit=5))
16
+ print(f"\nRecent blocks ({logs.total} total):")
17
+ for entry in logs.logs:
18
+ print(f" [{entry.timestamp}] {entry.reason} (trust: {entry.trust_score})")
19
+
20
+ # Auto-paginate through all entries
21
+ print("\nAll audit entries:")
22
+ for entry in client.iter_audit_logs(AuditQuery(level="BLOCK")):
23
+ print(f" #{entry.id}: {entry.firewall_result}")
24
+
25
+ # Export as CSV — returns the raw body plus X-ShieldCortex-Export-* integrity headers
26
+ export = client.export_audit_logs(format="csv")
27
+ with open("audit_export.csv", "w") as f:
28
+ f.write(export.content)
29
+ print("\nExported to audit_export.csv")
30
+
31
+ # Verify the export's integrity. sha256/signature/manifest_id are None when
32
+ # the server did not send them — the export is unverifiable, never "".
33
+ headers = export.headers
34
+ if headers.sha256 is None or headers.signature is None:
35
+ print("Export is UNVERIFIABLE — integrity headers absent")
36
+ else:
37
+ print(f"Entries: {headers.count}")
38
+ print(f"SHA-256: {headers.sha256}")
39
+ print(f"Signature ({headers.signature_algorithm}): {headers.signature}")
40
+ if headers.manifest_id is not None:
41
+ # Server-side check against the persisted, signed manifest
42
+ response = client.verify_audit_export(
43
+ headers.manifest_id, export_sha256=headers.sha256
44
+ )
45
+ result = response.verification
46
+ print(f"Manifest {headers.manifest_id}:")
47
+ print(f" signature valid: {result.signature_valid}")
48
+ print(f" sha256 matches: {result.sha256_matches}")
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "shieldcortex"
7
- version = "0.1.1"
7
+ version = "0.3.0"
8
8
  description = "Official Python SDK for the ShieldCortex API — AI memory security scanning"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -24,6 +24,10 @@ from shieldcortex.errors import (
24
24
  from shieldcortex.types import (
25
25
  AlertRule,
26
26
  AuditEntry,
27
+ AuditExportHeaders,
28
+ AuditExportResult,
29
+ AuditIngestEntry,
30
+ AuditIngestResponse,
27
31
  AuditQuery,
28
32
  AuditResponse,
29
33
  AuditStats,
@@ -32,19 +36,39 @@ from shieldcortex.types import (
32
36
  CheckoutResponse,
33
37
  CreateKeyResponse,
34
38
  CreateWebhookResponse,
39
+ DeleteVerificationResponse,
40
+ DetectedThreat,
35
41
  Device,
42
+ ExportManifest,
43
+ ExportManifestDetail,
44
+ ExportManifestListResponse,
45
+ ExportVerificationEvent,
46
+ ExportVerificationListResponse,
47
+ ExportVerifyExpected,
48
+ ExportVerifyResponse,
49
+ ExportVerifyResult,
36
50
  FirewallResult,
37
51
  FirewallRule,
38
52
  FragmentationResult,
53
+ IncidentCoverage,
54
+ IncidentEvent,
55
+ IncidentReplayResponse,
56
+ IncidentStreamSummary,
39
57
  InjectionPattern,
40
58
  InjectionPatternsResponse,
41
59
  Invite,
42
60
  InviteListResponse,
61
+ IronDomeEvent,
62
+ IronDomeEventsResponse,
43
63
  IronDomePoliciesResponse,
44
64
  IronDomePolicy,
65
+ IronDomeStats,
66
+ IronDomeTopDevice,
45
67
  KeyInfo,
46
68
  KeyListItem,
47
69
  KeyListResponse,
70
+ LicenseInfo,
71
+ ManifestVerification,
48
72
  MembersResponse,
49
73
  Pagination,
50
74
  PatternSyncItem,
@@ -55,24 +79,56 @@ from shieldcortex.types import (
55
79
  QuarantineItem,
56
80
  QuarantineQuery,
57
81
  QuarantineResponse,
82
+ RecallExplainResponse,
83
+ RecallLinkedEntity,
84
+ RecallMemory,
85
+ RecallResult,
86
+ RecallScoreBreakdown,
87
+ RegenerateLicenseResponse,
58
88
  ReviewResponse,
59
89
  ScanConfig,
60
90
  ScanResult,
61
91
  ScanSource,
62
92
  SensitivityResult,
93
+ SkillFinding,
94
+ SkillIngestResponse,
95
+ SkillScanFile,
96
+ SkillScanListResponse,
63
97
  SkillScanMetadata,
64
98
  SkillScanResult,
65
99
  SkillThreat,
66
100
  SourceCount,
101
+ SyncDeviceHealth,
102
+ SyncedMemoriesResponse,
103
+ SyncedMemory,
104
+ SyncedSkillScan,
105
+ SyncGraphEntity,
106
+ SyncGraphResponse,
107
+ SyncGraphTriple,
108
+ SyncHealth,
109
+ SyncHealthSummary,
110
+ SyncLatestDeviceSync,
111
+ SyncMemory,
112
+ SyncMemoryEntityLink,
113
+ SyncMemorySummary,
114
+ SyncPushResponse,
67
115
  TeamInfo,
68
116
  TeamMember,
69
117
  TestWebhookResponse,
118
+ ThreatReportResponse,
70
119
  TrendBucket,
71
120
  TrendResponse,
72
121
  TrustResult,
73
122
  UsageBreakdown,
74
123
  UsageInfo,
75
124
  UsageResponse,
125
+ VerificationDayStats,
126
+ VerificationDetail,
127
+ VerificationListItem,
128
+ VerificationListResponse,
129
+ VerificationQuota,
130
+ VerificationResult,
131
+ VerificationStats,
76
132
  Webhook,
77
133
  WebhookDelivery,
78
134
  )
@@ -111,6 +167,8 @@ __all__ = [
111
167
  "SourceCount",
112
168
  "TrendBucket",
113
169
  "TrendResponse",
170
+ "AuditExportHeaders",
171
+ "AuditExportResult",
114
172
  "Pagination",
115
173
  # Quarantine types
116
174
  "QuarantineQuery",
@@ -154,4 +212,66 @@ __all__ = [
154
212
  "IronDomePolicy",
155
213
  "IronDomePoliciesResponse",
156
214
  "PolicySyncResponse",
215
+ # Verification types
216
+ "DetectedThreat",
217
+ "VerificationResult",
218
+ "VerificationListItem",
219
+ "VerificationListResponse",
220
+ "VerificationDetail",
221
+ "VerificationDayStats",
222
+ "VerificationQuota",
223
+ "VerificationStats",
224
+ "DeleteVerificationResponse",
225
+ # Skill sync types
226
+ "SkillFinding",
227
+ "SkillScanFile",
228
+ "SkillIngestResponse",
229
+ "SyncedSkillScan",
230
+ "SkillScanListResponse",
231
+ # Threat types
232
+ "ThreatReportResponse",
233
+ # Incident types
234
+ "IncidentEvent",
235
+ "IncidentStreamSummary",
236
+ "IncidentCoverage",
237
+ "IncidentReplayResponse",
238
+ # Recall types
239
+ "RecallMemory",
240
+ "RecallLinkedEntity",
241
+ "RecallScoreBreakdown",
242
+ "RecallResult",
243
+ "RecallExplainResponse",
244
+ # Sync types
245
+ "SyncHealth",
246
+ "SyncMemory",
247
+ "SyncPushResponse",
248
+ "SyncedMemory",
249
+ "SyncLatestDeviceSync",
250
+ "SyncDeviceHealth",
251
+ "SyncHealthSummary",
252
+ "SyncMemorySummary",
253
+ "SyncedMemoriesResponse",
254
+ "SyncGraphEntity",
255
+ "SyncGraphTriple",
256
+ "SyncMemoryEntityLink",
257
+ "SyncGraphResponse",
258
+ # License types
259
+ "LicenseInfo",
260
+ "RegenerateLicenseResponse",
261
+ # Audit Iron Dome & export types
262
+ "IronDomeTopDevice",
263
+ "IronDomeStats",
264
+ "IronDomeEvent",
265
+ "IronDomeEventsResponse",
266
+ "AuditIngestEntry",
267
+ "AuditIngestResponse",
268
+ "ExportManifest",
269
+ "ExportManifestListResponse",
270
+ "ManifestVerification",
271
+ "ExportManifestDetail",
272
+ "ExportVerifyResult",
273
+ "ExportVerifyExpected",
274
+ "ExportVerifyResponse",
275
+ "ExportVerificationEvent",
276
+ "ExportVerificationListResponse",
157
277
  ]
@@ -8,6 +8,7 @@ from __future__ import annotations
8
8
 
9
9
  import dataclasses
10
10
  import re
11
+ from collections.abc import Callable
11
12
  from typing import Any, TypeVar, get_type_hints
12
13
 
13
14
  import httpx
@@ -21,6 +22,7 @@ from shieldcortex.errors import (
21
22
  ShieldCortexError,
22
23
  ValidationError,
23
24
  )
25
+ from shieldcortex.types import AuditExportHeaders
24
26
 
25
27
  T = TypeVar("T")
26
28
 
@@ -54,12 +56,12 @@ _QUERY_ALIASES: dict[str, str] = {
54
56
  # ── Serialisation ─────────────────────────────────────────────────────────────
55
57
 
56
58
 
57
- def serialize(obj: Any) -> Any:
58
- """Convert a dataclass (or primitive) to an API-compatible dict.
59
+ def _serialize(obj: Any, key_of: Callable[[dataclasses.Field[Any]], str]) -> Any:
60
+ """Shared serialisation traversal.
59
61
 
60
- - Dataclass fields are converted to camelCase
62
+ - Wire keys are derived per field by ``key_of``
61
63
  - None values are omitted
62
- - Nested dataclasses are recursed
64
+ - Nested dataclasses, lists and dicts are recursed
63
65
  """
64
66
  if dataclasses.is_dataclass(obj) and not isinstance(obj, type):
65
67
  result: dict[str, Any] = {}
@@ -67,16 +69,44 @@ def serialize(obj: Any) -> Any:
67
69
  value = getattr(obj, f.name)
68
70
  if value is None:
69
71
  continue
70
- api_key = _to_camel(f.name)
71
- result[api_key] = serialize(value)
72
+ result[key_of(f)] = _serialize(value, key_of)
72
73
  return result
73
74
  if isinstance(obj, list):
74
- return [serialize(item) for item in obj]
75
+ return [_serialize(item, key_of) for item in obj]
75
76
  if isinstance(obj, dict):
76
- return {k: serialize(v) for k, v in obj.items()}
77
+ return {k: _serialize(v, key_of) for k, v in obj.items()}
77
78
  return obj
78
79
 
79
80
 
81
+ def _camel_key(f: dataclasses.Field[Any]) -> str:
82
+ return _to_camel(f.name)
83
+
84
+
85
+ def _snake_wire_key(f: dataclasses.Field[Any]) -> str:
86
+ return str(f.metadata.get("wire", f.name))
87
+
88
+
89
+ def serialize(obj: Any) -> Any:
90
+ """Convert a dataclass (or primitive) to an API-compatible dict.
91
+
92
+ - Dataclass fields are converted to camelCase
93
+ - None values are omitted
94
+ - Nested dataclasses are recursed
95
+ """
96
+ return _serialize(obj, _camel_key)
97
+
98
+
99
+ def serialize_snake(obj: Any) -> Any:
100
+ """Convert a dataclass (or primitive) to a snake_case wire dict.
101
+
102
+ Unlike :func:`serialize`, field names are kept snake_case (the house wire
103
+ convention). Known camelCase warts (e.g. skills findings[].matchedText)
104
+ are declared per field via ``metadata={"wire": ...}`` in types.py.
105
+ None values are omitted; nested dataclasses are recursed.
106
+ """
107
+ return _serialize(obj, _snake_wire_key)
108
+
109
+
80
110
  def serialize_query(obj: Any) -> dict[str, str]:
81
111
  """Convert a dataclass to query parameter dict (string values, no Nones)."""
82
112
  if not dataclasses.is_dataclass(obj) or isinstance(obj, type):
@@ -223,6 +253,36 @@ def raise_for_status(response: httpx.Response) -> None:
223
253
  raise ShieldCortexError(f"API error: {status}", status, body)
224
254
 
225
255
 
256
+ # ── Audit export integrity headers ────────────────────────────────────────────
257
+
258
+
259
+ def parse_export_headers(headers: httpx.Headers) -> AuditExportHeaders:
260
+ """Parse the ``X-ShieldCortex-Export-*`` integrity headers.
261
+
262
+ Mirrors the TS SDK exactly: absent ``sha256``/``signature``/
263
+ ``manifest_id`` stay ``None`` (never ``""``) — the export is
264
+ unverifiable, and a ``""`` manifest_id would build a malformed
265
+ manifest URL; ``count`` is ``None`` when absent, empty, or
266
+ non-numeric; the remaining fields default to ``""`` / ``False``.
267
+ """
268
+ count_header = headers.get("X-ShieldCortex-Export-Count")
269
+ count: int | None = None
270
+ if count_header is not None:
271
+ try:
272
+ count = int(count_header)
273
+ except ValueError:
274
+ count = None
275
+ return AuditExportHeaders(
276
+ sha256=headers.get("X-ShieldCortex-Export-SHA256"),
277
+ count=count,
278
+ generated_at=headers.get("X-ShieldCortex-Export-Generated-At", ""),
279
+ manifest_id=headers.get("X-ShieldCortex-Export-Manifest-Id"),
280
+ signature=headers.get("X-ShieldCortex-Export-Signature"),
281
+ signature_algorithm=headers.get("X-ShieldCortex-Export-Signature-Alg", ""),
282
+ manifest_persisted=headers.get("X-ShieldCortex-Export-Manifest-Persisted") == "1",
283
+ )
284
+
285
+
226
286
  # ── Headers ───────────────────────────────────────────────────────────────────
227
287
 
228
288