lebrel-encrypted 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.
Files changed (33) hide show
  1. lebrel_encrypted-0.2.0/.gitignore +6 -0
  2. lebrel_encrypted-0.2.0/LICENSE +21 -0
  3. lebrel_encrypted-0.2.0/PKG-INFO +248 -0
  4. lebrel_encrypted-0.2.0/README.md +221 -0
  5. lebrel_encrypted-0.2.0/THIRD_PARTY_NOTICES.md +17 -0
  6. lebrel_encrypted-0.2.0/examples/chat.py +11 -0
  7. lebrel_encrypted-0.2.0/examples/stream.py +13 -0
  8. lebrel_encrypted-0.2.0/pyproject.toml +48 -0
  9. lebrel_encrypted-0.2.0/src/lebrel_encrypted/__init__.py +17 -0
  10. lebrel_encrypted-0.2.0/src/lebrel_encrypted/__main__.py +3 -0
  11. lebrel_encrypted-0.2.0/src/lebrel_encrypted/_vendor/__init__.py +1 -0
  12. lebrel_encrypted-0.2.0/src/lebrel_encrypted/_vendor/ehbp/LICENSE +22 -0
  13. lebrel_encrypted-0.2.0/src/lebrel_encrypted/_vendor/ehbp/__init__.py +54 -0
  14. lebrel_encrypted-0.2.0/src/lebrel_encrypted/_vendor/ehbp/_http.py +78 -0
  15. lebrel_encrypted-0.2.0/src/lebrel_encrypted/_vendor/ehbp/client.py +429 -0
  16. lebrel_encrypted-0.2.0/src/lebrel_encrypted/_vendor/ehbp/derive.py +172 -0
  17. lebrel_encrypted-0.2.0/src/lebrel_encrypted/_vendor/ehbp/errors.py +38 -0
  18. lebrel_encrypted-0.2.0/src/lebrel_encrypted/_vendor/ehbp/identity.py +154 -0
  19. lebrel_encrypted-0.2.0/src/lebrel_encrypted/_vendor/ehbp/protocol.py +33 -0
  20. lebrel_encrypted-0.2.0/src/lebrel_encrypted/_vendor/ehbp/py.typed +1 -0
  21. lebrel_encrypted-0.2.0/src/lebrel_encrypted/_vendor/ehbp/session.py +122 -0
  22. lebrel_encrypted-0.2.0/src/lebrel_encrypted/_vendor/ehbp/transport.py +277 -0
  23. lebrel_encrypted-0.2.0/src/lebrel_encrypted/cli.py +45 -0
  24. lebrel_encrypted-0.2.0/src/lebrel_encrypted/client.py +541 -0
  25. lebrel_encrypted-0.2.0/src/lebrel_encrypted/proof.py +211 -0
  26. lebrel_encrypted-0.2.0/src/lebrel_encrypted/proxy.py +280 -0
  27. lebrel_encrypted-0.2.0/tests/interop/go.mod +11 -0
  28. lebrel_encrypted-0.2.0/tests/interop/go.sum +16 -0
  29. lebrel_encrypted-0.2.0/tests/interop/main.go +152 -0
  30. lebrel_encrypted-0.2.0/tests/test_cli.py +35 -0
  31. lebrel_encrypted-0.2.0/tests/test_client.py +305 -0
  32. lebrel_encrypted-0.2.0/tests/test_proof.py +101 -0
  33. lebrel_encrypted-0.2.0/tests/test_proxy.py +179 -0
@@ -0,0 +1,6 @@
1
+ .venv/
2
+ .install-smoke/
3
+ __pycache__/
4
+ .pytest_cache/
5
+ dist/
6
+ *.egg-info/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lebrel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,248 @@
1
+ Metadata-Version: 2.4
2
+ Name: lebrel-encrypted
3
+ Version: 0.2.0
4
+ Summary: Verified encrypted Python client for Lebrel DeepSeek V4 Flash Uncensored
5
+ Project-URL: Homepage, https://lebrel.ai
6
+ Project-URL: Repository, https://github.com/LEBREL-AI/lebrel-encrypted-python
7
+ Project-URL: Documentation, https://lebrel.ai/docs
8
+ Author: Lebrel
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: encrypted,hpke,lebrel,llm,openai-compatible,proof-of-edition,receipts
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Topic :: Security :: Cryptography
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.9
21
+ Requires-Dist: cryptography==46.0.7
22
+ Requires-Dist: httpx==0.28.1
23
+ Requires-Dist: pyhpke==0.6.3
24
+ Provides-Extra: test
25
+ Requires-Dist: pytest==8.4.2; extra == 'test'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # Lebrel encrypted Python client
29
+
30
+ Questions are encrypted locally for the verified runtime. Answers are decrypted
31
+ locally. This client serves **`lebrel/deepseek-v4-flash-uncensored`** and verifies
32
+ Lebrel's pinned Ed25519 signature before encrypting anything for a runtime key.
33
+
34
+ Requires Python 3.9 or newer.
35
+
36
+ ```sh
37
+ python -m pip install https://lebrel.ai/sdk/lebrel-encrypted-python.zip
38
+ export LEBREL_API_KEY='your-Lebrel-API-key'
39
+ ```
40
+
41
+ The same archive can be reviewed, extracted and installed locally with
42
+ `python -m pip install .`. The import is `lebrel_encrypted`; there is no need for
43
+ the OpenAI package. The interface accepts OpenAI chat-completion parameters and
44
+ returns dictionaries.
45
+
46
+ ```python
47
+ from lebrel_encrypted import Lebrel
48
+
49
+ with Lebrel() as client: # reads LEBREL_API_KEY
50
+ response = client.chat.completions.create(
51
+ messages=[{"role": "user", "content": "Write the opening of a story."}],
52
+ max_tokens=256,
53
+ )
54
+ print(response["choices"][0]["message"]["content"])
55
+ ```
56
+
57
+ The model defaults to the exact Flash edition above. Another model ID is
58
+ rejected locally. You can also pass `Lebrel(api_key="...")` or use the shorter
59
+ `client.create(...)` interface.
60
+
61
+ ## OpenCode and other OpenAI-compatible applications
62
+
63
+ Run the local adapter in a terminal:
64
+
65
+ ```sh
66
+ python -m lebrel_encrypted.proxy --port 11437
67
+ ```
68
+
69
+ Configure your application with:
70
+
71
+ - Base URL: `http://127.0.0.1:11437/v1`
72
+ - API key: your usual Lebrel API key
73
+ - Model: `lebrel/deepseek-v4-flash-uncensored`
74
+
75
+ The adapter receives normal OpenAI JSON and SSE on loopback, then uses this SDK
76
+ to encrypt requests to the public API and decrypt responses. It binds only to
77
+ `127.0.0.1`; there is no network-bind option. The local hop is plaintext, so other
78
+ processes with sufficient access on your computer remain within your trust
79
+ boundary. Requests require `Authorization: Bearer ...` and JSON bodies with
80
+ `Content-Length`. Browser-origin requests are rejected.
81
+
82
+ The adapter does not persist API keys or conversations. By default the key comes
83
+ from each application's request. Optional standalone use can read the key from
84
+ `--api-key-file /private/path/to/key`; an incoming Bearer key takes precedence.
85
+ The file must contain exactly one key. Do not put the key itself on the command
86
+ line. The authenticated `/v1/models` route forwards metadata only for the exact
87
+ Flash model. Stream disconnects close the encrypted upstream response.
88
+
89
+ `GET http://127.0.0.1:11437/healthz` requires no key and identifies the local
90
+ adapter, SDK version, upstream, signing-key ID and loaded proxy source hash.
91
+ Startup emits one status line; request bodies and credentials are never logged.
92
+ The proxy runs in the foreground until stopped. Installing this package does
93
+ not automatically start it or modify application configuration.
94
+
95
+ The console entry point `lebrel-encrypted proxy --port 11437` is equivalent.
96
+
97
+ ## Terminal requests
98
+
99
+ Put a normal OpenAI completion request in a local JSON file, then:
100
+
101
+ ```sh
102
+ export LEBREL_API_KEY='your-Lebrel-API-key'
103
+ lebrel-encrypted infer < request.json
104
+ ```
105
+
106
+ Equivalent: `python -m lebrel_encrypted infer < request.json`. The command reads
107
+ JSON from stdin and the key from the environment. It writes a JSON response, or
108
+ SSE when the input sets `"stream": true`. It never silently switches to the plain
109
+ public API. On failure it exits nonzero and prints a generic error to stderr.
110
+
111
+ ## Streaming and cancellation
112
+
113
+ ```python
114
+ from lebrel_encrypted import Lebrel
115
+
116
+ with Lebrel() as client:
117
+ with client.chat.completions.create(
118
+ messages=[{"role": "user", "content": "Explain how HPKE works."}],
119
+ max_tokens=512,
120
+ stream=True,
121
+ ) as stream:
122
+ for chunk in stream:
123
+ for choice in chunk.get("choices", []):
124
+ delta = choice.get("delta", {})
125
+ print(delta.get("content") or "", end="", flush=True)
126
+ assert stream.completed
127
+ ```
128
+
129
+ Fully exhaust the stream to verify its completion. It requires a final
130
+ `finish_reason` for every observed choice, then an authenticated `[DONE]`, and
131
+ rejects subsequent data or truncated encrypted frames. Partial output may have
132
+ been displayed before a later error: do not treat it as a completed answer.
133
+ `stream.close()` or leaving the context closes the HTTP response and propagates
134
+ cancellation. Closing early leaves `stream.completed` false.
135
+
136
+ ## Receipts: proof of what answered
137
+
138
+ The runtime signs a receipt for every answer: the SHA-256 of your request exactly as
139
+ sent, the SHA-256 of the answer, the serving manifest it ran under (which weights,
140
+ precision, engine and tokenizer) and the token counts. The client checks it for you,
141
+ with the same checks as https://lebrel.ai/verify:
142
+
143
+ ```python
144
+ from lebrel_encrypted import Lebrel
145
+
146
+ with Lebrel() as client:
147
+ answer = client.chat.completions.create(messages=[{"role": "user", "content": "Name one color."}])
148
+ receipt = client.receipt(answer)
149
+ assert receipt.verified
150
+ print(receipt.request_id, receipt.manifest.payload["weights"]["revision"])
151
+ ```
152
+
153
+ `receipt.checks` lists every step: the pinned signing key, the signature, the request
154
+ id, the digest of your request, the digest of the answer and the serving manifest the
155
+ receipt names, fetched and checked at the same time (`manifest=False` skips that
156
+ fetch). A stream is checked the same way once fully consumed: `client.receipt(stream)`.
157
+ Any receipt can be looked up later by its request id while the API keeps it:
158
+ `client.receipt("…")`; `client.manifest()` returns the current serving manifest,
159
+ checked. Every completion carries `request_id`, `request_sha256` and
160
+ `response_sha256`; the dictionary itself is unchanged.
161
+
162
+ The local proxy sends `X-Lebrel-Request-Id` on every answer and, on non-streaming
163
+ answers, `Proof-Of-Edition-Receipt` (the signed receipt, base64) as the API sent it.
164
+ `GET /v1/receipts/{id}` on the proxy returns the receipt with
165
+ `X-Lebrel-Receipt-Check: verified` once the proxy has checked its signature and the
166
+ serving manifest; the digests are yours to check against your text.
167
+
168
+ ## Errors and retries
169
+
170
+ ```python
171
+ from lebrel_encrypted import APIError, EncryptionError, Lebrel, StreamError, TransportError
172
+
173
+ try:
174
+ with Lebrel() as client:
175
+ response = client.create(messages=[{"role": "user", "content": "Hello"}])
176
+ except APIError as error:
177
+ print(error.status_code, error.code)
178
+ except (EncryptionError, StreamError, TransportError) as error:
179
+ print(str(error))
180
+ ```
181
+
182
+ The client never automatically retries inference. A connection failure may occur
183
+ after execution has begun, so a fresh application retry can incur a new charge.
184
+ Each explicit request gets a new UUID in `X-Lebrel-Request-Id` and a fresh HPKE
185
+ encapsulation. Redirects, plaintext successful responses and fallback to the
186
+ plain API are prohibited. Error messages omit conversation content and ignore
187
+ untrusted plaintext error bodies.
188
+
189
+ The default timeout is 900 seconds for cold starts; configure `Lebrel(timeout=...)`
190
+ when needed. Environment HTTP proxy settings are disabled. HTTPS certificate
191
+ verification remains enabled.
192
+
193
+ ## What is verified
194
+
195
+ The client retrieves `https://api.lebrel.ai/.well-known/lebrel-encryption` without
196
+ sending the API key, verifies the Ed25519 signature on the original decoded
197
+ payload bytes and checks the schema, exact model, signing-key digest, HPKE
198
+ configuration digest, freshness and validity interval (at most ten minutes).
199
+ The request body is encrypted using the verified HPKE configuration. API
200
+ credentials and routing metadata are still HTTP headers protected by HTTPS.
201
+
202
+ Pinned Ed25519 public key (raw bytes, base64):
203
+
204
+ ```text
205
+ beFZtSwt6FnlhIYbX636n7w3/gpaASIkRnIMx52XJwk=
206
+ ```
207
+
208
+ SHA-256:
209
+
210
+ ```text
211
+ 8f72beb9680a0f1911dce59d1fc103a0a89039998003715d35422d3020e5292d
212
+ ```
213
+
214
+ The runtime signature authenticates Lebrel's encryption key. It is not hardware
215
+ attestation or proof that the operator cannot access the model process's memory.
216
+ Questions and answers are decrypted inside the inference runtime. The current
217
+ standard Modal runtime remains administered by Lebrel.
218
+
219
+ ## Reused implementation and dependencies
220
+
221
+ The EHBP Python source is vendored **without changes** from the official Tinfoil
222
+ repository at commit `6ae53f8b6270834ca1cb8c29f8d2a79e372bc4b0`, under
223
+ `src/lebrel_encrypted/_vendor/ehbp`. Relative imports remain unchanged. Its MIT
224
+ license accompanies the source. The wrapper supplies application checks; HPKE,
225
+ HKDF and response encryption/decryption use upstream EHBP and its dependencies.
226
+
227
+ Exact direct dependency versions:
228
+
229
+ - `pyhpke==0.6.3`
230
+ - `cryptography==46.0.7` (latest compatible 46.x patch selected for pyhpke's `<47` bound)
231
+ - `httpx==0.28.1`
232
+
233
+ Tests use `pytest==8.4.2`; builds use `hatchling==1.27.0`. The archive includes
234
+ `VENDORED_SHA256.json` so reviewers can compare vendored files to the pinned
235
+ upstream revision.
236
+
237
+ ## Offline verification
238
+
239
+ ```sh
240
+ python -m pip install '.[test]'
241
+ python -m pytest
242
+ ```
243
+
244
+ The interoperability fixture builds a local Go EHBP server with the same pinned
245
+ upstream revision (`GOTOOLCHAIN=go1.26.0`) and verifies real Python-to-Go encrypted
246
+ requests, JSON responses, streaming, truncation and failure handling. No GPU,
247
+ production credentials or production API calls are used. Dependency and Go
248
+ toolchain downloads are needed once unless already cached.
@@ -0,0 +1,221 @@
1
+ # Lebrel encrypted Python client
2
+
3
+ Questions are encrypted locally for the verified runtime. Answers are decrypted
4
+ locally. This client serves **`lebrel/deepseek-v4-flash-uncensored`** and verifies
5
+ Lebrel's pinned Ed25519 signature before encrypting anything for a runtime key.
6
+
7
+ Requires Python 3.9 or newer.
8
+
9
+ ```sh
10
+ python -m pip install https://lebrel.ai/sdk/lebrel-encrypted-python.zip
11
+ export LEBREL_API_KEY='your-Lebrel-API-key'
12
+ ```
13
+
14
+ The same archive can be reviewed, extracted and installed locally with
15
+ `python -m pip install .`. The import is `lebrel_encrypted`; there is no need for
16
+ the OpenAI package. The interface accepts OpenAI chat-completion parameters and
17
+ returns dictionaries.
18
+
19
+ ```python
20
+ from lebrel_encrypted import Lebrel
21
+
22
+ with Lebrel() as client: # reads LEBREL_API_KEY
23
+ response = client.chat.completions.create(
24
+ messages=[{"role": "user", "content": "Write the opening of a story."}],
25
+ max_tokens=256,
26
+ )
27
+ print(response["choices"][0]["message"]["content"])
28
+ ```
29
+
30
+ The model defaults to the exact Flash edition above. Another model ID is
31
+ rejected locally. You can also pass `Lebrel(api_key="...")` or use the shorter
32
+ `client.create(...)` interface.
33
+
34
+ ## OpenCode and other OpenAI-compatible applications
35
+
36
+ Run the local adapter in a terminal:
37
+
38
+ ```sh
39
+ python -m lebrel_encrypted.proxy --port 11437
40
+ ```
41
+
42
+ Configure your application with:
43
+
44
+ - Base URL: `http://127.0.0.1:11437/v1`
45
+ - API key: your usual Lebrel API key
46
+ - Model: `lebrel/deepseek-v4-flash-uncensored`
47
+
48
+ The adapter receives normal OpenAI JSON and SSE on loopback, then uses this SDK
49
+ to encrypt requests to the public API and decrypt responses. It binds only to
50
+ `127.0.0.1`; there is no network-bind option. The local hop is plaintext, so other
51
+ processes with sufficient access on your computer remain within your trust
52
+ boundary. Requests require `Authorization: Bearer ...` and JSON bodies with
53
+ `Content-Length`. Browser-origin requests are rejected.
54
+
55
+ The adapter does not persist API keys or conversations. By default the key comes
56
+ from each application's request. Optional standalone use can read the key from
57
+ `--api-key-file /private/path/to/key`; an incoming Bearer key takes precedence.
58
+ The file must contain exactly one key. Do not put the key itself on the command
59
+ line. The authenticated `/v1/models` route forwards metadata only for the exact
60
+ Flash model. Stream disconnects close the encrypted upstream response.
61
+
62
+ `GET http://127.0.0.1:11437/healthz` requires no key and identifies the local
63
+ adapter, SDK version, upstream, signing-key ID and loaded proxy source hash.
64
+ Startup emits one status line; request bodies and credentials are never logged.
65
+ The proxy runs in the foreground until stopped. Installing this package does
66
+ not automatically start it or modify application configuration.
67
+
68
+ The console entry point `lebrel-encrypted proxy --port 11437` is equivalent.
69
+
70
+ ## Terminal requests
71
+
72
+ Put a normal OpenAI completion request in a local JSON file, then:
73
+
74
+ ```sh
75
+ export LEBREL_API_KEY='your-Lebrel-API-key'
76
+ lebrel-encrypted infer < request.json
77
+ ```
78
+
79
+ Equivalent: `python -m lebrel_encrypted infer < request.json`. The command reads
80
+ JSON from stdin and the key from the environment. It writes a JSON response, or
81
+ SSE when the input sets `"stream": true`. It never silently switches to the plain
82
+ public API. On failure it exits nonzero and prints a generic error to stderr.
83
+
84
+ ## Streaming and cancellation
85
+
86
+ ```python
87
+ from lebrel_encrypted import Lebrel
88
+
89
+ with Lebrel() as client:
90
+ with client.chat.completions.create(
91
+ messages=[{"role": "user", "content": "Explain how HPKE works."}],
92
+ max_tokens=512,
93
+ stream=True,
94
+ ) as stream:
95
+ for chunk in stream:
96
+ for choice in chunk.get("choices", []):
97
+ delta = choice.get("delta", {})
98
+ print(delta.get("content") or "", end="", flush=True)
99
+ assert stream.completed
100
+ ```
101
+
102
+ Fully exhaust the stream to verify its completion. It requires a final
103
+ `finish_reason` for every observed choice, then an authenticated `[DONE]`, and
104
+ rejects subsequent data or truncated encrypted frames. Partial output may have
105
+ been displayed before a later error: do not treat it as a completed answer.
106
+ `stream.close()` or leaving the context closes the HTTP response and propagates
107
+ cancellation. Closing early leaves `stream.completed` false.
108
+
109
+ ## Receipts: proof of what answered
110
+
111
+ The runtime signs a receipt for every answer: the SHA-256 of your request exactly as
112
+ sent, the SHA-256 of the answer, the serving manifest it ran under (which weights,
113
+ precision, engine and tokenizer) and the token counts. The client checks it for you,
114
+ with the same checks as https://lebrel.ai/verify:
115
+
116
+ ```python
117
+ from lebrel_encrypted import Lebrel
118
+
119
+ with Lebrel() as client:
120
+ answer = client.chat.completions.create(messages=[{"role": "user", "content": "Name one color."}])
121
+ receipt = client.receipt(answer)
122
+ assert receipt.verified
123
+ print(receipt.request_id, receipt.manifest.payload["weights"]["revision"])
124
+ ```
125
+
126
+ `receipt.checks` lists every step: the pinned signing key, the signature, the request
127
+ id, the digest of your request, the digest of the answer and the serving manifest the
128
+ receipt names, fetched and checked at the same time (`manifest=False` skips that
129
+ fetch). A stream is checked the same way once fully consumed: `client.receipt(stream)`.
130
+ Any receipt can be looked up later by its request id while the API keeps it:
131
+ `client.receipt("…")`; `client.manifest()` returns the current serving manifest,
132
+ checked. Every completion carries `request_id`, `request_sha256` and
133
+ `response_sha256`; the dictionary itself is unchanged.
134
+
135
+ The local proxy sends `X-Lebrel-Request-Id` on every answer and, on non-streaming
136
+ answers, `Proof-Of-Edition-Receipt` (the signed receipt, base64) as the API sent it.
137
+ `GET /v1/receipts/{id}` on the proxy returns the receipt with
138
+ `X-Lebrel-Receipt-Check: verified` once the proxy has checked its signature and the
139
+ serving manifest; the digests are yours to check against your text.
140
+
141
+ ## Errors and retries
142
+
143
+ ```python
144
+ from lebrel_encrypted import APIError, EncryptionError, Lebrel, StreamError, TransportError
145
+
146
+ try:
147
+ with Lebrel() as client:
148
+ response = client.create(messages=[{"role": "user", "content": "Hello"}])
149
+ except APIError as error:
150
+ print(error.status_code, error.code)
151
+ except (EncryptionError, StreamError, TransportError) as error:
152
+ print(str(error))
153
+ ```
154
+
155
+ The client never automatically retries inference. A connection failure may occur
156
+ after execution has begun, so a fresh application retry can incur a new charge.
157
+ Each explicit request gets a new UUID in `X-Lebrel-Request-Id` and a fresh HPKE
158
+ encapsulation. Redirects, plaintext successful responses and fallback to the
159
+ plain API are prohibited. Error messages omit conversation content and ignore
160
+ untrusted plaintext error bodies.
161
+
162
+ The default timeout is 900 seconds for cold starts; configure `Lebrel(timeout=...)`
163
+ when needed. Environment HTTP proxy settings are disabled. HTTPS certificate
164
+ verification remains enabled.
165
+
166
+ ## What is verified
167
+
168
+ The client retrieves `https://api.lebrel.ai/.well-known/lebrel-encryption` without
169
+ sending the API key, verifies the Ed25519 signature on the original decoded
170
+ payload bytes and checks the schema, exact model, signing-key digest, HPKE
171
+ configuration digest, freshness and validity interval (at most ten minutes).
172
+ The request body is encrypted using the verified HPKE configuration. API
173
+ credentials and routing metadata are still HTTP headers protected by HTTPS.
174
+
175
+ Pinned Ed25519 public key (raw bytes, base64):
176
+
177
+ ```text
178
+ beFZtSwt6FnlhIYbX636n7w3/gpaASIkRnIMx52XJwk=
179
+ ```
180
+
181
+ SHA-256:
182
+
183
+ ```text
184
+ 8f72beb9680a0f1911dce59d1fc103a0a89039998003715d35422d3020e5292d
185
+ ```
186
+
187
+ The runtime signature authenticates Lebrel's encryption key. It is not hardware
188
+ attestation or proof that the operator cannot access the model process's memory.
189
+ Questions and answers are decrypted inside the inference runtime. The current
190
+ standard Modal runtime remains administered by Lebrel.
191
+
192
+ ## Reused implementation and dependencies
193
+
194
+ The EHBP Python source is vendored **without changes** from the official Tinfoil
195
+ repository at commit `6ae53f8b6270834ca1cb8c29f8d2a79e372bc4b0`, under
196
+ `src/lebrel_encrypted/_vendor/ehbp`. Relative imports remain unchanged. Its MIT
197
+ license accompanies the source. The wrapper supplies application checks; HPKE,
198
+ HKDF and response encryption/decryption use upstream EHBP and its dependencies.
199
+
200
+ Exact direct dependency versions:
201
+
202
+ - `pyhpke==0.6.3`
203
+ - `cryptography==46.0.7` (latest compatible 46.x patch selected for pyhpke's `<47` bound)
204
+ - `httpx==0.28.1`
205
+
206
+ Tests use `pytest==8.4.2`; builds use `hatchling==1.27.0`. The archive includes
207
+ `VENDORED_SHA256.json` so reviewers can compare vendored files to the pinned
208
+ upstream revision.
209
+
210
+ ## Offline verification
211
+
212
+ ```sh
213
+ python -m pip install '.[test]'
214
+ python -m pytest
215
+ ```
216
+
217
+ The interoperability fixture builds a local Go EHBP server with the same pinned
218
+ upstream revision (`GOTOOLCHAIN=go1.26.0`) and verifies real Python-to-Go encrypted
219
+ requests, JSON responses, streaming, truncation and failure handling. No GPU,
220
+ production credentials or production API calls are used. Dependency and Go
221
+ toolchain downloads are needed once unless already cached.
@@ -0,0 +1,17 @@
1
+ # Third-party notices
2
+
3
+ The entire `src/lebrel_encrypted/_vendor/ehbp` Python module is copied unchanged
4
+ from https://github.com/tinfoilsh/encrypted-http-body-protocol at commit
5
+ `6ae53f8b6270834ca1cb8c29f8d2a79e372bc4b0`, directory `python/src/ehbp`.
6
+ The sole added file inside that directory is the upstream repository's LICENSE.
7
+ Its original relative imports preserve the upstream implementation.
8
+
9
+ Copyright (c) 2025 Tinfoil, Inc. — MIT License.
10
+
11
+ The full license appears in `src/lebrel_encrypted/_vendor/ehbp/LICENSE` and must
12
+ remain included with redistributions. `VENDORED_SHA256.json` records the exact
13
+ source hashes for comparison to that public revision.
14
+
15
+ The SDK separately depends on pyhpke (MIT), cryptography
16
+ (Apache-2.0 OR BSD-3-Clause), and HTTPX (BSD-3-Clause). These distributions are
17
+ installed from their pinned packages and retain their respective notices.
@@ -0,0 +1,11 @@
1
+ from lebrel_encrypted import Lebrel
2
+
3
+ with Lebrel() as client:
4
+ answer = client.chat.completions.create(
5
+ messages=[{"role": "user", "content": "Write the opening of a story."}],
6
+ max_tokens=256,
7
+ )
8
+ print(answer["choices"][0]["message"]["content"])
9
+ # The signed receipt of this answer: which weights answered, bound to this request and this text.
10
+ receipt = client.receipt(answer)
11
+ print("receipt", receipt.request_id, "verified" if receipt.verified else "NOT VERIFIED")
@@ -0,0 +1,13 @@
1
+ from lebrel_encrypted import Lebrel
2
+
3
+ with Lebrel() as client:
4
+ with client.chat.completions.create(
5
+ messages=[{"role": "user", "content": "Explain how HPKE works."}],
6
+ max_tokens=512,
7
+ stream=True,
8
+ ) as stream:
9
+ for chunk in stream:
10
+ for choice in chunk.get("choices", []):
11
+ print(choice.get("delta", {}).get("content") or "", end="", flush=True)
12
+ assert stream.completed
13
+ print()
@@ -0,0 +1,48 @@
1
+ [build-system]
2
+ requires = ["hatchling==1.27.0"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "lebrel-encrypted"
7
+ version = "0.2.0"
8
+ authors = [{ name = "Lebrel" }]
9
+ description = "Verified encrypted Python client for Lebrel DeepSeek V4 Flash Uncensored"
10
+ readme = "README.md"
11
+ license = "MIT"
12
+ requires-python = ">=3.9"
13
+ keywords = ["lebrel", "encrypted", "hpke", "llm", "openai-compatible", "proof-of-edition", "receipts"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "Operating System :: OS Independent",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3 :: Only",
20
+ "Topic :: Security :: Cryptography",
21
+ "Topic :: Software Development :: Libraries :: Python Modules",
22
+ "Typing :: Typed",
23
+ ]
24
+ dependencies = [
25
+ "cryptography==46.0.7",
26
+ "httpx==0.28.1",
27
+ "pyhpke==0.6.3",
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://lebrel.ai"
32
+ Repository = "https://github.com/LEBREL-AI/lebrel-encrypted-python"
33
+ Documentation = "https://lebrel.ai/docs"
34
+
35
+ [project.scripts]
36
+ lebrel-encrypted = "lebrel_encrypted.cli:main"
37
+
38
+ [project.optional-dependencies]
39
+ test = ["pytest==8.4.2"]
40
+
41
+ [tool.hatch.build.targets.wheel]
42
+ packages = ["src/lebrel_encrypted"]
43
+
44
+ [tool.hatch.build.targets.sdist]
45
+ include = ["src", "examples", "tests", "README.md", "LICENSE", "THIRD_PARTY_NOTICES.md", "pyproject.toml"]
46
+
47
+ [tool.pytest.ini_options]
48
+ testpaths = ["tests"]
@@ -0,0 +1,17 @@
1
+ """Encrypted Lebrel API client. Conversation dictionaries use OpenAI's schema."""
2
+
3
+ from .client import (
4
+ APIError,
5
+ Completion,
6
+ CompletionStream,
7
+ EncryptionError,
8
+ Lebrel,
9
+ LebrelError,
10
+ MODEL_ID,
11
+ StreamError,
12
+ TransportError,
13
+ )
14
+ from .proof import Check, Manifest, Receipt
15
+
16
+ __version__ = "0.2.0"
17
+ __all__ = ["Lebrel", "MODEL_ID", "Completion", "CompletionStream", "Receipt", "Manifest", "Check", "LebrelError", "EncryptionError", "TransportError", "APIError", "StreamError"]
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ raise SystemExit(main())
@@ -0,0 +1 @@
1
+ """Unmodified third-party source; see THIRD_PARTY_NOTICES.md."""
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Tinfoil, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+