primust 1.0.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.
@@ -0,0 +1,22 @@
1
+ node_modules/
2
+ dist/
3
+ .next/
4
+ __pycache__/
5
+ *.egg-info/
6
+ .venv/
7
+ .env
8
+ .env.local
9
+ .env.*.local
10
+ *.pyc
11
+ .turbo/
12
+ *.pem
13
+ *.key
14
+ .DS_Store
15
+ .claude/
16
+ .claude.json
17
+ .claude.json.backup
18
+ .pytest_cache/
19
+ *.db
20
+ *.db-shm
21
+ *.db-wal
22
+ .vercel
primust-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,23 @@
1
+ Metadata-Version: 2.4
2
+ Name: primust
3
+ Version: 1.0.0
4
+ Summary: Prove your governance checks ran. Portable, offline-verifiable cryptographic credentials.
5
+ Project-URL: Homepage, https://primust.com
6
+ Project-URL: Documentation, https://docs.primust.com
7
+ Project-URL: Verify, https://verify.primust.com
8
+ Author-email: "Primust, Inc." <eng@primust.com>
9
+ License-Expression: LicenseRef-Proprietary
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Security :: Cryptography
17
+ Requires-Python: >=3.11
18
+ Requires-Dist: cryptography>=41.0
19
+ Requires-Dist: httpx>=0.27.0
20
+ Requires-Dist: primust-artifact-core>=1.0.0
21
+ Requires-Dist: pydantic>=2.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=8.0; extra == 'dev'
@@ -0,0 +1,183 @@
1
+ # Primust Python SDK
2
+
3
+ Prove governance ran. Disclose nothing.
4
+
5
+ ```bash
6
+ pip install primust
7
+ ```
8
+
9
+ ## What this is
10
+
11
+ Primust issues **Verifiable Process Execution Credentials (VPECs)** — portable, offline-verifiable proofs that a defined governance process executed correctly on specific data, without disclosing the data.
12
+
13
+ A VPEC answers: *"Did your AML screening actually run on this entity?"* — without your watchlist matching criteria, velocity thresholds, or customer data leaving your environment.
14
+
15
+ ## Quickstart
16
+
17
+ ```python
18
+ import primust
19
+
20
+ p = primust.Pipeline(
21
+ api_key="pk_live_...",
22
+ workflow_id="aml-screening-v2"
23
+ )
24
+
25
+ run = p.open()
26
+
27
+ result = run.record(
28
+ check="aml_entity_screen",
29
+ manifest_id="sha256:abc123...", # from p.register_check()
30
+ input=entity_data, # committed locally — never sent to Primust
31
+ check_result="pass",
32
+ visibility="opaque",
33
+ )
34
+
35
+ # Write result.commitment_hash to your own logs
36
+ # This is your log linkage anchor — connects your logs to the VPEC
37
+ print(result.commitment_hash) # sha256:...
38
+
39
+ vpec = run.close()
40
+ # vpec is the signed, portable credential
41
+ # Provide to your regulator. They verify at verify.primust.com
42
+ # without receiving your data.
43
+ ```
44
+
45
+ ## Privacy guarantee
46
+
47
+ Raw input values are committed locally via SHA-256 (Poseidon2 when the native extension is available) before any network call. Only the commitment hash and bounded normalized metadata transit to `api.primust.com`.
48
+
49
+ **Your data never leaves your environment.**
50
+
51
+ This is enforced in the SDK — not advisory. The transport layer never receives raw values. Tests verify this by intercepting every outbound HTTP request and asserting sensitive input strings are absent.
52
+
53
+ ## Proof levels
54
+
55
+ | Level | When | Verifier confidence |
56
+ |---|---|---|
57
+ | `mathematical` | Deterministic rule, arithmetic verifiable | Cryptographic — can replay |
58
+ | `execution` | In-process instrumentation | Strong — execution binding |
59
+ | `witnessed` | Human review with RFC 3161 timing | Regulatory — signed review |
60
+ | `attestation` | API-level observation | Audit — process ran |
61
+
62
+ The VPEC applies weakest-link: the overall credential level is the lowest level across all checks in the run.
63
+
64
+ ## API reference
65
+
66
+ ### `Pipeline(api_key, workflow_id, ...)`
67
+
68
+ ```python
69
+ p = primust.Pipeline(
70
+ api_key="pk_live_...", # or set PRIMUST_API_KEY env var
71
+ workflow_id="my-workflow", # identifies the governed process
72
+ surface_id=None, # optional: instrumentation surface
73
+ environment="production", # inferred from key prefix
74
+ )
75
+ ```
76
+
77
+ ### `p.open(policy_pack_id?) → Run`
78
+
79
+ Opens a governed process run. Returns a `Run`. All `.record()` calls belong to this run. Close with `.close()` to issue the VPEC.
80
+
81
+ ### `run.record(check, manifest_id, check_result, input, ...) → RecordResult`
82
+
83
+ ```python
84
+ result = run.record(
85
+ check="pii_scan",
86
+ manifest_id="sha256:...",
87
+ check_result="pass", # pass | fail | error | skipped | degraded | override
88
+ input=content, # committed locally — never sent
89
+ details={"score": 0.04}, # bounded metadata — will transit, must not be sensitive
90
+ output=None, # optional output commitment
91
+ visibility="opaque", # transparent | selective | opaque
92
+ )
93
+
94
+ result.commitment_hash # sha256:... — write this to your logs
95
+ result.record_id # rec_...
96
+ result.proof_level # attestation | execution | witnessed | mathematical
97
+ result.queued # True if API was unreachable — will flush on reconnect
98
+ ```
99
+
100
+ ### `run.open_check(check, manifest_id) → CheckSession`
101
+
102
+ Opens a timed check session. Returns an RFC 3161 timestamp at open time. Pass to `run.record(check_session=...)`. Sub-100ms ML inference emits `check_timing_suspect` gap.
103
+
104
+ ### `run.open_review(check, manifest_id, reviewer_key_id, ...) → ReviewSession`
105
+
106
+ Opens a Witnessed level human review session. Pass to `run.record(check_session=..., reviewer_signature=..., rationale=...)`.
107
+
108
+ ### `run.close() → VPEC`
109
+
110
+ Closes the run and issues the VPEC. After close, no further records can be added.
111
+
112
+ ```python
113
+ vpec = run.close()
114
+
115
+ vpec.vpec_id # vpec_...
116
+ vpec.proof_level # weakest-link across all checks
117
+ vpec.chain_intact # True if commitment chain is unbroken
118
+ vpec.governance_gaps # list of GovernanceGap — missing checks, timing anomalies
119
+ vpec.is_clean() # True if chain intact and zero gaps
120
+ vpec.to_dict() # full JSON for offline verification
121
+ ```
122
+
123
+ ### `p.register_check(manifest) → ManifestRegistration`
124
+
125
+ Register a check manifest. Call once per manifest version. Returns `manifest_id` — content-addressed SHA-256, idempotent.
126
+
127
+ ## Offline durability
128
+
129
+ If `api.primust.com` is unreachable, records queue locally in SQLite. The SDK never throws to the caller due to API unavailability. When connectivity recovers, the queue flushes automatically on the next successful call.
130
+
131
+ If the queue is permanently lost, a `system_unavailable` gap is recorded in the VPEC — the SDK never silently drops governance evidence.
132
+
133
+ ```python
134
+ p.pending_queue_count() # items waiting in local queue
135
+ p.flush_queue() # manually trigger flush attempt
136
+ ```
137
+
138
+ ## Governance gaps
139
+
140
+ The VPEC records gaps automatically:
141
+
142
+ | Gap type | Meaning |
143
+ |---|---|
144
+ | `check_missing` | Expected check in manifest not executed |
145
+ | `check_failed` | Check ran and returned fail |
146
+ | `check_timing_suspect` | Execution time implausibly fast (< 100ms for ML check) |
147
+ | `sequence_gap` | Record sequence broken — possible tampering |
148
+ | `system_unavailable` | Primust API unreachable during run |
149
+ | `policy_config_drift` | Policy changed between run open and close |
150
+
151
+ ## Log linkage
152
+
153
+ Every `RecordResult` contains a `commitment_hash`. Write this to your operational logs alongside the transaction or decision ID it corresponds to. This creates a verifiable link between your logs and the VPEC — a verifier can confirm your log entry corresponds to a specific record in the credential.
154
+
155
+ ```python
156
+ result = run.record(...)
157
+ logger.info("aml_screen completed",
158
+ commitment_hash=result.commitment_hash,
159
+ transaction_id=txn_id,
160
+ )
161
+ ```
162
+
163
+ ## Verify a VPEC
164
+
165
+ ```bash
166
+ pip install primust-verify
167
+ primust-verify vpec.json
168
+ ```
169
+
170
+ Or online at [verify.primust.com](https://verify.primust.com) — no account required.
171
+
172
+ ## Requirements
173
+
174
+ - Python 3.11+
175
+ - `httpx>=0.27.0`
176
+
177
+ ## License
178
+
179
+ Proprietary — see LICENSE file.
180
+
181
+ ---
182
+
183
+ [Docs](https://docs.primust.com) · [Verify](https://verify.primust.com) · [Connectors](https://github.com/primust-dev/connectors)
@@ -0,0 +1,45 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "primust"
7
+ version = "1.0.0"
8
+ description = "Prove your governance checks ran. Portable, offline-verifiable cryptographic credentials."
9
+ requires-python = ">=3.11"
10
+ license = "LicenseRef-Proprietary"
11
+ authors = [{ name = "Primust, Inc.", email = "eng@primust.com" }]
12
+ classifiers = [
13
+ "Development Status :: 5 - Production/Stable",
14
+ "Intended Audience :: Developers",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Topic :: Security :: Cryptography",
20
+ ]
21
+ dependencies = [
22
+ "httpx>=0.27.0",
23
+ "pydantic>=2.0",
24
+ "cryptography>=41.0",
25
+ "primust-artifact-core>=1.0.0",
26
+ ]
27
+
28
+ [project.optional-dependencies]
29
+ dev = [
30
+ "pytest>=8.0",
31
+ ]
32
+
33
+ [project.scripts]
34
+ primust = "primust.cli:main"
35
+
36
+ [project.urls]
37
+ Homepage = "https://primust.com"
38
+ Documentation = "https://docs.primust.com"
39
+ Verify = "https://verify.primust.com"
40
+
41
+ [tool.hatch.build.targets.wheel]
42
+ packages = ["src/primust"]
43
+
44
+ [tool.pytest.ini_options]
45
+ testpaths = ["tests"]
@@ -0,0 +1,74 @@
1
+ """
2
+ Primust SDK
3
+ ===========
4
+ Verifiable Process Execution Credentials for regulated workflows.
5
+
6
+ Quickstart:
7
+ import primust
8
+
9
+ p = primust.Pipeline(api_key="pk_live_...", workflow_id="my-workflow")
10
+
11
+ run = p.open()
12
+ result = run.record(
13
+ check="aml_screen",
14
+ manifest_id="sha256:abc123...",
15
+ input=entity_data, # committed locally — never sent to Primust
16
+ check_result="pass",
17
+ )
18
+ # Write result.commitment_hash to your own logs (log linkage anchor)
19
+ vpec = run.close()
20
+
21
+ Privacy guarantee:
22
+ Raw input values are committed locally via Poseidon2 (or SHA-256 when
23
+ the native extension is unavailable). Only commitment hashes and bounded
24
+ normalized metadata transit to api.primust.com. Your data never leaves
25
+ your environment.
26
+
27
+ Docs: https://docs.primust.com
28
+ Verify: https://verify.primust.com
29
+ """
30
+
31
+ from primust.pipeline import (
32
+ Pipeline,
33
+ CheckSession,
34
+ ReviewSession,
35
+ ResumedContext,
36
+ ZK_IS_BLOCKING,
37
+ )
38
+ from primust.models import (
39
+ CheckResult,
40
+ GovernanceGap,
41
+ LoggerOptions,
42
+ ManifestRegistration,
43
+ PrimustLogEvent,
44
+ ProofLevel,
45
+ ProofLevelBreakdown,
46
+ RecordResult,
47
+ VPEC,
48
+ VisibilityMode,
49
+ )
50
+ from primust.models import (
51
+ CheckSession as ModelCheckSession,
52
+ ReviewSession as ModelReviewSession,
53
+ )
54
+
55
+ __version__ = "1.0.0"
56
+ __all__ = [
57
+ "Pipeline",
58
+ "CheckResult",
59
+ "CheckSession",
60
+ "GovernanceGap",
61
+ "LoggerOptions",
62
+ "ManifestRegistration",
63
+ "ModelCheckSession",
64
+ "ModelReviewSession",
65
+ "PrimustLogEvent",
66
+ "ProofLevel",
67
+ "ProofLevelBreakdown",
68
+ "RecordResult",
69
+ "ResumedContext",
70
+ "ReviewSession",
71
+ "VPEC",
72
+ "VisibilityMode",
73
+ "ZK_IS_BLOCKING",
74
+ ]
@@ -0,0 +1 @@
1
+ """Primust framework adapters."""
@@ -0,0 +1,147 @@
1
+ """
2
+ Primust CrewAI adapter — step_callback observer.
3
+
4
+ Privacy invariant: raw agent output NEVER leaves the customer environment.
5
+ Only commitment hashes (poseidon2) transit to the Primust API.
6
+
7
+ Usage:
8
+ from primust.adapters.crewai import PrimustCrewAICallback
9
+
10
+ callback = PrimustCrewAICallback(
11
+ pipeline=p,
12
+ manifest_map={
13
+ "Research Analyst": "manifest_research_v1",
14
+ "Content Writer": "manifest_output_policy_v2",
15
+ }
16
+ )
17
+
18
+ crew = Crew(
19
+ agents=[research_analyst, content_writer],
20
+ tasks=[research_task, write_task],
21
+ step_callback=callback.on_step
22
+ )
23
+ result = crew.kickoff()
24
+ vpec = p.close()
25
+
26
+ Surface declaration:
27
+ surface_type: in_process_adapter
28
+ surface_name: crewai_step_callback
29
+ observation_mode: post_action_realtime
30
+ scope_type: full_workflow
31
+ proof_ceiling: execution
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ import logging
37
+ from typing import Any
38
+
39
+ from primust import Pipeline
40
+
41
+ logger = logging.getLogger("primust.crewai")
42
+
43
+ SURFACE_DECLARATION = {
44
+ "surface_type": "in_process_adapter",
45
+ "surface_name": "crewai_step_callback",
46
+ "observation_mode": "post_action_realtime",
47
+ "scope_type": "full_workflow",
48
+ "proof_ceiling": "execution",
49
+ "surface_coverage_statement": (
50
+ "All CrewAI agent steps observed via step_callback. "
51
+ "Actions taken by agents outside the CrewAI step lifecycle are not observed."
52
+ ),
53
+ }
54
+
55
+
56
+ class PrimustCrewAICallback:
57
+ """
58
+ CrewAI step_callback observer. Attaches as crew.step_callback.
59
+
60
+ Callback NEVER raises into CrewAI — all exceptions caught, logged, continue.
61
+ Raw agent output committed locally — only commitment_hash transits.
62
+ CrewAI step_callback has no return value — Primust is purely observational.
63
+ """
64
+
65
+ def __init__(
66
+ self,
67
+ pipeline: Pipeline,
68
+ manifest_map: dict[str, str] | None = None,
69
+ ) -> None:
70
+ self.pipeline = pipeline
71
+ self.manifest_map = manifest_map or {}
72
+
73
+ def on_step(self, agent_output: Any) -> None:
74
+ """
75
+ CrewAI step_callback handler.
76
+
77
+ agent_output can be:
78
+ - AgentAction: tool call in progress
79
+ - AgentFinish: agent output ready
80
+ - dict or other: fallback handling
81
+ """
82
+ try:
83
+ self._handle_step(agent_output)
84
+ except Exception:
85
+ # CRITICAL: never raise into CrewAI
86
+ logger.exception("Primust callback error (swallowed)")
87
+
88
+ def _handle_step(self, agent_output: Any) -> None:
89
+ output_type = type(agent_output).__name__
90
+
91
+ # Extract agent role for manifest lookup
92
+ agent_role = getattr(agent_output, "agent", None)
93
+ if agent_role and hasattr(agent_role, "role"):
94
+ agent_role = agent_role.role
95
+ elif isinstance(agent_output, dict):
96
+ agent_role = agent_output.get("agent_role", "unknown")
97
+ else:
98
+ agent_role = getattr(agent_output, "role", "unknown")
99
+
100
+ manifest_id = self.manifest_map.get(str(agent_role), f"auto:{agent_role}")
101
+
102
+ # Determine if this is an action (tool call) or finish (output)
103
+ is_action = output_type == "AgentAction" or (
104
+ isinstance(agent_output, dict) and agent_output.get("type") == "action"
105
+ )
106
+ is_finish = output_type == "AgentFinish" or (
107
+ isinstance(agent_output, dict) and agent_output.get("type") == "finish"
108
+ )
109
+
110
+ # Extract input and output
111
+ if isinstance(agent_output, dict):
112
+ input_data = agent_output.get("input", agent_output.get("tool_input", str(agent_output)))
113
+ output_data = agent_output.get("output", agent_output.get("result"))
114
+ else:
115
+ input_data = getattr(agent_output, "tool_input", getattr(agent_output, "text", str(agent_output)))
116
+ output_data = getattr(agent_output, "output", getattr(agent_output, "return_values", None))
117
+
118
+ check_name = f"crewai_{output_type.lower()}"
119
+ if is_action:
120
+ tool = getattr(agent_output, "tool", None) or (
121
+ agent_output.get("tool") if isinstance(agent_output, dict) else None
122
+ )
123
+ if tool:
124
+ check_name = f"crewai_action:{tool}"
125
+
126
+ # Check if agent role is mapped
127
+ unverified = str(agent_role) not in self.manifest_map
128
+
129
+ session = self.pipeline.open_check(check_name, manifest_id)
130
+
131
+ record_kwargs: dict[str, Any] = {}
132
+ if output_data is not None:
133
+ record_kwargs["output"] = output_data
134
+
135
+ check_result = "pass"
136
+ if unverified:
137
+ check_result = "pass" # still pass, but marked unverified via metadata
138
+
139
+ self.pipeline.record(
140
+ session,
141
+ input=input_data,
142
+ check_result=check_result,
143
+ **record_kwargs,
144
+ )
145
+
146
+ def get_surface_declaration(self) -> dict[str, str]:
147
+ return dict(SURFACE_DECLARATION)