glacis 0.1.4__tar.gz → 0.2.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.
@@ -83,3 +83,5 @@ dmypy.json
83
83
  *.pem
84
84
  .env.local
85
85
  .env.*.local
86
+ examples/.env
87
+ tests/*.py
glacis-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,275 @@
1
+ Metadata-Version: 2.4
2
+ Name: glacis
3
+ Version: 0.2.0
4
+ Summary: GLACIS SDK for Python - AI Compliance Attestation
5
+ Project-URL: Homepage, https://glacis.io
6
+ Project-URL: Documentation, https://docs.glacis.io/sdk/python
7
+ Project-URL: Repository, https://github.com/Glacis-io/glacis-python
8
+ Project-URL: Issues, https://github.com/Glacis-io/glacis-python/issues
9
+ Author-email: GLACIS <sdk@glacis.io>
10
+ License-Expression: Apache-2.0
11
+ License-File: LICENSE
12
+ Keywords: ai,attestation,compliance,logging,transparency
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Security :: Cryptography
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.9
24
+ Requires-Dist: httpx>=0.25.0
25
+ Requires-Dist: pydantic>=2.0.0
26
+ Requires-Dist: pynacl>=1.5.0
27
+ Provides-Extra: all
28
+ Requires-Dist: anthropic>=0.18.0; extra == 'all'
29
+ Requires-Dist: mypy>=1.0.0; extra == 'all'
30
+ Requires-Dist: openai>=1.0.0; extra == 'all'
31
+ Requires-Dist: presidio-analyzer>=2.2.0; extra == 'all'
32
+ Requires-Dist: presidio-anonymizer>=2.2.0; extra == 'all'
33
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'all'
34
+ Requires-Dist: pytest-httpx>=0.22.0; extra == 'all'
35
+ Requires-Dist: pytest>=7.0.0; extra == 'all'
36
+ Requires-Dist: pyyaml>=6.0.0; extra == 'all'
37
+ Requires-Dist: ruff>=0.1.0; extra == 'all'
38
+ Requires-Dist: spacy>=3.5.0; extra == 'all'
39
+ Requires-Dist: torch>=2.0.0; extra == 'all'
40
+ Requires-Dist: transformers>=4.35.0; extra == 'all'
41
+ Provides-Extra: anthropic
42
+ Requires-Dist: anthropic>=0.18.0; extra == 'anthropic'
43
+ Provides-Extra: controls
44
+ Requires-Dist: presidio-analyzer>=2.2.0; extra == 'controls'
45
+ Requires-Dist: presidio-anonymizer>=2.2.0; extra == 'controls'
46
+ Requires-Dist: pyyaml>=6.0.0; extra == 'controls'
47
+ Requires-Dist: spacy>=3.5.0; extra == 'controls'
48
+ Requires-Dist: torch>=2.0.0; extra == 'controls'
49
+ Requires-Dist: transformers>=4.35.0; extra == 'controls'
50
+ Provides-Extra: dev
51
+ Requires-Dist: mypy>=1.0.0; extra == 'dev'
52
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
53
+ Requires-Dist: pytest-httpx>=0.22.0; extra == 'dev'
54
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
55
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
56
+ Provides-Extra: jailbreak
57
+ Requires-Dist: pyyaml>=6.0.0; extra == 'jailbreak'
58
+ Requires-Dist: torch>=2.0.0; extra == 'jailbreak'
59
+ Requires-Dist: transformers>=4.35.0; extra == 'jailbreak'
60
+ Provides-Extra: openai
61
+ Requires-Dist: openai>=1.0.0; extra == 'openai'
62
+ Provides-Extra: redaction
63
+ Requires-Dist: presidio-analyzer>=2.2.0; extra == 'redaction'
64
+ Requires-Dist: presidio-anonymizer>=2.2.0; extra == 'redaction'
65
+ Requires-Dist: pyyaml>=6.0.0; extra == 'redaction'
66
+ Requires-Dist: spacy>=3.5.0; extra == 'redaction'
67
+ Description-Content-Type: text/markdown
68
+
69
+ <p align="center">
70
+ <img src="assets/glacis-logo.png" alt="Glacis" width="200">
71
+ </p>
72
+
73
+ # Glacis Python SDK
74
+
75
+ **Tamper-proof audit logs for AI systems - without exposing sensitive data.**
76
+
77
+ ## The Problem
78
+
79
+ You need to prove what your AI did for compliance, audits, or legal discovery. But sending prompts and responses to a logging service exposes sensitive data (PII, PHI, trade secrets).
80
+
81
+ ## The Solution
82
+
83
+ Glacis creates cryptographic proofs of AI operations. Your data stays local - only a SHA-256 hash is sent for witnessing.
84
+
85
+ ```
86
+ Your Infrastructure Glacis Log
87
+ ┌─────────────────────┐ ┌─────────────────────┐
88
+ │ "Pt. Frodo Baggins │ │ 7a3f8b2c... │
89
+ │ has diabetes" │ ──→ │ (64-char hash) │
90
+ │ │ │ + timestamp │
91
+ │ (data stays here) │ │ + Merkle proof │
92
+ └─────────────────────┘ └─────────────────────┘
93
+ ```
94
+
95
+ Later, you can prove the hash matches your local records without revealing the data itself.
96
+
97
+ ## Installation
98
+
99
+ ```bash
100
+ pip install glacis[openai] # For OpenAI
101
+ pip install glacis[anthropic] # For Anthropic
102
+ pip install glacis[controls] # Add PII redaction + jailbreak detection
103
+ pip install glacis[all] # Everything
104
+ ```
105
+
106
+ ## Quick Start
107
+
108
+ ### Option 1: Drop-in Wrapper (Recommended)
109
+
110
+ Replace your OpenAI/Anthropic client with a wrapped version. Every API call is automatically attested.
111
+
112
+ ```python
113
+ import os
114
+ from glacis.integrations.openai import attested_openai, get_last_receipt
115
+
116
+ # Create wrapped client (offline mode - no Glacis account needed)
117
+ client = attested_openai(
118
+ openai_api_key="sk-...",
119
+ offline=True,
120
+ signing_seed=os.urandom(32),
121
+ )
122
+
123
+ # Use exactly like the normal OpenAI client
124
+ response = client.chat.completions.create(
125
+ model="gpt-4",
126
+ messages=[{"role": "user", "content": "Hello!"}]
127
+ )
128
+
129
+ # Get the attestation receipt
130
+ receipt = get_last_receipt()
131
+ print(f"Attestation ID: {receipt.attestation_id}")
132
+ ```
133
+
134
+ Works the same for Anthropic:
135
+
136
+ ```python
137
+ from glacis.integrations.anthropic import attested_anthropic, get_last_receipt
138
+
139
+ client = attested_anthropic(
140
+ anthropic_api_key="sk-ant-...",
141
+ offline=True,
142
+ signing_seed=os.urandom(32),
143
+ )
144
+ ```
145
+
146
+ ### Option 2: Direct API
147
+
148
+ For custom attestations (non-OpenAI/Anthropic, or manual control):
149
+
150
+ ```python
151
+ import os
152
+ from glacis import Glacis
153
+
154
+ glacis = Glacis(mode="offline", signing_seed=os.urandom(32))
155
+
156
+ receipt = glacis.attest(
157
+ service_id="my-ai-app",
158
+ operation_type="inference",
159
+ input={"prompt": "Summarize this..."},
160
+ output={"response": "The document..."},
161
+ )
162
+ ```
163
+
164
+ ## Adding PII Redaction
165
+
166
+ Automatically detect and redact sensitive data before it's hashed:
167
+
168
+ ```python
169
+ client = attested_openai(
170
+ openai_api_key="sk-...",
171
+ offline=True,
172
+ signing_seed=os.urandom(32),
173
+ redaction="fast", # Regex-based, or "full" for ML models
174
+ )
175
+
176
+ response = client.chat.completions.create(
177
+ model="gpt-4",
178
+ messages=[{"role": "user", "content": "My SSN is 123-45-6789"}]
179
+ )
180
+
181
+ # The attestation hash is computed on: "My SSN is [US_SSN]"
182
+ # Original text still sent to OpenAI, but redacted version is attested
183
+ ```
184
+
185
+ ## Configuration File
186
+
187
+ For persistent settings, create `glacis.yaml`:
188
+
189
+ ```yaml
190
+ attestation:
191
+ offline: true
192
+ service_id: my-ai-service
193
+
194
+ controls:
195
+ pii_phi:
196
+ enabled: true
197
+ mode: fast # "fast" (regex) or "full" (Presidio NER)
198
+
199
+ jailbreak:
200
+ enabled: true
201
+ threshold: 0.5 # Block prompt injection attempts
202
+ action: block # "warn" or "block"
203
+ ```
204
+
205
+ Then:
206
+
207
+ ```python
208
+ client = attested_openai(
209
+ openai_api_key="sk-...",
210
+ config_path="glacis.yaml",
211
+ )
212
+ ```
213
+
214
+ ## Retrieving Evidence
215
+
216
+ Full payloads are stored locally for audits:
217
+
218
+ ```python
219
+ from glacis.integrations.openai import get_last_receipt, get_evidence
220
+
221
+ receipt = get_last_receipt()
222
+ evidence = get_evidence(receipt.attestation_id)
223
+
224
+ print(evidence["input"]) # Original input
225
+ print(evidence["output"]) # Original output
226
+ print(evidence["control_plane_results"]) # PII/jailbreak results
227
+ ```
228
+
229
+ Evidence is stored in `~/.glacis/receipts.db` (SQLite).
230
+
231
+ ## Online vs Offline Mode
232
+
233
+ | Feature | Offline | Online |
234
+ |---------|---------|--------|
235
+ | Requires Glacis account | No | Yes |
236
+ | Signing | Local Ed25519 | Glacis witness |
237
+ | Third-party verifiable | No | Yes (Merkle proofs) |
238
+ | Use case | Development, air-gapped | Production, audits |
239
+
240
+ To use online mode:
241
+
242
+ ```python
243
+ client = attested_openai(
244
+ openai_api_key="sk-...",
245
+ glacis_api_key="glsk_live_...", # Get at glacis.io
246
+ )
247
+ ```
248
+
249
+ ## What Gets Sent to Glacis?
250
+
251
+ | Data | Sent? |
252
+ |------|-------|
253
+ | Your prompts | No (hash only) |
254
+ | Model responses | No (hash only) |
255
+ | API keys | No |
256
+ | service_id, operation_type | Yes |
257
+ | Timestamps | Yes |
258
+
259
+ ## CLI
260
+
261
+ Verify a receipt:
262
+
263
+ ```bash
264
+ python -m glacis verify receipt.json
265
+ ```
266
+
267
+ ## Security
268
+
269
+ - **Hashing**: SHA-256 with RFC 8785 canonical JSON (cross-runtime compatible)
270
+ - **Signing**: Ed25519 via PyNaCl (libsodium)
271
+ - **Online mode**: Merkle tree inclusion proofs (RFC 6962)
272
+
273
+ ## License
274
+
275
+ Apache 2.0
glacis-0.2.0/README.md ADDED
@@ -0,0 +1,207 @@
1
+ <p align="center">
2
+ <img src="assets/glacis-logo.png" alt="Glacis" width="200">
3
+ </p>
4
+
5
+ # Glacis Python SDK
6
+
7
+ **Tamper-proof audit logs for AI systems - without exposing sensitive data.**
8
+
9
+ ## The Problem
10
+
11
+ You need to prove what your AI did for compliance, audits, or legal discovery. But sending prompts and responses to a logging service exposes sensitive data (PII, PHI, trade secrets).
12
+
13
+ ## The Solution
14
+
15
+ Glacis creates cryptographic proofs of AI operations. Your data stays local - only a SHA-256 hash is sent for witnessing.
16
+
17
+ ```
18
+ Your Infrastructure Glacis Log
19
+ ┌─────────────────────┐ ┌─────────────────────┐
20
+ │ "Pt. Frodo Baggins │ │ 7a3f8b2c... │
21
+ │ has diabetes" │ ──→ │ (64-char hash) │
22
+ │ │ │ + timestamp │
23
+ │ (data stays here) │ │ + Merkle proof │
24
+ └─────────────────────┘ └─────────────────────┘
25
+ ```
26
+
27
+ Later, you can prove the hash matches your local records without revealing the data itself.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install glacis[openai] # For OpenAI
33
+ pip install glacis[anthropic] # For Anthropic
34
+ pip install glacis[controls] # Add PII redaction + jailbreak detection
35
+ pip install glacis[all] # Everything
36
+ ```
37
+
38
+ ## Quick Start
39
+
40
+ ### Option 1: Drop-in Wrapper (Recommended)
41
+
42
+ Replace your OpenAI/Anthropic client with a wrapped version. Every API call is automatically attested.
43
+
44
+ ```python
45
+ import os
46
+ from glacis.integrations.openai import attested_openai, get_last_receipt
47
+
48
+ # Create wrapped client (offline mode - no Glacis account needed)
49
+ client = attested_openai(
50
+ openai_api_key="sk-...",
51
+ offline=True,
52
+ signing_seed=os.urandom(32),
53
+ )
54
+
55
+ # Use exactly like the normal OpenAI client
56
+ response = client.chat.completions.create(
57
+ model="gpt-4",
58
+ messages=[{"role": "user", "content": "Hello!"}]
59
+ )
60
+
61
+ # Get the attestation receipt
62
+ receipt = get_last_receipt()
63
+ print(f"Attestation ID: {receipt.attestation_id}")
64
+ ```
65
+
66
+ Works the same for Anthropic:
67
+
68
+ ```python
69
+ from glacis.integrations.anthropic import attested_anthropic, get_last_receipt
70
+
71
+ client = attested_anthropic(
72
+ anthropic_api_key="sk-ant-...",
73
+ offline=True,
74
+ signing_seed=os.urandom(32),
75
+ )
76
+ ```
77
+
78
+ ### Option 2: Direct API
79
+
80
+ For custom attestations (non-OpenAI/Anthropic, or manual control):
81
+
82
+ ```python
83
+ import os
84
+ from glacis import Glacis
85
+
86
+ glacis = Glacis(mode="offline", signing_seed=os.urandom(32))
87
+
88
+ receipt = glacis.attest(
89
+ service_id="my-ai-app",
90
+ operation_type="inference",
91
+ input={"prompt": "Summarize this..."},
92
+ output={"response": "The document..."},
93
+ )
94
+ ```
95
+
96
+ ## Adding PII Redaction
97
+
98
+ Automatically detect and redact sensitive data before it's hashed:
99
+
100
+ ```python
101
+ client = attested_openai(
102
+ openai_api_key="sk-...",
103
+ offline=True,
104
+ signing_seed=os.urandom(32),
105
+ redaction="fast", # Regex-based, or "full" for ML models
106
+ )
107
+
108
+ response = client.chat.completions.create(
109
+ model="gpt-4",
110
+ messages=[{"role": "user", "content": "My SSN is 123-45-6789"}]
111
+ )
112
+
113
+ # The attestation hash is computed on: "My SSN is [US_SSN]"
114
+ # Original text still sent to OpenAI, but redacted version is attested
115
+ ```
116
+
117
+ ## Configuration File
118
+
119
+ For persistent settings, create `glacis.yaml`:
120
+
121
+ ```yaml
122
+ attestation:
123
+ offline: true
124
+ service_id: my-ai-service
125
+
126
+ controls:
127
+ pii_phi:
128
+ enabled: true
129
+ mode: fast # "fast" (regex) or "full" (Presidio NER)
130
+
131
+ jailbreak:
132
+ enabled: true
133
+ threshold: 0.5 # Block prompt injection attempts
134
+ action: block # "warn" or "block"
135
+ ```
136
+
137
+ Then:
138
+
139
+ ```python
140
+ client = attested_openai(
141
+ openai_api_key="sk-...",
142
+ config_path="glacis.yaml",
143
+ )
144
+ ```
145
+
146
+ ## Retrieving Evidence
147
+
148
+ Full payloads are stored locally for audits:
149
+
150
+ ```python
151
+ from glacis.integrations.openai import get_last_receipt, get_evidence
152
+
153
+ receipt = get_last_receipt()
154
+ evidence = get_evidence(receipt.attestation_id)
155
+
156
+ print(evidence["input"]) # Original input
157
+ print(evidence["output"]) # Original output
158
+ print(evidence["control_plane_results"]) # PII/jailbreak results
159
+ ```
160
+
161
+ Evidence is stored in `~/.glacis/receipts.db` (SQLite).
162
+
163
+ ## Online vs Offline Mode
164
+
165
+ | Feature | Offline | Online |
166
+ |---------|---------|--------|
167
+ | Requires Glacis account | No | Yes |
168
+ | Signing | Local Ed25519 | Glacis witness |
169
+ | Third-party verifiable | No | Yes (Merkle proofs) |
170
+ | Use case | Development, air-gapped | Production, audits |
171
+
172
+ To use online mode:
173
+
174
+ ```python
175
+ client = attested_openai(
176
+ openai_api_key="sk-...",
177
+ glacis_api_key="glsk_live_...", # Get at glacis.io
178
+ )
179
+ ```
180
+
181
+ ## What Gets Sent to Glacis?
182
+
183
+ | Data | Sent? |
184
+ |------|-------|
185
+ | Your prompts | No (hash only) |
186
+ | Model responses | No (hash only) |
187
+ | API keys | No |
188
+ | service_id, operation_type | Yes |
189
+ | Timestamps | Yes |
190
+
191
+ ## CLI
192
+
193
+ Verify a receipt:
194
+
195
+ ```bash
196
+ python -m glacis verify receipt.json
197
+ ```
198
+
199
+ ## Security
200
+
201
+ - **Hashing**: SHA-256 with RFC 8785 canonical JSON (cross-runtime compatible)
202
+ - **Signing**: Ed25519 via PyNaCl (libsodium)
203
+ - **Online mode**: Merkle tree inclusion proofs (RFC 6962)
204
+
205
+ ## License
206
+
207
+ Apache 2.0
@@ -37,6 +37,13 @@ Streaming Example:
37
37
  ... })
38
38
  >>> await session.attest_chunk(input=audio_chunk, output=transcript)
39
39
  >>> receipt = await session.end(metadata={"duration": "00:05:23"})
40
+
41
+ Controls Example:
42
+ >>> from glacis.controls import ControlsRunner, PIIControl, JailbreakControl
43
+ >>> from glacis.config import load_config
44
+ >>> cfg = load_config() # Loads glacis.yaml
45
+ >>> runner = ControlsRunner(cfg.controls)
46
+ >>> results = runner.run("Patient SSN: 123-45-6789")
40
47
  """
41
48
 
42
49
  from glacis.client import AsyncGlacis, Glacis, GlacisMode
@@ -44,26 +51,56 @@ from glacis.crypto import canonical_json, hash_payload
44
51
  from glacis.models import (
45
52
  AttestInput,
46
53
  AttestReceipt,
54
+ ControlExecution,
55
+ ControlPlaneAttestation,
56
+ ControlStatus,
57
+ ControlType,
58
+ Determination,
59
+ GlacisApiError,
47
60
  GlacisConfig,
61
+ JailbreakSummary,
48
62
  LogEntry,
49
63
  LogQueryParams,
50
64
  LogQueryResult,
51
65
  MerkleInclusionProof,
66
+ ModelInfo,
52
67
  OfflineAttestReceipt,
53
68
  OfflineVerifyResult,
69
+ PiiPhiSummary,
70
+ PolicyContext,
71
+ PolicyScope,
72
+ SafetyScores,
73
+ SamplingDecision,
74
+ SamplingMetadata,
54
75
  SignedTreeHead,
55
76
  VerifyResult,
56
77
  )
57
78
  from glacis.storage import ReceiptStorage
58
79
  from glacis.streaming import SessionContext, SessionReceipt, StreamingSession
59
80
 
60
- __version__ = "0.2.0"
81
+ # Controls module (optional dependencies for individual controls)
82
+ try:
83
+ from glacis.controls import ( # noqa: F401
84
+ BaseControl,
85
+ ControlResult,
86
+ ControlsRunner,
87
+ JailbreakControl,
88
+ PIIControl,
89
+ )
90
+
91
+ _CONTROLS_AVAILABLE = True
92
+ except ImportError:
93
+ _CONTROLS_AVAILABLE = False
94
+
95
+ __version__ = "0.3.0"
61
96
 
62
97
  __all__ = [
63
98
  # Main classes
64
99
  "Glacis",
65
100
  "AsyncGlacis",
66
101
  "GlacisMode",
102
+ # Exceptions
103
+ "GlacisApiError",
67
104
  # Streaming
68
105
  "StreamingSession",
69
106
  "SessionContext",
@@ -82,7 +119,31 @@ __all__ = [
82
119
  "LogEntry",
83
120
  "MerkleInclusionProof",
84
121
  "SignedTreeHead",
122
+ # Control Plane
123
+ "ControlPlaneAttestation",
124
+ "PolicyContext",
125
+ "PolicyScope",
126
+ "ModelInfo",
127
+ "Determination",
128
+ "ControlExecution",
129
+ "ControlType",
130
+ "ControlStatus",
131
+ "SafetyScores",
132
+ "PiiPhiSummary",
133
+ "JailbreakSummary",
134
+ "SamplingMetadata",
135
+ "SamplingDecision",
85
136
  # Crypto utilities
86
137
  "canonical_json",
87
138
  "hash_payload",
88
139
  ]
140
+
141
+ # Add controls exports if available
142
+ if _CONTROLS_AVAILABLE:
143
+ __all__.extend([
144
+ "BaseControl",
145
+ "ControlResult",
146
+ "ControlsRunner",
147
+ "PIIControl",
148
+ "JailbreakControl",
149
+ ])
@@ -0,0 +1,11 @@
1
+ """
2
+ Glacis CLI
3
+
4
+ Usage:
5
+ python -m glacis verify <receipt.json>
6
+ """
7
+
8
+ from glacis.verify import main
9
+
10
+ if __name__ == "__main__":
11
+ main()