File attachments
CloakAPI lets you attach documents to your AI calls — with extraction and tokenisation performed on your own device, on the client-side ways:
- Local proxy — accepts the
attachments[]base64-in-JSON shape (Option B below), decodes and converts each file to Markdown/text locally (pure-Rust extractors, the same file pipeline the desktop app uses), folds the text intomessages[], tokenises it on your machine, and strips the rawattachments[]before forwarding. Fail-closed: an attachment that cannot be converted locally blocks the whole request (422) — there is no “pass the raw document through” mode. - Browser portal, desktop app, browser extension — extract and tokenise attached files on-device before anything egresses (see the per-surface details below).
How it works (client-side ways)
Your machine — local proxy / portal / desktop / extension │ 1. decode + convert file → Markdown/text (locally) │ 2. inject the text into messages[] │ 3. tokenise PII → <<CLOAK:…>> placeholders (locally) │ 4. discard / strip the raw file bytes ▼CloakAPI gateway ← blind relay: sees only tokenised text, never file bytes ▼Upstream LLM provider ← sees only tokenised Markdown, no raw file contentThe receipt for the call records the file attestation (formats, hashes, detector firings) — cryptographic proof that the file content was tokenised before egress.
Supported formats
| Format | Extensions |
|---|---|
.pdf | |
| Microsoft Word | .docx, .doc |
| Microsoft PowerPoint | .pptx, .ppt |
| Microsoft Excel | .xlsx, .xls |
| CSV | .csv |
| HTML | .html, .htm |
| Plain text | .txt |
| Markdown | .md |
| JSON | .json |
| XML | .xml |
| EPUB | .epub |
| Jupyter notebook | .ipynb |
| Email (MIME) | .eml |
| Outlook message | .msg |
| PNG image | .png |
| JPEG image | .jpg, .jpeg |
| TIFF image | .tiff, .tif |
| WebP image | .webp |
| BMP image | .bmp |
| GIF image | .gif |
Image parts are converted to text on your machine by the local proxy / desktop engine before egress — the upstream provider never receives the original image bytes, only the recognised text.
Not supported in the current release: audio files, ZIP archives, and video files. These formats are planned for a future Lite→Full sidecar upgrade.
Limits
| Limit | Value |
|---|---|
| Max file size | 20 MB per file (local proxy: CLOAK_DOCUMENT_MAX_BYTES, default 20 MiB) |
| Max files per request | 5 (portal / desktop / extension) |
| Allowed formats | See table above |
Attaching files from an SDK — base64 in JSON, via the local proxy
Send the non-standard attachments[] key alongside the standard body, and point your
SDK at the local proxy (http://localhost:8799/v1) so conversion and tokenisation
happen on your machine:
ENCODED=$(base64 -i contract.pdf)
curl http://localhost:8799/v1/chat/completions \ -H "Content-Type: application/json" \ -d "{ \"model\": \"claude-opus-4-7\", \"messages\": [{\"role\": \"user\", \"content\": \"Summarise this contract.\"}], \"attachments\": [ { \"filename\": \"contract.pdf\", \"content_b64\": \"${ENCODED}\", \"mime_type\": \"application/pdf\" } ], \"stream\": false }"The proxy converts the PDF to text locally, tokenises it, strips attachments[], and
forwards only tokenised text through the gateway.
Python SDK example (via the local proxy)
from openai import OpenAIimport base64
client = OpenAI( base_url="http://localhost:8799/v1", # local proxy — tokenises on your machine api_key="unused", # the proxy holds your CloakAPI key)
with open("report.xlsx", "rb") as f: content_b64 = base64.b64encode(f.read()).decode()
response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Analyse this spreadsheet."}], extra_body={ "attachments": [ { "filename": "report.xlsx", "content_b64": content_b64, "mime_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", } ] },)print(response.choices[0].message.content)Portal and desktop apps
Browser portal (/chat) — click the paperclip button next to the composer to attach
up to 5 files. Files can also be pasted directly (Ctrl/Cmd+V) from the clipboard.
The portal validates the allowlist and size limits client-side and shows per-file error
chips before sending. After a successful request, the receipt strip shows a
“N files tokenised” chip alongside the normal “N det · tokenised” chip.
Desktop app — click the attach button in the chat composer to open the native file picker. The Rust backend reads the file, validates the extension and size against the gateway limits, and encodes it as base64 before the request leaves the app. The same “files tokenised” chip appears in the receipt strip after a successful call.
Browser extension — on claude.ai (the only site with a file adapter today), uploaded files are intercepted and tokenised client-side in your browser before they reach Anthropic — the raw bytes never leave your machine. This is fail-closed: if conversion or tokenisation can’t be confirmed, the upload is blocked and the original file never leaves the browser. On providers without a dedicated adapter yet (ChatGPT, Gemini, Grok, etc.), the extension warns that file contents can’t be tokenised and offers to cancel the request; text messages in the same request are still tokenised normally. When a request carries file attachments straight to the CloakAPI gateway (multipart), the extension passes it through unchanged — the blind-relay gateway does not convert or tokenise it server-side, so the upload is relayed untokenised (see the caution at the top of this page).
Error behaviour
File handling is fail-closed on every client-side way: a file that is over the size limit, in an unsupported format, or that fails conversion/tokenisation blocks the request with a clear error (HTTP 422 from the local proxy) — the raw file is never forwarded.
Fail-closed on the client side: in the browser extension and the desktop app, if file conversion or tokenisation cannot be confirmed, the upload is blocked on your machine and the raw file never leaves it. The blind-relay gateway itself performs no server-side conversion — a raw upload sent straight to the API is relayed as-is (see the caution at the top of this page).
Receipt audit trail
Requests with attachments carry per-file attestations in the signed receipt — file
hashes (pre/post), format, and detector firings — under the registered
v1.1-file-ext claim (see the
extension registry):
{ "claims": { "file_attestations": [ { "mime": "application/pdf", "sha256_pre": "…", "sha256_post": "…", "exif_stripped": true, "processing_ms": 412 } ] }}Verify any receipt at /api/v1/receipts/verify or in the browser at
app.cloakapi.io/receipt-verifier to
confirm that the file content was tokenised — not leaked — before it reached the upstream
provider.
Related
- File rebuild (round-trip) — reconstruct a real file from a
tokenised response via
POST /v1/reconstruct - Chat completions — streaming, idempotency, receipts
- Receipts — receipt envelope format and verification
- Privacy model — how the tokenisation boundary works