cloakapi 0.5.1__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.
- cloakapi-0.5.1/CHANGELOG.md +382 -0
- cloakapi-0.5.1/LICENSE +202 -0
- cloakapi-0.5.1/NOTICE +18 -0
- cloakapi-0.5.1/PKG-INFO +441 -0
- cloakapi-0.5.1/README.md +392 -0
- cloakapi-0.5.1/THIRD-PARTY-NOTICES.md +22 -0
- cloakapi-0.5.1/TRADEMARKS.md +19 -0
- cloakapi-0.5.1/cloakapi/__init__.py +271 -0
- cloakapi-0.5.1/cloakapi/_disclosure/clk.txt +11 -0
- cloakapi-0.5.1/cloakapi/_disclosure/clk_message.txt +11 -0
- cloakapi-0.5.1/cloakapi/_disclosure/cloak_angle.txt +16 -0
- cloakapi-0.5.1/cloakapi/_disclosure/cloak_angle_message.txt +16 -0
- cloakapi-0.5.1/cloakapi/_http.py +443 -0
- cloakapi-0.5.1/cloakapi/_pretokenised.py +134 -0
- cloakapi-0.5.1/cloakapi/_relay.py +213 -0
- cloakapi-0.5.1/cloakapi/_release.py +30 -0
- cloakapi-0.5.1/cloakapi/_request_strings.py +1267 -0
- cloakapi-0.5.1/cloakapi/_ruleset/patterns.json +2238 -0
- cloakapi-0.5.1/cloakapi/_stream_detok.py +788 -0
- cloakapi-0.5.1/cloakapi/_version.py +31 -0
- cloakapi-0.5.1/cloakapi/async_client.py +704 -0
- cloakapi-0.5.1/cloakapi/attachments.py +176 -0
- cloakapi-0.5.1/cloakapi/cli.py +245 -0
- cloakapi-0.5.1/cloakapi/client.py +848 -0
- cloakapi-0.5.1/cloakapi/config.py +491 -0
- cloakapi-0.5.1/cloakapi/coverage.py +21 -0
- cloakapi-0.5.1/cloakapi/degraded.py +196 -0
- cloakapi-0.5.1/cloakapi/disclosure.py +334 -0
- cloakapi-0.5.1/cloakapi/engine.py +922 -0
- cloakapi-0.5.1/cloakapi/errors.py +304 -0
- cloakapi-0.5.1/cloakapi/notices.py +142 -0
- cloakapi-0.5.1/cloakapi/pii/__init__.py +4 -0
- cloakapi-0.5.1/cloakapi/pii/_checksum.py +510 -0
- cloakapi-0.5.1/cloakapi/pii/_ruleset.py +131 -0
- cloakapi-0.5.1/cloakapi/pii/patterns.py +1713 -0
- cloakapi-0.5.1/cloakapi/pii/rulefile.py +738 -0
- cloakapi-0.5.1/cloakapi/pii/tokenizer.py +537 -0
- cloakapi-0.5.1/cloakapi/posture.py +112 -0
- cloakapi-0.5.1/cloakapi/py.typed +1 -0
- cloakapi-0.5.1/cloakapi/receipts.py +605 -0
- cloakapi-0.5.1/cloakapi/resources/__init__.py +32 -0
- cloakapi-0.5.1/cloakapi/resources/admin.py +104 -0
- cloakapi-0.5.1/cloakapi/resources/buffer.py +54 -0
- cloakapi-0.5.1/cloakapi/resources/chat.py +293 -0
- cloakapi-0.5.1/cloakapi/resources/detect.py +115 -0
- cloakapi-0.5.1/cloakapi/resources/messages.py +260 -0
- cloakapi-0.5.1/cloakapi/resources/models.py +58 -0
- cloakapi-0.5.1/cloakapi/resources/receipts.py +252 -0
- cloakapi-0.5.1/cloakapi/resources/reconstruct.py +173 -0
- cloakapi-0.5.1/cloakapi/resources/tokens.py +47 -0
- cloakapi-0.5.1/cloakapi/review.py +112 -0
- cloakapi-0.5.1/cloakapi/streaming.py +446 -0
- cloakapi-0.5.1/cloakapi/surrogate_mode.py +308 -0
- cloakapi-0.5.1/cloakapi/types.py +284 -0
- cloakapi-0.5.1/examples/fake_gateway.py +105 -0
- cloakapi-0.5.1/examples/hello.py +44 -0
- cloakapi-0.5.1/hatch_build.py +302 -0
- cloakapi-0.5.1/licenses/Apache-2.0.txt +202 -0
- cloakapi-0.5.1/licenses/CC-BY-4.0.txt +104 -0
- cloakapi-0.5.1/licenses/MIT-Faker.txt +21 -0
- cloakapi-0.5.1/licenses/MIT.txt +21 -0
- cloakapi-0.5.1/pyproject.toml +251 -0
- cloakapi-0.5.1/scripts/build_wheels.py +518 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the CloakAPI Python SDK are documented here.
|
|
4
|
+
|
|
5
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
6
|
+
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## [0.5.1] — 2026-11-15
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- A `thinking` block is sent byte for byte as written, with or without a
|
|
15
|
+
`signature`. The text and the signature are not replaced. The rules still
|
|
16
|
+
run over the thinking text. When they see personal data, the request carries
|
|
17
|
+
`unchecked_content` / `value_not_replaced` and the existing sentence, which
|
|
18
|
+
does not include the request text. A `redacted_thinking` block's `data` stays
|
|
19
|
+
as written and is named the same way when the rules match it. Thai and Khmer
|
|
20
|
+
text still carries `unchecked_content` / `unverified_script`. The send is not
|
|
21
|
+
stopped. Release 0.5.0 already left the block untokenised so a provider
|
|
22
|
+
signature still matched, and did not name personal data the rules saw in
|
|
23
|
+
that text.
|
|
24
|
+
|
|
25
|
+
## [0.5.0] — 2026-11-12
|
|
26
|
+
|
|
27
|
+
Part of release set R-1.6.1: every CloakAPI SDK of this set carries rule set
|
|
28
|
+
1.6.1 and shares one behaviour contract. (0.4.1 was not offered for download.)
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- `review=` on `CloakAPI` and `AsyncCloakAPI`: `review(text, info)` is called
|
|
33
|
+
with the exact outgoing body and a `ReviewInfo` (`state`, `coverage`,
|
|
34
|
+
`warnings`); return `True` to send.
|
|
35
|
+
- Step form for applications with their own review screen:
|
|
36
|
+
`client.messages.prepare(...)` / `client.chat.completions.prepare(...)`,
|
|
37
|
+
`client.approve(prepared)`, `client.send(approved)`.
|
|
38
|
+
- `coverage` and `warnings` on every reply; `client.last_warnings`.
|
|
39
|
+
A notice is a `SendWarning(code, reason, message)`; codes `reduced_coverage`
|
|
40
|
+
and `unchecked_content`.
|
|
41
|
+
- `reply.receipt` and `client.last_receipt` (the receipt the gateway returned
|
|
42
|
+
with the reply, when it returned one; `last_receipt` is `None` after a
|
|
43
|
+
streaming call or a call that failed) and
|
|
44
|
+
`client.receipts.verify_offline(receipt, jwks=...)`.
|
|
45
|
+
- Gateway refusals about the engine or the request proof carry the gateway's
|
|
46
|
+
code in `err.code` (`engine_identity_required`, `engine_outdated`,
|
|
47
|
+
`engine_untrusted`, `engine_superseded`, `pretokenised_required`) and one
|
|
48
|
+
sentence saying what it means and what to do.
|
|
49
|
+
- Replies are restored in the string values of tool-call arguments as well as
|
|
50
|
+
in their text, including a stand-in split across streamed argument chunks
|
|
51
|
+
(chat `tool_calls` and Anthropic `input_json_delta`).
|
|
52
|
+
- `cloakapi.engine_issue()`: why the engine is not in use, or `None`.
|
|
53
|
+
- `examples/fake_gateway.py`: a stand-in gateway to run the hello world without a key.
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- The proof for the exact request bytes is requested on every send, also when
|
|
58
|
+
the structured rules prepared the request (0.4.1 skipped it there, and the
|
|
59
|
+
gateway does not relay a request without it). If the proof cannot be
|
|
60
|
+
obtained, the gateway or network error is raised and nothing is sent; 0.4.1
|
|
61
|
+
sent the request without proof in that case.
|
|
62
|
+
- A request the structured rules prepared claims no engine: it carries no
|
|
63
|
+
`X-Cloak-Engine-Version` or `X-Cloak-Engine-Hash`. Coverage is stated in
|
|
64
|
+
`X-Cloak-Coverage` and in `X-CloakAPI-Tokenized: true;coverage=<coverage>`.
|
|
65
|
+
- One rule for the engine pin. An engine file without `CLOAKAPI_ENGINE_SHA256_PIN`,
|
|
66
|
+
with a pin that does not match the loaded bytes, or built for another rule
|
|
67
|
+
set than this SDK is not used; the request goes out prepared by the
|
|
68
|
+
structured rules and the notice names what to fix. The engine inside a
|
|
69
|
+
platform wheel needs no pin of yours: the package knows which file it was
|
|
70
|
+
released with.
|
|
71
|
+
- An engine that will not take a text (for example because its name model is
|
|
72
|
+
missing) no longer stops the request; the structured rules run instead, the
|
|
73
|
+
request then states no engine identity, and its notice says so
|
|
74
|
+
(`reduced_coverage` / `engine_refused`).
|
|
75
|
+
- Content no longer stops a request. `RequestBodyRejectedError` was raised for
|
|
76
|
+
inline media and for a detected value in a tool schema `enum`, `const` or
|
|
77
|
+
`default`. Now: a detected value used as an object key in free-form data
|
|
78
|
+
is replaced, except a key under `metadata`, which stays as written while the
|
|
79
|
+
value under it is replaced (owner decision F-068); a value in a place that must stay as written
|
|
80
|
+
(tool schema literal or property name, key inside tool-call arguments,
|
|
81
|
+
identifier, number) is sent with `unchecked_content` / `value_not_replaced`;
|
|
82
|
+
images, audio and files in a request are sent as provided with
|
|
83
|
+
`unchecked_content` / `media_not_checked`. The notice reaches your review
|
|
84
|
+
before anything is sent. `RequestBodyRejectedError` remains for a request
|
|
85
|
+
that does not have the shape of a request.
|
|
86
|
+
- A content block of a type this SDK does not name (for example `search_result`) is
|
|
87
|
+
sent. Every string in it outside protocol fields is tokenised, a string `source`
|
|
88
|
+
included (before, a string `source` was left as written), and the notice says so
|
|
89
|
+
(`unchecked_content` / `unrecognised_block`). A `redacted_thinking` block's `data`
|
|
90
|
+
(a provider blob) stays as written; before, it was handed to the tokeniser.
|
|
91
|
+
- The engine file is hashed and compared before it is loaded. A truncated or
|
|
92
|
+
altered file is not loaded; the structured rules run and the notice names
|
|
93
|
+
the reason. (Before, the comparison came after loading and a truncated file
|
|
94
|
+
could end the process.)
|
|
95
|
+
- Without the engine, the structured rules now apply every rule type of the
|
|
96
|
+
bundled rule set that has a pattern (139 of 142), with the check-digit
|
|
97
|
+
validation each rule names. Before, a hand-maintained table of 17 types ran,
|
|
98
|
+
so national ID numbers of most countries, postal codes, medical record
|
|
99
|
+
numbers and more were sent as written on that path. Names, places and
|
|
100
|
+
organisations still need the engine.
|
|
101
|
+
- Coverage, notices, the restore map and the receipt belong to each request.
|
|
102
|
+
Overlapping requests on one client no longer take each other's notices.
|
|
103
|
+
- `pip install` from the source archive or a checkout needs no flag
|
|
104
|
+
(`CLOAKAPI_ALLOW_PURE_WHEEL` is accepted and ignored). A wheel build accepts
|
|
105
|
+
only the engine file of the release set.
|
|
106
|
+
- The package builds, tests and packs from its own directory.
|
|
107
|
+
|
|
108
|
+
### Fixed
|
|
109
|
+
|
|
110
|
+
- A reply that holds a `thinking`, `redacted_thinking`, `server_tool_use` or
|
|
111
|
+
other block type is read. Before, the reply model knew only text and
|
|
112
|
+
`tool_use` blocks, and such a reply failed with "Gateway returned an invalid
|
|
113
|
+
response". Thinking is kept exactly as the model wrote it: a reply never
|
|
114
|
+
restores it, and a thinking block sent back is no longer tokenised, so its
|
|
115
|
+
`signature` still matches. A `server_tool_use` block sent back is tokenised
|
|
116
|
+
like other blocks, with its tool name and id as written.
|
|
117
|
+
- A reply could be restored to the engine's stand-in address instead of your
|
|
118
|
+
address: a second local pass replaced the stand-in again and the reply was
|
|
119
|
+
restored one step only.
|
|
120
|
+
- The notice about reduced coverage now says why (missing or wrong pin, another
|
|
121
|
+
rule set, an engine file that cannot be loaded and the glibc version it needs).
|
|
122
|
+
|
|
123
|
+
### Deprecated
|
|
124
|
+
|
|
125
|
+
- `review_callback=` (receives a `RelayReview`). Use `review=`. Removed in 0.6.
|
|
126
|
+
|
|
127
|
+
## [0.4.0] — 2026-08-09
|
|
128
|
+
|
|
129
|
+
### Fixed — P0: the SDK silently protected 17 of 140 PII types off Linux/x86-64 (finding 180)
|
|
130
|
+
|
|
131
|
+
`cloakapi` was published as a SINGLE `cloakapi-0.3.1-py3-none-any.whl` that
|
|
132
|
+
bundled one `libcloak_engine.so` built for Linux/x86-64. On macOS (Intel and
|
|
133
|
+
Apple Silicon), Windows and Linux/ARM that wheel installed **cleanly**, the
|
|
134
|
+
library could not be loaded, and the SDK fell back to the pure-Python lane —
|
|
135
|
+
**17 of the canonical 140 PII types**. The missing 123 include `NAME`,
|
|
136
|
+
`GPS_COORDS`, every national-ID format, and the whole health/biometric family;
|
|
137
|
+
they egressed **verbatim**. The only signal was an `engine="regex"` field that
|
|
138
|
+
nothing read.
|
|
139
|
+
|
|
140
|
+
A platform-tagged wheel would at least have made `pip` say "no matching
|
|
141
|
+
distribution". `py3-none-any` removed even that.
|
|
142
|
+
|
|
143
|
+
**Two changes, in the same direction:**
|
|
144
|
+
|
|
145
|
+
- **Platform-specific wheels.** `hatch_build.py` (new hatchling build hook)
|
|
146
|
+
derives the wheel platform tag **from the bundled binary itself** — ELF/Mach-O/PE
|
|
147
|
+
header plus the glibc versions the object actually references — and **fails the
|
|
148
|
+
build** when a native library is present and no honest tag can be derived. An
|
|
149
|
+
`any`-tagged wheel containing a native library is now unbuildable. An
|
|
150
|
+
engine-less wheel additionally requires `CLOAKAPI_ALLOW_PURE_WHEEL=1`.
|
|
151
|
+
`scripts/build_wheels.py` builds one wheel per platform and prints every
|
|
152
|
+
platform it CANNOT build, with the reason.
|
|
153
|
+
- **Fail-closed reduced lane.** `cloakapi/degraded.py` (new). Whenever the
|
|
154
|
+
native engine cannot be loaded, `tokenize_text_ex` / `tokenize_text`,
|
|
155
|
+
`client.detect.detect` and `prepare_attachments` now raise
|
|
156
|
+
`ReducedCoverageError` instead of quietly running reduced. Opt in with
|
|
157
|
+
`CLOAKAPI_ALLOW_REDUCED=1` (or `allow_reduced=True` at the call site) and you
|
|
158
|
+
get a `ReducedCoverageWarning` **and** an unconditional stderr banner — the
|
|
159
|
+
banner because `warnings` can be filtered process-wide, and a degradation a
|
|
160
|
+
stray filter can mute is the silence this fix exists to end.
|
|
161
|
+
|
|
162
|
+
**Breaking for callers who were relying on the silent fallback** — deliberately.
|
|
163
|
+
A privacy SDK that quietly protects less than it promises is worse than one that
|
|
164
|
+
refuses to start.
|
|
165
|
+
|
|
166
|
+
### Added
|
|
167
|
+
|
|
168
|
+
- `cloakapi.ReducedCoverageError`, `ReducedCoverageWarning`, `coverage_counts()`,
|
|
169
|
+
`reduced_coverage_allowed()`, `guard_reduced_coverage()`, `ENV_ALLOW_REDUCED`.
|
|
170
|
+
- `linux aarch64` engine build (Graviton, ARM servers, Docker on Apple Silicon):
|
|
171
|
+
`cloakapi-0.4.0-py3-none-manylinux_2_39_aarch64.whl`.
|
|
172
|
+
|
|
173
|
+
### Known gaps (explicitly BLOCKED, not hidden)
|
|
174
|
+
|
|
175
|
+
macOS (both arches), Windows (msvc and gnu) and musl/Alpine wheels cannot be
|
|
176
|
+
built on the current builder; see the BLOCKED table in
|
|
177
|
+
`goal-run/F-BEVIS/SAK180-PYTHON-SDK-ALLE-PLATTFORMER.md` and the `MATRIX` in
|
|
178
|
+
`scripts/build_wheels.py`. Until those exist, users there install the sdist and
|
|
179
|
+
hit the fail-closed error rather than silent 17-type protection.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Earlier notes, not tied to a release
|
|
184
|
+
|
|
185
|
+
These notes were written between 0.1.0 and 0.5.0 under an "Unreleased" heading
|
|
186
|
+
and are kept as written. 0.4.1 was not offered for download. Where they describe
|
|
187
|
+
a refusal, the 0.5.0 entry above says what replaced it: content no longer stops
|
|
188
|
+
a request.
|
|
189
|
+
|
|
190
|
+
### Fixed — refuse unprotected and unreviewable request egress
|
|
191
|
+
|
|
192
|
+
- Sync and async relay calls now reject `tokenize=False` before transport,
|
|
193
|
+
require the pre-tokenized proof, walk the supported text-bearing request
|
|
194
|
+
slots, and refuse inline media or sensitive schema literals.
|
|
195
|
+
- Requests and streams do not follow redirects. Upstream response text and
|
|
196
|
+
transport exception details are no longer copied into SDK errors or logs.
|
|
197
|
+
- Invalid surrogate-mode diagnostics no longer retain or echo the caller's raw
|
|
198
|
+
value. The `messages.create` and chat completion docstrings describe which
|
|
199
|
+
body text is transformed and which unsafe values are refused.
|
|
200
|
+
|
|
201
|
+
**Breaking behavior/API changes for release notes:** `tokenize=False` now raises
|
|
202
|
+
`PlaintextRelayNotSupportedError`; `SurrogateModeError` no longer takes or
|
|
203
|
+
exposes the raw invalid value; `NetworkError.cause` is always `None`; and API
|
|
204
|
+
errors use status-based generic messages rather than upstream response text.
|
|
205
|
+
These changes require a new SDK version before republishing. Do not republish
|
|
206
|
+
the changed bytes under the existing `0.4.1` identity.
|
|
207
|
+
|
|
208
|
+
### Fixed — fail closed on unavailable file engines and require review before attachment egress
|
|
209
|
+
|
|
210
|
+
- File/image operations now explain which platform lacks a compatible
|
|
211
|
+
`libcloak_engine`, how to download or build one, and confirm that raw bytes
|
|
212
|
+
were not sent. `CLOAKAPI_ALLOW_REDUCED` remains text-only.
|
|
213
|
+
- Runtime loading now refuses the withdrawn 6ed6, c135 (aarch64), and c965
|
|
214
|
+
engine digests that the wheel builder already rejects. Stale served wheels
|
|
215
|
+
therefore fail closed instead of using an obsolete embedded motor.
|
|
216
|
+
- `attachments=` now raises `AttachmentReviewRequiredError` before any request.
|
|
217
|
+
Call `tokenize_file`, review its returned text, then relay that text in
|
|
218
|
+
`messages`.
|
|
219
|
+
- This runtime and egress behavior is assigned Python SDK version **0.4.1**.
|
|
220
|
+
Build and publish are still required before users of the hosted 0.4.0 files
|
|
221
|
+
receive these changes.
|
|
222
|
+
|
|
223
|
+
### Fixed — P0: street addresses egressed verbatim (2026-07-28)
|
|
224
|
+
|
|
225
|
+
Continental-European addresses, which put the house number AFTER the street,
|
|
226
|
+
were not detected and left this SDK in the clear: `Bergensgata 14`,
|
|
227
|
+
`Storgata 5`, `Hauptstrasse 27`, `Rue de Rivoli 12`, `Via Roma 12`,
|
|
228
|
+
`Calle Mayor 45`, `Herengracht 182`, `Mannerheimintie 12`. There were TWO
|
|
229
|
+
independent leak paths, both real shipping configurations:
|
|
230
|
+
|
|
231
|
+
- **FFI path (default).** `engine.py` resolves the bundled
|
|
232
|
+
`cloakapi/_lib/libcloak_engine.so` BEFORE the in-repo `target/release` build.
|
|
233
|
+
The bundled artifact was stale (pre-fix), so it kept leaking even after the
|
|
234
|
+
shared ruleset moved to 1.6.0 and the engine was correctly rebuilt — the
|
|
235
|
+
rebuild never reached the copy the SDK actually loads. The bundled engine has
|
|
236
|
+
been refreshed, and `tests/test_ruleset_binding.py` now fails if it ever goes
|
|
237
|
+
stale again.
|
|
238
|
+
- **Pure-Python fallback.** `pii/patterns.py` had NO `ADDRESS` type at all, so it
|
|
239
|
+
leaked BOTH orderings — the Anglo forms were not "controls that kept working"
|
|
240
|
+
there either. `ADDRESS` is now **derived from the canonical ruleset** at import
|
|
241
|
+
time rather than hand-written, so it cannot drift from the fasit.
|
|
242
|
+
|
|
243
|
+
### Fixed — engine identity was a hand-typed literal that had drifted
|
|
244
|
+
|
|
245
|
+
`engine.RULESET_VERSION` was the literal `"1.4.29"` while the shared ruleset was
|
|
246
|
+
at `1.6.0`. That value is emitted as `X-Cloak-Engine-Version`, which the
|
|
247
|
+
gateway's EnforceEngineFloor gate (ADR 0014 §3) floor-checks — so the SDK
|
|
248
|
+
declared a ruleset it was not running. Two tests asserted the literal and so
|
|
249
|
+
PINNED the drift instead of catching it; both now assert the derived value.
|
|
250
|
+
`RULESET_VERSION` is now read from `shared/pii-patterns/patterns.json` (bundled
|
|
251
|
+
into the wheel as `cloakapi/_ruleset/patterns.json`, with a byte-identity guard).
|
|
252
|
+
When no ruleset is readable the SDK reports `0.0.0-unknown` — a deliberately
|
|
253
|
+
invalid sentinel so the gateway rejects rather than accepting a stale semver.
|
|
254
|
+
|
|
255
|
+
> **Release note:** this changes runtime BEHAVIOUR without a version bump so far.
|
|
256
|
+
> The published `0.1.1` artifact leaks these addresses; do not ship the fixed code
|
|
257
|
+
> under the same version number.
|
|
258
|
+
|
|
259
|
+
### Added — SDK parity (Phase 2c / Stage-1 A1-PYTHON)
|
|
260
|
+
|
|
261
|
+
- **Hardened, byte-locked disclosure (C7).** New `cloakapi.disclosure` module:
|
|
262
|
+
`CLK_DISCLOSURE_TEXT` / `CLOAK_ANGLE_DISCLOSURE_TEXT` (embedded byte-for-byte,
|
|
263
|
+
sha256-checked against the conformance golden vectors), `disclosure_text`,
|
|
264
|
+
`detect_marker_format`, and `with_disclosure` (idempotent, fail-safe leading
|
|
265
|
+
`system` message). Replaces the stale, weak `CONTEXT_PREAMBLE`, which is now an
|
|
266
|
+
alias of the hardened `cloak-angle` disclosure (the `<<CLOAK:…>>` lane this SDK
|
|
267
|
+
emits).
|
|
268
|
+
- **On-device device-receipt signer (C8).** `sign_device_receipt` +
|
|
269
|
+
`generate_device_key_pair` + `DeviceReceiptError` (ECDSA P-256, RFC-8785 JCS, raw
|
|
270
|
+
`r‖s` base64url). Content-free v3 receipts that `verify_device_receipt` validates
|
|
271
|
+
offline; fail-closed. `verify_device_receipt` added as the canonical alias.
|
|
272
|
+
- **Privacy posture (C20).** `cloakapi.posture`: `privacy_posture`, `PrivacyPosture`,
|
|
273
|
+
`PRIVACY_TIERS` — engine identity + fail-closed + Level-1 tier introspection.
|
|
274
|
+
|
|
275
|
+
### Changed
|
|
276
|
+
|
|
277
|
+
- **Secrets are tokenise-and-flag, never blocked (C5).** Corrected stale
|
|
278
|
+
"hard-blocked secret" docstrings in `engine.py`, `pii/tokenizer.py`, and
|
|
279
|
+
`resources/detect.py`: a credential is tokenised to `<<CLOAK:CRED_KEY:…>>` and
|
|
280
|
+
flagged, never thrown/blocked.
|
|
281
|
+
- Bundled `cloakapi/_lib/libcloak_engine.so` refreshed to the FROZEN engine
|
|
282
|
+
(sha256 `9e8bab7c…`, ruleset `03597feb`, v1.4.29) so the shipped wheel attests the
|
|
283
|
+
correct `X-Cloak-Engine-Hash` out of the box.
|
|
284
|
+
- Documented the browser-only capabilities (C4 caption gate, C5 review UI) as
|
|
285
|
+
intentionally not shipped server-side.
|
|
286
|
+
|
|
287
|
+
### Tests
|
|
288
|
+
|
|
289
|
+
- `tests/test_conformance_golden.py` — asserts the 14 FFI exact vectors, the
|
|
290
|
+
disclosure byte-lock + `with_disclosure` behaviour, the receipt fixture + tamper
|
|
291
|
+
case, and a self-minted receipt round-trip against
|
|
292
|
+
`goal-run/conformance/golden-vectors.json`. **CONFORMANT.**
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## [0.1.0] - 2026-04-18
|
|
297
|
+
|
|
298
|
+
### Added
|
|
299
|
+
|
|
300
|
+
#### Core SDK
|
|
301
|
+
- `CloakAPI` synchronous client with `messages`, `chat`, `buffer`, `admin` resource namespaces
|
|
302
|
+
- `AsyncCloakAPI` asynchronous client with identical surface area using `httpx.AsyncClient`
|
|
303
|
+
- Both clients support context manager (`with` / `async with`) usage
|
|
304
|
+
- `http_client` constructor parameter to inject a pre-configured `httpx.Client` / `httpx.AsyncClient`
|
|
305
|
+
|
|
306
|
+
#### Anthropic-compatible messages
|
|
307
|
+
- `client.messages.create(model, messages, ...)` returning a typed `Message` Pydantic model
|
|
308
|
+
- `stream=True` mode returning `Iterator[StreamEvent]` / `AsyncIterator[StreamEvent]`
|
|
309
|
+
- Typed streaming events: `MessageStart`, `ContentBlockStart`, `ContentBlockDelta`,
|
|
310
|
+
`ContentBlockStop`, `MessageDelta`, `MessageStop`, `ErrorEvent`
|
|
311
|
+
|
|
312
|
+
#### OpenAI-compatible chat completions
|
|
313
|
+
- `client.chat.completions.create(model, messages, ...)` returning typed `ChatCompletion`
|
|
314
|
+
- `stream=True` mode returning `Iterator[ChatCompletionChunk]` / `AsyncIterator[ChatCompletionChunk]`
|
|
315
|
+
|
|
316
|
+
#### Buffered responses
|
|
317
|
+
- `client.buffer.get(request_id)` returning `BufferedResponse`
|
|
318
|
+
|
|
319
|
+
#### Admin stats (admin token required)
|
|
320
|
+
- `client.admin.stats.overview()` → `AdminOverview`
|
|
321
|
+
- `client.admin.stats.usage(period=...)` → `AdminUsagePeriod`
|
|
322
|
+
- `client.admin.stats.users(page=..., per_page=...)` → `AdminUsers`
|
|
323
|
+
|
|
324
|
+
#### PII tokenization
|
|
325
|
+
- `tokenize=True` on the client or per-call enables automatic PII detection and tokenization
|
|
326
|
+
- Supported types: EMAIL, PHONE, SSN, IBAN, CREDIT_CARD, IP_ADDRESS, IPV6_ADDRESS,
|
|
327
|
+
DATE_OF_BIRTH, PASSPORT, URL, API_KEY
|
|
328
|
+
- `X-CloakAPI-Tokenized: true` header set automatically when tokenization is active
|
|
329
|
+
- Token format: `<<CLOAK:{TYPE}:{6-char-hash}>>`
|
|
330
|
+
- Deterministic tokens: same PII value always maps to the same token within a session
|
|
331
|
+
- Response content is automatically de-tokenized before returning to the caller
|
|
332
|
+
- `extra_patterns` parameter in `detect_pii()` for user-supplied regex patterns
|
|
333
|
+
- Existing tokenized regions skipped (no double-tokenization)
|
|
334
|
+
|
|
335
|
+
#### Error handling
|
|
336
|
+
- Full exception hierarchy: `CloakAPIError` → `APIError` → specific typed errors
|
|
337
|
+
- `RateLimitError` with `retry_after` attribute
|
|
338
|
+
- `ConcurrencyLimitError` for streaming limit (code=`stream_concurrency_limit`)
|
|
339
|
+
- `NetworkError` wraps connection/timeout failures with `.cause` attribute
|
|
340
|
+
|
|
341
|
+
#### Retry logic
|
|
342
|
+
- Automatic retry on: 429, 502, 503, 504, `httpx.ConnectError`, `httpx.ReadTimeout`
|
|
343
|
+
- Exponential backoff with full jitter
|
|
344
|
+
- Respects `Retry-After` response header for 429
|
|
345
|
+
- Configurable via `max_retries` constructor argument (default: 3)
|
|
346
|
+
- Non-retryable 4xx errors (401, 403, 404, etc.) raise immediately
|
|
347
|
+
|
|
348
|
+
#### Configuration
|
|
349
|
+
- Priority: constructor arg → `CLOAKAPI_API_KEY` env → `~/.cloakapi/config.json`
|
|
350
|
+
- `CLOAKAPI_BASE_URL` env var supported
|
|
351
|
+
- `_load_config_file()` / `_save_config_file()` utilities for config management
|
|
352
|
+
|
|
353
|
+
#### Logging
|
|
354
|
+
- `logging.getLogger("cloakapi")` — API key is scrubbed in all log messages
|
|
355
|
+
- `CLOAKAPI_LOG_REQUESTS=1` env var enables request-level debug logging
|
|
356
|
+
|
|
357
|
+
#### CLI (`cloakapi` entry point)
|
|
358
|
+
- `cloakapi ping` — verify API key validity and gateway connectivity
|
|
359
|
+
- `cloakapi chat --model ... --prompt ...` — one-shot Anthropic-format chat
|
|
360
|
+
- `cloakapi stream --model ... --prompt ...` — streaming chat demo
|
|
361
|
+
- `cloakapi config show` — display current credentials (masked)
|
|
362
|
+
- `cloakapi config set --api-key ... --base-url ...` — save credentials
|
|
363
|
+
|
|
364
|
+
#### Packaging
|
|
365
|
+
- `pyproject.toml` (PEP 621, hatchling build backend)
|
|
366
|
+
- `py.typed` marker for PEP 561 compliance
|
|
367
|
+
- Dependencies: `httpx>=0.27`, `pydantic>=2.5`, `typer>=0.12`, `rich>=13`
|
|
368
|
+
- Optional extras: `[ner]` (spaCy), `[gliner]` (GLiNER ONNX)
|
|
369
|
+
- `noxfile.py` for multi-Python test matrix (3.10–3.14)
|
|
370
|
+
- `ruff` for linting and formatting
|
|
371
|
+
- `mypy --strict` for type checking
|
|
372
|
+
|
|
373
|
+
#### Testing
|
|
374
|
+
- pytest suite with `respx` mocking (no real HTTP calls in default suite)
|
|
375
|
+
- Integration tests gated behind `CLOAKAPI_INTEGRATION=1` env var
|
|
376
|
+
- PII tokenizer golden tests: email, phone, SSN, IBAN, credit card, IPv4, IPv6
|
|
377
|
+
- Streaming parser tests with byte-for-byte canned SSE responses
|
|
378
|
+
- Retry logic tests (429+Retry-After, 503×3, non-retryable 401)
|
|
379
|
+
- CLI tests via `typer.testing.CliRunner`
|
|
380
|
+
- Config resolution tests with `monkeypatch`
|
|
381
|
+
- `≥85%` line coverage target
|
|
382
|
+
|
cloakapi-0.5.1/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright 2024-2026 Klokk Nettablering
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
cloakapi-0.5.1/NOTICE
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
CloakAPI SDK for Python
|
|
2
|
+
Copyright 2024-2026 Klokk Nettablering (org. no. 821 466 962, Norway)
|
|
3
|
+
|
|
4
|
+
This product is licensed under the Apache License, Version 2.0 (see LICENSE).
|
|
5
|
+
|
|
6
|
+
This package includes the CloakAPI rule set under the same license.
|
|
7
|
+
Where a build also ships a compiled engine, that binary is under the
|
|
8
|
+
same license. The engine's source code is not distributed with this package.
|
|
9
|
+
|
|
10
|
+
"CloakAPI" and "Verified by CloakAPI" are trademarks of Klokk Nettablering.
|
|
11
|
+
The Apache License does not grant permission to use them (section 6);
|
|
12
|
+
see TRADEMARKS.md.
|
|
13
|
+
|
|
14
|
+
This product includes third-party software and data. Their notices and
|
|
15
|
+
license texts are in THIRD-PARTY-NOTICES.md and the licenses/ directory.
|
|
16
|
+
Place names are derived from GeoNames (https://www.geonames.org/), licensed
|
|
17
|
+
under CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/); modified by
|
|
18
|
+
filtering and compaction.
|