panoptis 0.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (370) hide show
  1. panopti/__init__.py +166 -0
  2. panopti/__main__.py +8 -0
  3. panopti/_banner.py +37 -0
  4. panopti/attest.py +175 -0
  5. panopti/attest_keys.py +202 -0
  6. panopti/bench.py +127 -0
  7. panopti/cli.py +324 -0
  8. panopti/contracts.py +228 -0
  9. panopti/court.py +149 -0
  10. panopti/demo.py +383 -0
  11. panopti/doctor.py +90 -0
  12. panopti/egress.py +66 -0
  13. panopti/guard.py +260 -0
  14. panopti/killswitch.py +85 -0
  15. panopti/mesh.py +122 -0
  16. panopti/playground.html +791 -0
  17. panopti/playground.py +71 -0
  18. panopti/presets/__init__.py +35 -0
  19. panopti/presets/profiles.py +87 -0
  20. panopti/presets/rules.py +307 -0
  21. panopti/replay.py +197 -0
  22. panopti/reports.py +143 -0
  23. panopti/shield.py +53 -0
  24. panopti/synth.py +147 -0
  25. panopti/themes.py +205 -0
  26. panoptis/__init__.py +19 -0
  27. panoptis/__main__.py +6 -0
  28. panoptis/_version.py +30 -0
  29. panoptis/adapters/__init__.py +22 -0
  30. panoptis/adapters/autogen.py +188 -0
  31. panoptis/adapters/autogpt.py +55 -0
  32. panoptis/adapters/base.py +62 -0
  33. panoptis/adapters/claude_code.py +516 -0
  34. panoptis/adapters/crewai.py +119 -0
  35. panoptis/adapters/generic.py +143 -0
  36. panoptis/adapters/langchain.py +276 -0
  37. panoptis/adapters/langgraph.py +191 -0
  38. panoptis/adapters/openai_agents.py +117 -0
  39. panoptis/adapters/openclaw.py +32 -0
  40. panoptis/adapters/smolagents.py +189 -0
  41. panoptis/alerting/__init__.py +6 -0
  42. panoptis/alerting/channels.py +124 -0
  43. panoptis/alerting/engine.py +220 -0
  44. panoptis/analysis/__init__.py +0 -0
  45. panoptis/analysis/base.py +53 -0
  46. panoptis/analysis/capabilities.py +164 -0
  47. panoptis/analysis/entropy.py +174 -0
  48. panoptis/analysis/maturity.py +80 -0
  49. panoptis/analysis/registry.py +19 -0
  50. panoptis/api/__init__.py +1 -0
  51. panoptis/api/auth.py +214 -0
  52. panoptis/api/entitlement.py +102 -0
  53. panoptis/api/limits.py +172 -0
  54. panoptis/api/middleware/__init__.py +0 -0
  55. panoptis/api/middleware/rate_limiter.py +288 -0
  56. panoptis/api/protect_api.py +450 -0
  57. panoptis/api/protect_approvals.py +103 -0
  58. panoptis/api/protect_control.py +100 -0
  59. panoptis/api/protect_rbac.py +109 -0
  60. panoptis/api/server.py +2355 -0
  61. panoptis/api/tenancy.py +105 -0
  62. panoptis/api/tenant_auth.py +265 -0
  63. panoptis/api/tenant_store_sql.py +529 -0
  64. panoptis/api/v3.py +838 -0
  65. panoptis/behaviour/__init__.py +0 -0
  66. panoptis/behaviour/drift.py +186 -0
  67. panoptis/behaviour/motifs.py +603 -0
  68. panoptis/behaviour/profile.py +247 -0
  69. panoptis/causality/__init__.py +23 -0
  70. panoptis/causality/cones.py +181 -0
  71. panoptis/causality/decay.py +302 -0
  72. panoptis/causality/hypotheses.py +218 -0
  73. panoptis/circuit_breaker/__init__.py +34 -0
  74. panoptis/circuit_breaker/breaker.py +470 -0
  75. panoptis/circuit_breaker/loop_detector.py +134 -0
  76. panoptis/circuit_breaker/thresholds.py +66 -0
  77. panoptis/cli/__init__.py +11 -0
  78. panoptis/cli/_utils/__init__.py +11 -0
  79. panoptis/cli/_utils/run_cmd.py +170 -0
  80. panoptis/cli/animator.py +117 -0
  81. panoptis/cli/demo.py +606 -0
  82. panoptis/cli/init.py +124 -0
  83. panoptis/cli/main.py +3218 -0
  84. panoptis/cli/mcp.py +201 -0
  85. panoptis/cli/protect_ops.py +513 -0
  86. panoptis/cli/v3.py +1212 -0
  87. panoptis/cli/verify_env.py +151 -0
  88. panoptis/compare/__init__.py +0 -0
  89. panoptis/compare/runs.py +405 -0
  90. panoptis/contracts/__init__.py +149 -0
  91. panoptis/contracts/coverage.py +190 -0
  92. panoptis/contracts/evaluator.py +528 -0
  93. panoptis/contracts/irreversible.py +178 -0
  94. panoptis/contracts/mfotl.py +1614 -0
  95. panoptis/contracts/order.py +496 -0
  96. panoptis/contracts/remedies.py +44 -0
  97. panoptis/contracts/schema.py +198 -0
  98. panoptis/contracts/streaming.py +751 -0
  99. panoptis/core/__init__.py +37 -0
  100. panoptis/core/blast_radius.py +170 -0
  101. panoptis/core/config.py +72 -0
  102. panoptis/core/event_bus.py +384 -0
  103. panoptis/core/http_forwarder.py +87 -0
  104. panoptis/core/injection.py +145 -0
  105. panoptis/core/loop_detector.py +93 -0
  106. panoptis/core/models.py +835 -0
  107. panoptis/core/policy_dsl.py +373 -0
  108. panoptis/core/policy_loader.py +161 -0
  109. panoptis/core/recursion_depth_detector.py +78 -0
  110. panoptis/core/risk.py +101 -0
  111. panoptis/core/safety.py +933 -0
  112. panoptis/core/schema.py +501 -0
  113. panoptis/core/watcher.py +1131 -0
  114. panoptis/cost/__init__.py +8 -0
  115. panoptis/cost/anomaly.py +74 -0
  116. panoptis/cost/caching.py +13 -0
  117. panoptis/cost/comparator.py +80 -0
  118. panoptis/cost/complexity_router.py +213 -0
  119. panoptis/cost/governance.py +189 -0
  120. panoptis/cost/predictor.py +97 -0
  121. panoptis/cost/reporting.py +166 -0
  122. panoptis/cost/roi.py +100 -0
  123. panoptis/cost/router.py +16 -0
  124. panoptis/cost/semantic_cache.py +102 -0
  125. panoptis/cost/tracker.py +176 -0
  126. panoptis/entities/__init__.py +0 -0
  127. panoptis/entities/resolve.py +150 -0
  128. panoptis/eval/__init__.py +35 -0
  129. panoptis/eval/retrieval_drift.py +171 -0
  130. panoptis/eval/runner.py +258 -0
  131. panoptis/events/__init__.py +0 -0
  132. panoptis/events/codec.py +64 -0
  133. panoptis/events/model.py +289 -0
  134. panoptis/events/normalize.py +298 -0
  135. panoptis/events/normalizers/__init__.py +0 -0
  136. panoptis/events/normalizers/anthropic.py +3 -0
  137. panoptis/events/normalizers/claude_code.py +299 -0
  138. panoptis/events/normalizers/langchain.py +223 -0
  139. panoptis/events/normalizers/legacy.py +230 -0
  140. panoptis/events/normalizers/llm_api.py +221 -0
  141. panoptis/events/normalizers/mcp.py +185 -0
  142. panoptis/events/normalizers/native.py +297 -0
  143. panoptis/events/normalizers/openai.py +3 -0
  144. panoptis/events/normalizers/otel.py +355 -0
  145. panoptis/events/registry.py +44 -0
  146. panoptis/evidence/__init__.py +36 -0
  147. panoptis/evidence/attestation.py +465 -0
  148. panoptis/evidence/blobs.py +89 -0
  149. panoptis/evidence/canonical.py +63 -0
  150. panoptis/evidence/crypto.py +81 -0
  151. panoptis/evidence/ids.py +41 -0
  152. panoptis/evidence/merkle.py +186 -0
  153. panoptis/evidence/model.py +275 -0
  154. panoptis/evidence/recursive_proof.py +193 -0
  155. panoptis/evidence/redaction.py +149 -0
  156. panoptis/evidence/segments.py +160 -0
  157. panoptis/evidence/shredding.py +169 -0
  158. panoptis/evidence/verifier.py +238 -0
  159. panoptis/forecasting/__init__.py +0 -0
  160. panoptis/forecasting/trajectory.py +196 -0
  161. panoptis/governance/__init__.py +14 -0
  162. panoptis/governance/audit_log.py +272 -0
  163. panoptis/governance/causal.py +108 -0
  164. panoptis/governance/compliance_reporter.py +129 -0
  165. panoptis/governance/engine.py +360 -0
  166. panoptis/governance/eu_ai_act.py +146 -0
  167. panoptis/governance/gdpr.py +423 -0
  168. panoptis/governance/hipaa.py +107 -0
  169. panoptis/governance/iso42001.py +131 -0
  170. panoptis/governance/rbac.py +213 -0
  171. panoptis/governance/reports.py +134 -0
  172. panoptis/governance/residency.py +92 -0
  173. panoptis/graph/__init__.py +0 -0
  174. panoptis/graph/build.py +248 -0
  175. panoptis/graph/information.py +890 -0
  176. panoptis/graph/model.py +177 -0
  177. panoptis/graph/traverse.py +266 -0
  178. panoptis/hitl/__init__.py +31 -0
  179. panoptis/hitl/decisions.py +102 -0
  180. panoptis/hitl/feedback.py +154 -0
  181. panoptis/hitl/orchestrator.py +295 -0
  182. panoptis/hitl/thresholds.py +53 -0
  183. panoptis/infrastructure/__init__.py +1 -0
  184. panoptis/infrastructure/config.py +51 -0
  185. panoptis/infrastructure/router.py +176 -0
  186. panoptis/instrument.py +210 -0
  187. panoptis/lab/__init__.py +0 -0
  188. panoptis/lab/branch.py +252 -0
  189. panoptis/lab/observe.py +84 -0
  190. panoptis/lab/replay.py +237 -0
  191. panoptis/lab/replay_runtime.py +117 -0
  192. panoptis/lattice/__init__.py +29 -0
  193. panoptis/lattice/attention_scatter.py +198 -0
  194. panoptis/lattice/shadow_filesystem.py +200 -0
  195. panoptis/memory/__init__.py +15 -0
  196. panoptis/memory/causal_graph.py +130 -0
  197. panoptis/memory/decay.py +189 -0
  198. panoptis/memory/engine.py +636 -0
  199. panoptis/memory/governance.py +199 -0
  200. panoptis/memory/graph_query.py +403 -0
  201. panoptis/memory/health.py +118 -0
  202. panoptis/memory/identity.py +87 -0
  203. panoptis/memory/nlquery.py +183 -0
  204. panoptis/memory/resolver.py +76 -0
  205. panoptis/memory/temporal_decay.py +58 -0
  206. panoptis/memory/visualization.py +93 -0
  207. panoptis/models/__init__.py +0 -0
  208. panoptis/models/cache.py +14 -0
  209. panoptis/models/tenant.py +229 -0
  210. panoptis/monitoring/__init__.py +0 -0
  211. panoptis/monitoring/context_guard.py +363 -0
  212. panoptis/monitoring/metrics.py +122 -0
  213. panoptis/orchestration/__init__.py +19 -0
  214. panoptis/orchestration/consensus.py +84 -0
  215. panoptis/orchestration/crew_context.py +86 -0
  216. panoptis/orchestration/dag.py +119 -0
  217. panoptis/orchestration/deadlock.py +52 -0
  218. panoptis/orchestration/engine.py +418 -0
  219. panoptis/orchestration/handoff_tracker.py +91 -0
  220. panoptis/orchestration/propagation.py +57 -0
  221. panoptis/orchestration/race_condition.py +134 -0
  222. panoptis/orchestration/shapley.py +76 -0
  223. panoptis/orchestration/spawning.py +80 -0
  224. panoptis/orchestration/trust.py +61 -0
  225. panoptis/platform/__init__.py +1 -0
  226. panoptis/platform/cloud.py +105 -0
  227. panoptis/platform/eval_builder.py +90 -0
  228. panoptis/platform/intelligence.py +176 -0
  229. panoptis/platform/prompts.py +140 -0
  230. panoptis/platform/sharing.py +204 -0
  231. panoptis/plugins/__init__.py +6 -0
  232. panoptis/plugins/registry.py +102 -0
  233. panoptis/plugins/sandbox.py +155 -0
  234. panoptis/protect/__init__.py +230 -0
  235. panoptis/protect/approvals.py +433 -0
  236. panoptis/protect/arguments.py +1699 -0
  237. panoptis/protect/claude_hook.py +284 -0
  238. panoptis/protect/claude_hook_entry.py +21 -0
  239. panoptis/protect/control.py +268 -0
  240. panoptis/protect/counterfactual.py +306 -0
  241. panoptis/protect/crescendo_drift.py +255 -0
  242. panoptis/protect/discovery.py +621 -0
  243. panoptis/protect/enclave_attestation.py +255 -0
  244. panoptis/protect/enforce.py +290 -0
  245. panoptis/protect/formal_verifier.py +544 -0
  246. panoptis/protect/ifc_lattice.py +198 -0
  247. panoptis/protect/integrations.py +317 -0
  248. panoptis/protect/integrity.py +89 -0
  249. panoptis/protect/intent.py +674 -0
  250. panoptis/protect/kernel_sandbox.py +271 -0
  251. panoptis/protect/ledger.py +263 -0
  252. panoptis/protect/loops.py +460 -0
  253. panoptis/protect/ltl_trajectory.py +232 -0
  254. panoptis/protect/mcp_gateway.py +1012 -0
  255. panoptis/protect/memory_sentry.py +257 -0
  256. panoptis/protect/model.py +272 -0
  257. panoptis/protect/output.py +485 -0
  258. panoptis/protect/pipeline.py +1493 -0
  259. panoptis/protect/pqc_attestation.py +464 -0
  260. panoptis/protect/rag_sanitizer.py +185 -0
  261. panoptis/protect/rbac.py +946 -0
  262. panoptis/protect/registry.py +62 -0
  263. panoptis/protect/stego_neutralizer.py +182 -0
  264. panoptis/protect/support.py +208 -0
  265. panoptis/protect/taint_tracker.py +152 -0
  266. panoptis/protect/temporal_simulator.py +615 -0
  267. panoptis/protect/threats.py +234 -0
  268. panoptis/protect/transactor.py +100 -0
  269. panoptis/protect/wasi_sandbox.py +374 -0
  270. panoptis/protocol/__init__.py +1 -0
  271. panoptis/protocol/badge.py +81 -0
  272. panoptis/protocol/benchmark.py +104 -0
  273. panoptis/protocol/mcp_server.py +295 -0
  274. panoptis/protocol/schema_v1.py +132 -0
  275. panoptis/provenance/__init__.py +0 -0
  276. panoptis/provenance/ambiguity.py +103 -0
  277. panoptis/provenance/lineage.py +268 -0
  278. panoptis/query/__init__.py +0 -0
  279. panoptis/query/engine.py +368 -0
  280. panoptis/query/workspace.py +360 -0
  281. panoptis/reasoning/__init__.py +8 -0
  282. panoptis/reasoning/adversarial.py +97 -0
  283. panoptis/reasoning/auditor.py +359 -0
  284. panoptis/reasoning/benchmark.py +148 -0
  285. panoptis/reasoning/calibration.py +109 -0
  286. panoptis/reasoning/dual_eval.py +73 -0
  287. panoptis/reasoning/explainer.py +88 -0
  288. panoptis/reasoning/fingerprint.py +105 -0
  289. panoptis/reasoning/goal_drift.py +86 -0
  290. panoptis/reasoning/hallucination.py +122 -0
  291. panoptis/reasoning/quality.py +117 -0
  292. panoptis/reasoning/semantic_drift.py +92 -0
  293. panoptis/reasoning/trust_score.py +132 -0
  294. panoptis/replay/__init__.py +68 -0
  295. panoptis/replay/causal_scoring.py +186 -0
  296. panoptis/replay/counterfactual.py +459 -0
  297. panoptis/replay/divergence.py +293 -0
  298. panoptis/replay/engine.py +499 -0
  299. panoptis/replay/interventions.py +219 -0
  300. panoptis/replay/invariants.py +310 -0
  301. panoptis/replay/shadow.py +649 -0
  302. panoptis/rollback/__init__.py +11 -0
  303. panoptis/rollback/engine.py +507 -0
  304. panoptis/runs/__init__.py +0 -0
  305. panoptis/runs/segment.py +157 -0
  306. panoptis/runtime/__init__.py +0 -0
  307. panoptis/runtime/engine.py +472 -0
  308. panoptis/runtime/killswitch.py +7 -0
  309. panoptis/scoring/__init__.py +15 -0
  310. panoptis/scoring/confidence.py +354 -0
  311. panoptis/scoring/drift.py +170 -0
  312. panoptis/scoring/silence.py +143 -0
  313. panoptis/security/__init__.py +6 -0
  314. panoptis/security/abuse_detection.py +97 -0
  315. panoptis/security/checkout.py +50 -0
  316. panoptis/security/egress.py +7 -0
  317. panoptis/security/entitlement_store.py +96 -0
  318. panoptis/security/exfiltration.py +125 -0
  319. panoptis/security/license.py +176 -0
  320. panoptis/security/owasp.py +191 -0
  321. panoptis/security/payloads.json +72 -0
  322. panoptis/security/redaction.py +193 -0
  323. panoptis/security/redteam.py +224 -0
  324. panoptis/security/report.py +125 -0
  325. panoptis/security/sandbox.py +153 -0
  326. panoptis/security/shield.py +7 -0
  327. panoptis/security/webhook_signing.py +140 -0
  328. panoptis/sensors/__init__.py +58 -0
  329. panoptis/sensors/base.py +215 -0
  330. panoptis/sensors/claude_code/__init__.py +109 -0
  331. panoptis/sensors/groq_sensor.py +388 -0
  332. panoptis/sensors/langchain/__init__.py +418 -0
  333. panoptis/sensors/legacy/__init__.py +0 -0
  334. panoptis/sensors/legacy/translator.py +177 -0
  335. panoptis/sensors/llm_clients.py +168 -0
  336. panoptis/sensors/mcp.py +48 -0
  337. panoptis/sensors/mcp_error.py +74 -0
  338. panoptis/sensors/native/__init__.py +3 -0
  339. panoptis/sensors/native/launch.py +76 -0
  340. panoptis/sensors/native/recorder.py +493 -0
  341. panoptis/sensors/openrouter_sensor.py +421 -0
  342. panoptis/sensors/otel/__init__.py +223 -0
  343. panoptis/state/__init__.py +0 -0
  344. panoptis/state/latent.py +215 -0
  345. panoptis/storage/__init__.py +23 -0
  346. panoptis/storage/hyperedge.py +417 -0
  347. panoptis/storage/migration.py +189 -0
  348. panoptis/storage/schema.py +381 -0
  349. panoptis/storage/store.py +1833 -0
  350. panoptis/tasks.py +120 -0
  351. panoptis/telemetry/__init__.py +1 -0
  352. panoptis/telemetry/collector.py +271 -0
  353. panoptis/telemetry/execution_logger.py +189 -0
  354. panoptis/telemetry/exporter.py +139 -0
  355. panoptis/telemetry/otel.py +653 -0
  356. panoptis/tracing/__init__.py +8 -0
  357. panoptis/tracing/audit.py +136 -0
  358. panoptis/tracing/collector.py +367 -0
  359. panoptis/tracing/live.py +149 -0
  360. panoptis/tracing/otel.py +1 -0
  361. panoptis/tracing/sampling.py +128 -0
  362. panoptis/tracing/spans.py +186 -0
  363. panoptis/tracing/trajectory.py +147 -0
  364. panoptis/validation/__init__.py +0 -0
  365. panoptis/validation/schema_validator.py +36 -0
  366. panoptis-0.3.1.dist-info/METADATA +657 -0
  367. panoptis-0.3.1.dist-info/RECORD +370 -0
  368. panoptis-0.3.1.dist-info/WHEEL +4 -0
  369. panoptis-0.3.1.dist-info/entry_points.txt +3 -0
  370. panoptis-0.3.1.dist-info/licenses/LICENSE +201 -0
panopti/__init__.py ADDED
@@ -0,0 +1,166 @@
1
+ """Panopti: The All-Seeing Pre-Execution Guardrail & Forensic Engine for Autonomous AI.
2
+
3
+ Panopti is the successor to AgentWatch, providing pre-execution contract verification,
4
+ causal confidence decay tracking, cryptographic witness proofs, and deterministic forensic
5
+ shadow replay for agent swarms.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ try:
11
+ from panoptis._version import __version__
12
+ except (ImportError, ModuleNotFoundError):
13
+ __version__ = "0.2.0"
14
+
15
+ from panopti.attest import (
16
+ AttestationEngine,
17
+ PanoptiAttestationSeal,
18
+ )
19
+ from panopti.attest_keys import AttestationKeyError, Keyring, keyring_from_env
20
+ from panopti.contracts import (
21
+ Contract,
22
+ ContractRegistry,
23
+ ContractViolationError,
24
+ Disallows,
25
+ MaxFrequency,
26
+ RequiresPrior,
27
+ )
28
+ from panopti.court import (
29
+ WitnessChain,
30
+ WitnessEntry,
31
+ )
32
+ from panopti.egress import (
33
+ EgressGuard,
34
+ SSRFBlockedError,
35
+ )
36
+ from panopti.killswitch import (
37
+ EmergencyKillSwitch,
38
+ EmergencyStopTriggered,
39
+ )
40
+ from panopti.mesh import (
41
+ SwarmAgentNode,
42
+ SwarmMesh,
43
+ )
44
+ from panopti.replay import (
45
+ DropStepPerturbation,
46
+ MutateResultPerturbation,
47
+ Perturbation,
48
+ RecordedCall,
49
+ ReplayReport,
50
+ ReplayTape,
51
+ ShadowReplayer,
52
+ )
53
+ from panopti.shield import (
54
+ AntiTamperShield,
55
+ TamperAttemptBlocked,
56
+ )
57
+ from panopti.synth import (
58
+ PolicySynthesizer,
59
+ SynthesizedInvariant,
60
+ )
61
+
62
+ try:
63
+ from panopti.doctor import run_diagnostics
64
+ from panopti.guard import (
65
+ GuardDecision,
66
+ PanoptiGuard,
67
+ guard,
68
+ )
69
+ from panoptis.causality import (
70
+ CUMULATIVE_CONFIDENCE_COLLAPSE,
71
+ ConfidenceCollapseWarning,
72
+ ConfidenceDecayTracker,
73
+ )
74
+ from panoptis.circuit_breaker import (
75
+ AgentLoopError,
76
+ AgentLoopException,
77
+ CircuitBreaker,
78
+ CircuitState,
79
+ CircuitThresholds,
80
+ LoopDetector,
81
+ TripReason,
82
+ )
83
+ from panoptis.core.watcher import (
84
+ AgentWatchBlockedError,
85
+ GenericAdapter,
86
+ detect_framework,
87
+ detect_framework_label,
88
+ watch,
89
+ )
90
+
91
+ PanoptiBlockedError = AgentWatchBlockedError
92
+ except ImportError: # pragma: no cover
93
+ watch = None # type: ignore[assignment]
94
+ guard = None # type: ignore[assignment]
95
+ detect_framework = None # type: ignore[assignment]
96
+ detect_framework_label = None # type: ignore[assignment]
97
+ GenericAdapter = None # type: ignore[assignment]
98
+ AgentWatchBlockedError = Exception # type: ignore[assignment,misc]
99
+ PanoptiBlockedError = AgentWatchBlockedError
100
+ CircuitBreaker = None # type: ignore[assignment]
101
+ CircuitState = None # type: ignore[assignment]
102
+ CircuitThresholds = None # type: ignore[assignment]
103
+ TripReason = None # type: ignore[assignment]
104
+ LoopDetector = None # type: ignore[assignment]
105
+ AgentLoopError = Exception # type: ignore[assignment,misc]
106
+ AgentLoopException = AgentLoopError
107
+ PanoptiGuard = None # type: ignore[assignment]
108
+ GuardDecision = None # type: ignore[assignment]
109
+ ConfidenceDecayTracker = None # type: ignore[assignment]
110
+ CUMULATIVE_CONFIDENCE_COLLAPSE = "CUMULATIVE_CONFIDENCE_COLLAPSE"
111
+ ConfidenceCollapseWarning = None # type: ignore[assignment]
112
+ run_diagnostics = None # type: ignore[assignment]
113
+
114
+ __all__ = [
115
+ "__version__",
116
+ "guard",
117
+ "watch",
118
+ "detect_framework",
119
+ "detect_framework_label",
120
+ "GenericAdapter",
121
+ "PanoptiBlockedError",
122
+ "AgentWatchBlockedError",
123
+ "CircuitBreaker",
124
+ "CircuitState",
125
+ "CircuitThresholds",
126
+ "TripReason",
127
+ "LoopDetector",
128
+ "AgentLoopError",
129
+ "AgentLoopException",
130
+ "ConfidenceDecayTracker",
131
+ "CUMULATIVE_CONFIDENCE_COLLAPSE",
132
+ "ConfidenceCollapseWarning",
133
+ "PanoptiGuard",
134
+ "GuardDecision",
135
+ "Contract",
136
+ "ContractRegistry",
137
+ "RequiresPrior",
138
+ "Disallows",
139
+ "MaxFrequency",
140
+ "ContractViolationError",
141
+ "WitnessEntry",
142
+ "WitnessChain",
143
+ "SwarmAgentNode",
144
+ "SwarmMesh",
145
+ "run_diagnostics",
146
+ "RecordedCall",
147
+ "ReplayTape",
148
+ "ShadowReplayer",
149
+ "ReplayReport",
150
+ "Perturbation",
151
+ "DropStepPerturbation",
152
+ "MutateResultPerturbation",
153
+ "PanoptiAttestationSeal",
154
+ "AttestationKeyError",
155
+ "Keyring",
156
+ "keyring_from_env",
157
+ "AttestationEngine",
158
+ "PolicySynthesizer",
159
+ "SynthesizedInvariant",
160
+ "EmergencyKillSwitch",
161
+ "EmergencyStopTriggered",
162
+ "AntiTamperShield",
163
+ "TamperAttemptBlocked",
164
+ "EgressGuard",
165
+ "SSRFBlockedError",
166
+ ]
panopti/__main__.py ADDED
@@ -0,0 +1,8 @@
1
+ """Panopti CLI entry point."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from panoptis.cli.main import main
6
+
7
+ if __name__ == "__main__":
8
+ main()
panopti/_banner.py ADDED
@@ -0,0 +1,37 @@
1
+ """Panopti ASCII Banner."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+ PANOPTI_ASCII_BANNER = r"""
8
+ ██████╗ █████╗ ███╗ ██╗ ██████╗ ██████╗ ████████╗██╗
9
+ ██╔══██╗██╔══██╗████╗ ██║██╔═══██╗██╔══██╗╚══██╔══╝██║
10
+ ██████╔╝███████║██╔██╗ ██║██║ ██║██████╔╝ ██║ ██║
11
+ ██╔═══╝ ██╔══██║██║╚██╗██║██║ ██║██╔═══╝ ██║ ██║
12
+ ██║ ██║ ██║██║ ╚████║╚██████╔╝██║ ██║ ██║
13
+ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝
14
+ Argos Panoptes • All-Seeing Pre-Execution Guardrail
15
+ """
16
+
17
+ PANOPTI_ASCII_BANNER_SAFE = r"""
18
+ ____ _ _
19
+ | _ \ __ _ _ __ ___ _ __ _ _ (_) | |
20
+ | |_) / _` | '_ \ / _ \| '_ \| |_| | | | |
21
+ | __/ (_| | | | | (_) | |_) | _ | | |_|
22
+ |_| \__,_|_| |_|\___/| .__/|_| |_|_| (_)
23
+ |_|
24
+ Argos Panoptes - All-Seeing Pre-Execution Guardrail
25
+ """
26
+
27
+
28
+ def print_banner() -> None:
29
+ """Print banner safely across all terminal encodings (e.g. Windows cp1252/cp437)."""
30
+ encoding = getattr(sys.stdout, "encoding", None) or "utf-8"
31
+ try:
32
+ PANOPTI_ASCII_BANNER.encode(encoding)
33
+ sys.stdout.write(PANOPTI_ASCII_BANNER + "\n")
34
+ sys.stdout.flush()
35
+ except (UnicodeEncodeError, LookupError):
36
+ sys.stdout.write(PANOPTI_ASCII_BANNER_SAFE + "\n")
37
+ sys.stdout.flush()
panopti/attest.py ADDED
@@ -0,0 +1,175 @@
1
+ """Panopti Cryptographic Policy Attestation & Verifiable Audit Seals.
2
+
3
+ Generates tamper-evident cryptographic seals for agent execution traces, proving
4
+ formal invariant compliance and absence of runaway loops.
5
+ Pure standard library (Ponytail: Ultra).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+ import hmac
12
+ import json
13
+ import uuid
14
+ from dataclasses import asdict, dataclass
15
+ from datetime import UTC, datetime
16
+ from typing import Any
17
+
18
+ from panopti.attest_keys import AttestationKeyError, Keyring, keyring_from_env
19
+
20
+
21
+ def _canonical_json(data: Any) -> str:
22
+ """Serialize object to canonical, deterministic JSON."""
23
+ return json.dumps(data, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
24
+
25
+
26
+ SEAL_VERSION = "2"
27
+
28
+
29
+ @dataclass(slots=True)
30
+ class PanoptiAttestationSeal:
31
+ """Cryptographic attestation certificate proving verified execution.
32
+
33
+ Version 2 seals name the key that signed them (``key_id``) and sign every field,
34
+ including the version and key id, as canonical JSON.
35
+ """
36
+
37
+ seal_id: str
38
+ merkle_root: str
39
+ policy_verdict: str
40
+ confidence_floor: float
41
+ loop_status: str
42
+ tool_count: int
43
+ timestamp_iso: str
44
+ signature: str
45
+ key_id: str | None = None
46
+ version: str = "1"
47
+
48
+ def to_dict(self) -> dict[str, Any]:
49
+ return asdict(self)
50
+
51
+ def to_json(self) -> str:
52
+ return json.dumps(self.to_dict(), indent=2)
53
+
54
+ def signed_fields(self) -> dict[str, Any]:
55
+ d = self.to_dict()
56
+ d.pop("signature")
57
+ d["confidence_floor"] = f"{float(d['confidence_floor']):.4f}"
58
+ return d
59
+
60
+ @classmethod
61
+ def from_dict(cls, data: dict[str, Any]) -> PanoptiAttestationSeal:
62
+ return cls(
63
+ seal_id=data["seal_id"],
64
+ merkle_root=data["merkle_root"],
65
+ policy_verdict=data["policy_verdict"],
66
+ confidence_floor=float(data["confidence_floor"]),
67
+ loop_status=data["loop_status"],
68
+ tool_count=int(data["tool_count"]),
69
+ timestamp_iso=data["timestamp_iso"],
70
+ signature=data["signature"],
71
+ key_id=data.get("key_id"),
72
+ version=str(data.get("version", "1")),
73
+ )
74
+
75
+ @classmethod
76
+ def from_json(cls, json_str: str) -> PanoptiAttestationSeal:
77
+ return cls.from_dict(json.loads(json_str))
78
+
79
+
80
+ def _keyring_for(keyring: Keyring | None, secret_key: str | bytes | None) -> Keyring:
81
+ """The keyring to use: explicit, else a one-key ring from ``secret_key``, else the env."""
82
+ if keyring is not None:
83
+ return keyring
84
+ if secret_key is not None:
85
+ ring = Keyring()
86
+ key = secret_key.encode() if isinstance(secret_key, str) else secret_key
87
+ ring.add("explicit", key) # validated: strength, not a known public default
88
+ return ring
89
+ return keyring_from_env()
90
+
91
+
92
+ class AttestationEngine:
93
+ """Issues and cryptographically validates Panopti attestation seals."""
94
+
95
+ @staticmethod
96
+ def _v1_payload(seal: PanoptiAttestationSeal) -> str:
97
+ return (
98
+ f"{seal.seal_id}|{seal.merkle_root}|{seal.policy_verdict}|"
99
+ f"{seal.confidence_floor:.4f}|{seal.loop_status}|{seal.tool_count}|{seal.timestamp_iso}"
100
+ )
101
+
102
+ @staticmethod
103
+ def _sign(key: bytes, seal: PanoptiAttestationSeal) -> str:
104
+ return hmac.new(
105
+ key, _canonical_json(seal.signed_fields()).encode(), hashlib.sha256
106
+ ).hexdigest()
107
+
108
+ @classmethod
109
+ def create_seal(
110
+ cls,
111
+ merkle_root: str,
112
+ tool_count: int,
113
+ confidence_floor: float = 1.0,
114
+ is_compliant: bool = True,
115
+ loop_detected: bool = False,
116
+ secret_key: str | bytes | None = None,
117
+ *,
118
+ keyring: Keyring | None = None,
119
+ ) -> PanoptiAttestationSeal:
120
+ """Issue a signed (version 2) attestation seal.
121
+
122
+ The key comes from ``keyring``, else ``secret_key`` (checked: at least 32 bytes,
123
+ not a known public key), else ``AGENTWATCH_ATTEST_KEYS``. There is no default key:
124
+ with none configured this raises :class:`AttestationKeyError`.
125
+ """
126
+ kid, key = _keyring_for(keyring, secret_key).signing_key()
127
+ seal = PanoptiAttestationSeal(
128
+ seal_id=f"seal_{uuid.uuid4().hex[:16]}",
129
+ merkle_root=merkle_root,
130
+ policy_verdict="COMPLIANT" if is_compliant else "VIOLATION",
131
+ confidence_floor=confidence_floor,
132
+ loop_status="LOOP_DETECTED" if loop_detected else "CLEAN",
133
+ tool_count=tool_count,
134
+ timestamp_iso=datetime.now(UTC).isoformat(),
135
+ signature="",
136
+ key_id=kid,
137
+ version=SEAL_VERSION,
138
+ )
139
+ seal.signature = cls._sign(key, seal)
140
+ return seal
141
+
142
+ @classmethod
143
+ def verify_seal(
144
+ cls,
145
+ seal: PanoptiAttestationSeal,
146
+ secret_key: str | bytes | None = None,
147
+ *,
148
+ keyring: Keyring | None = None,
149
+ allow_legacy: bool = False,
150
+ ) -> bool:
151
+ """True only if the seal was signed by a known, unrevoked key and is unmodified.
152
+
153
+ Fails closed: an unknown or revoked key id, a missing key or any modified field
154
+ returns False. Version 1 seals (no key id) verify only with ``allow_legacy=True``
155
+ and an explicit, non-public ``secret_key``.
156
+ """
157
+ try:
158
+ ring = _keyring_for(keyring, secret_key)
159
+ except AttestationKeyError:
160
+ return False
161
+ if seal.version == "1" or seal.key_id is None:
162
+ if not allow_legacy or secret_key is None:
163
+ return False
164
+ key = ring.verification_key("explicit")
165
+ if key is None:
166
+ return False
167
+ expected = hmac.new(key, cls._v1_payload(seal).encode(), hashlib.sha256).hexdigest()
168
+ return hmac.compare_digest(seal.signature, expected)
169
+ if seal.version != SEAL_VERSION:
170
+ return False
171
+ kid = seal.key_id if keyring is not None or secret_key is None else "explicit"
172
+ key = ring.verification_key(kid)
173
+ if key is None:
174
+ return False
175
+ return hmac.compare_digest(seal.signature, cls._sign(key, seal))
panopti/attest_keys.py ADDED
@@ -0,0 +1,202 @@
1
+ """Signing keys for attestation seals: a keyring with ids, rotation and revocation.
2
+
3
+ Configuration (environment)::
4
+
5
+ AGENTWATCH_ATTEST_KEYS kid=<base64 or hex key>,kid2=... (one per line also works)
6
+ AGENTWATCH_ATTEST_ACTIVE_KID the kid new seals are signed with (default: the last listed)
7
+ AGENTWATCH_ATTEST_REVOKED kids that must no longer verify (comma separated)
8
+ AGENTWATCH_ATTEST_DEV_KEY=1 development only: an ephemeral random key for this process
9
+
10
+ There is no built-in key. A key must be at least 32 bytes and must not be a known public
11
+ default; the dev key is refused in production and cloud mode. Key material is never
12
+ logged or included in exceptions.
13
+
14
+ Generate a key::
15
+
16
+ python -c "import secrets, base64; print(base64.b64encode(secrets.token_bytes(32)).decode())"
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import base64
22
+ import binascii
23
+ import hmac
24
+ import logging
25
+ import os
26
+ import re
27
+ import secrets
28
+ from dataclasses import dataclass, field
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+ MIN_KEY_BYTES = 32
33
+ # Keys that shipped in source code; anyone can forge seals with them.
34
+ KNOWN_PUBLIC_KEYS = frozenset({b"panopti-seal-default-key", b"panopti-demo-hmac-key"})
35
+ _KID = re.compile(r"^[A-Za-z0-9_.\-]{1,64}$")
36
+
37
+
38
+ class AttestationKeyError(ValueError):
39
+ """The signing-key configuration is missing or unsafe. The message never contains key material."""
40
+
41
+
42
+ def decode_key(text: str) -> bytes:
43
+ """A key given as base64 (preferred) or hex."""
44
+ t = text.strip()
45
+ if re.fullmatch(r"[0-9a-fA-F]+", t) and len(t) % 2 == 0 and len(t) >= 2 * MIN_KEY_BYTES:
46
+ return bytes.fromhex(t)
47
+ try:
48
+ return base64.b64decode(t, validate=True)
49
+ except (binascii.Error, ValueError) as exc:
50
+ raise AttestationKeyError("attestation key is neither valid base64 nor hex") from exc
51
+
52
+
53
+ def check_key(kid: str, key: bytes) -> None:
54
+ if not _KID.match(kid):
55
+ raise AttestationKeyError(f"invalid key id {kid!r}")
56
+ if any(hmac.compare_digest(key, k) for k in KNOWN_PUBLIC_KEYS):
57
+ raise AttestationKeyError(
58
+ f"key {kid!r} is a known public default key; anyone could forge seals with it"
59
+ )
60
+ if len(key) < MIN_KEY_BYTES:
61
+ raise AttestationKeyError(
62
+ f"key {kid!r} is {len(key)} bytes; at least {MIN_KEY_BYTES} are required"
63
+ )
64
+
65
+
66
+ def is_production() -> bool:
67
+ env = (os.environ.get("AGENTWATCH_ENV") or "").strip().lower()
68
+ cloud = (os.environ.get("AGENTWATCH_CLOUD_MODE") or "").strip().lower() in (
69
+ "1",
70
+ "true",
71
+ "yes",
72
+ "on",
73
+ )
74
+ return env in ("production", "prod") or cloud
75
+
76
+
77
+ @dataclass
78
+ class Keyring:
79
+ keys: dict[str, bytes] = field(default_factory=dict)
80
+ active_kid: str | None = None
81
+ revoked: frozenset[str] = frozenset()
82
+ ephemeral: bool = False # the development-only random key
83
+
84
+ def add(self, kid: str, key: bytes, *, activate: bool = True) -> None:
85
+ check_key(kid, key)
86
+ self.keys[kid] = key
87
+ if activate or self.active_kid is None:
88
+ self.active_kid = kid
89
+
90
+ def signing_key(self) -> tuple[str, bytes]:
91
+ if not self.active_kid or self.active_kid not in self.keys:
92
+ raise AttestationKeyError("no active attestation signing key is configured")
93
+ if self.active_kid in self.revoked:
94
+ raise AttestationKeyError(
95
+ f"the active key {self.active_kid!r} is revoked; activate another key"
96
+ )
97
+ return self.active_kid, self.keys[self.active_kid]
98
+
99
+ def verification_key(self, kid: str) -> bytes | None:
100
+ """The key for ``kid``, or None if it is unknown or revoked (verification then fails)."""
101
+ if kid in self.revoked:
102
+ return None
103
+ return self.keys.get(kid)
104
+
105
+ def describe(self) -> dict[str, object]:
106
+ """Safe summary for diagnostics (no key material)."""
107
+ return {
108
+ "kids": sorted(self.keys),
109
+ "active_kid": self.active_kid,
110
+ "revoked": sorted(self.revoked),
111
+ "ephemeral": self.ephemeral,
112
+ }
113
+
114
+
115
+ def parse_keys(raw: str | None) -> dict[str, bytes]:
116
+ out: dict[str, bytes] = {}
117
+ if not raw:
118
+ return out
119
+ for part in re.split(r"[,\n]", raw):
120
+ part = part.strip()
121
+ if not part:
122
+ continue
123
+ kid, sep, value = part.partition("=")
124
+ kid = kid.strip()
125
+ if not sep or not value.strip():
126
+ raise AttestationKeyError(
127
+ f"AGENTWATCH_ATTEST_KEYS: entry for {kid!r} must be kid=<key>"
128
+ )
129
+ if kid in out:
130
+ raise AttestationKeyError(f"AGENTWATCH_ATTEST_KEYS: key id {kid!r} is listed twice")
131
+ key = decode_key(value)
132
+ check_key(kid, key)
133
+ out[kid] = key
134
+ return out
135
+
136
+
137
+ def keyring_from_env(environ: dict[str, str] | None = None) -> Keyring:
138
+ """The configured keyring. Raises :class:`AttestationKeyError` if it is missing or unsafe."""
139
+ env = os.environ if environ is None else environ
140
+ keys = parse_keys(env.get("AGENTWATCH_ATTEST_KEYS"))
141
+ revoked = frozenset(
142
+ k.strip() for k in (env.get("AGENTWATCH_ATTEST_REVOKED") or "").split(",") if k.strip()
143
+ )
144
+ ring = Keyring(keys=keys, revoked=revoked)
145
+ if keys:
146
+ active = (env.get("AGENTWATCH_ATTEST_ACTIVE_KID") or "").strip() or list(keys)[-1]
147
+ if active not in keys:
148
+ raise AttestationKeyError(
149
+ f"AGENTWATCH_ATTEST_ACTIVE_KID {active!r} is not in AGENTWATCH_ATTEST_KEYS"
150
+ )
151
+ ring.active_kid = active
152
+ return ring
153
+ if (env.get("AGENTWATCH_ATTEST_DEV_KEY") or "").strip().lower() in ("1", "true", "yes", "on"):
154
+ prod = (env.get("AGENTWATCH_ENV") or "").strip().lower() in ("production", "prod") or (
155
+ env.get("AGENTWATCH_CLOUD_MODE") or ""
156
+ ).strip().lower() in ("1", "true", "yes", "on")
157
+ if prod:
158
+ raise AttestationKeyError(
159
+ "AGENTWATCH_ATTEST_DEV_KEY is development-only and is refused in production/cloud mode"
160
+ )
161
+ logger.warning(
162
+ "attestation: using an ephemeral development key; seals will not verify in another process"
163
+ )
164
+ ring.keys["dev-ephemeral"] = secrets.token_bytes(MIN_KEY_BYTES)
165
+ ring.active_kid = "dev-ephemeral"
166
+ ring.ephemeral = True
167
+ return ring
168
+ raise AttestationKeyError(
169
+ "no attestation signing key configured: set AGENTWATCH_ATTEST_KEYS (see docs/ATTESTATION_KEYS.md); "
170
+ "for local development only, AGENTWATCH_ATTEST_DEV_KEY=1"
171
+ )
172
+
173
+
174
+ def check_startup(environ: dict[str, str] | None = None) -> dict[str, object] | None:
175
+ """Startup gate for servers: in production/cloud mode, refuse unsafe attestation config.
176
+
177
+ Returns a key-free summary of the keyring when one is configured, None when
178
+ attestation is neither configured nor required. Raises :class:`AttestationKeyError`
179
+ (the server must not start) when, in production/cloud mode, keys are configured but
180
+ weak/public/malformed, the development key is requested, or
181
+ AGENTWATCH_REQUIRE_ATTESTATION=1 and no key is configured. Outside production, a
182
+ broken configuration is still an error; an absent one is not.
183
+ """
184
+ env = os.environ if environ is None else environ
185
+ prod = (env.get("AGENTWATCH_ENV") or "").strip().lower() in ("production", "prod") or (
186
+ env.get("AGENTWATCH_CLOUD_MODE") or ""
187
+ ).strip().lower() in ("1", "true", "yes", "on")
188
+ configured = bool((env.get("AGENTWATCH_ATTEST_KEYS") or "").strip())
189
+ dev = (env.get("AGENTWATCH_ATTEST_DEV_KEY") or "").strip().lower() in ("1", "true", "yes", "on")
190
+ required = (env.get("AGENTWATCH_REQUIRE_ATTESTATION") or "").strip().lower() in (
191
+ "1",
192
+ "true",
193
+ "yes",
194
+ "on",
195
+ )
196
+ if not (configured or dev or required):
197
+ return None
198
+ if prod and required and not configured:
199
+ raise AttestationKeyError(
200
+ "AGENTWATCH_REQUIRE_ATTESTATION=1 but AGENTWATCH_ATTEST_KEYS is not set"
201
+ )
202
+ return keyring_from_env(env).describe()