Skip to content

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 into messages[], tokenises it on your machine, and strips the raw attachments[] 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 content

The receipt for the call records the file attestation (formats, hashes, detector firings) — cryptographic proof that the file content was tokenised before egress.

Supported formats

FormatExtensions
PDF.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

LimitValue
Max file size20 MB per file (local proxy: CLOAK_DOCUMENT_MAX_BYTES, default 20 MiB)
Max files per request5 (portal / desktop / extension)
Allowed formatsSee 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:

Terminal window
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 OpenAI
import 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.