File rebuild (round-trip)
CloakAPI can round-trip a whole document, not just text. You attach a file (see
File attachments — file extraction and
tokenisation happen client-side via the browser extension, desktop app, or
local proxy), let a model work on the tokenised content, and once the response
comes back you turn it into a real file — a .docx, .xlsx, .html, and other
formats.
The reconstruction step is client-side, like tokenisation. It runs wherever
you already hold the alias map — the desktop app, or the open
@cloakapi/reconstruction engine running in your own environment. The gateway
relays the tokenised response; reconstruction stays on the device that holds
the alias map. There is no server-side reconstruct endpoint.
How it works (client-side)
Your device / environment (holds the alias map) │ │ 1. The model's tokenised response comes back through the gateway │ (blind relay — tokenisation runs before egress) │ 2. You detokenise the response locally, using the alias map you │ already hold for the conversation │ 3. The reconstruction engine (@cloakapi/reconstruction, bundled in │ the desktop app) rebuilds a real file — locally │ ▼Real output file (.docx / .xlsx / .html / …), written on your deviceThe plaintext and the rebuilt file stay on the machine that holds the alias map. Reconstruction does not upload either to CloakAPI.
Where reconstruction runs
- Desktop app.
md,txt,html,docx, andxlsxare rebuilt entirely locally in native Rust (CommonMark viapulldown-cmark,.docxviadocx-rs,.xlsxviarust_xlsxwriter— all MIT/Apache). No network call, this rebuild path does not upload the file. At the max privacy tier — where detokenisation itself happens only on-device — the desktop app rebuilds the file locally too, so plaintext stays on the device. - Your own environment (builders). The reconstruction engine is the open
@cloakapi/reconstructionpackage. Run it wherever you hold the alias map — your own server, a CLI, or a background job. It supports the full format matrix below. Because you hold the map and run the engine, the plaintext stays inside your trust boundary; CloakAPI’s gateway is never in the loop for the rebuild.
Browser SDK — trivial-format rebuild with no dependency
For a web app, the published SDK ships a dependency-free helper that rebuilds the
trivial text formats (txt / md / html / csv / json) directly in the browser —
you detokenise the reply locally first (holding your own alias map), then turn it into a
downloadable file. The plaintext and the file stay on the device:
import { rebuildTextFile, isRebuildableFormat } from "@cloakapi/sdk/browser";
const detokenised = engine.detokenise(reply, map); // client-side, your mapconst { blob, filename } = rebuildTextFile(detokenised, "md", "summary.md");// trigger a download of `blob` (e.g. via an object URL)Heavier output formats (docx / xlsx / pdf) throw from rebuildTextFile on purpose —
it will not route your plaintext through a server to generate them. Use the full
@cloakapi/reconstruction engine (matrix below) or the desktop app’s native Rust rebuild
for those.
Supported formats
The reconstruction engine validates the target format against a fixed allowlist,
matching the “implemented” formats in the package’s format matrix
(packages/reconstruction/docs/matrix.md):
| Format | MIME type | Round-trip fidelity |
|---|---|---|
txt | text/plain | GREEN — trivial |
md | text/markdown | GREEN — pass-through |
html | text/html | GREEN |
docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document | YELLOW — style skeleton, new paragraphs get default style |
pdf | application/pdf | YELLOW — textual fidelity, no original glyph layout |
xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet | GREEN — values only, formulas write-only |
csv | text/csv | GREEN |
pptx | application/vnd.openxmlformats-officedocument.presentationml.presentation | YELLOW — structured JSON or auto-split by headings |
epub | application/epub+zip | YELLOW |
latex | application/x-latex | GREEN — raw .tex, run pdflatex yourself |
svg | image/svg+xml | YELLOW — text-to-SVG, no original vector layout |
srt | application/x-subrip | GREEN |
vtt | text/vtt | GREEN |
json | application/json | GREEN — RFC 8785 canonical |
yaml | text/yaml | GREEN |
xml | application/xml | GREEN |
Not yet supported: png/jpg image output, rtf, odt/ods/odp,
mmd (Mermaid), mp3, mp4, zip. See
packages/reconstruction/docs/matrix.md for the full deferred-formats rationale.
The alias map
Reconstruction takes the detokenised text plus the alias map you already hold for the conversation:
{ "response_text": "Dear Henry James, your invoice for Acme Corp is attached.", "alias_map": { "conversationId": "conv_9f1c2b7a", "hmacPrefix": "a1b2", "entries": [ { "placeholder": "[TOKEN_PERSON_1]", "original": "Henry James" }, { "placeholder": "[TOKEN_ORG_1]", "original": "Acme Corp" } ] }, "target_format": "docx", "input_mime": "application/pdf", "fidelity": "structural"}| Field | Type | Notes |
|---|---|---|
response_text | string | The (already detokenised) text to rebuild. |
alias_map.conversationId | string | Max 256 chars. |
alias_map.hmacPrefix | string | Max 16 chars — the request-scoped HMAC prefix salt the tokeniser stamped on every placeholder it emitted for this conversation. |
alias_map.entries[] | array | Each entry: { placeholder, original }, both strings, max 512 chars each. |
target_format | string | One of the supported formats above. |
input_mime | string | Original file’s MIME type, e.g. application/pdf. Defaults to text/plain. |
fidelity | string | textual | structural | layout | typographic. Defaults to structural. |
Only placeholders whose prefix matches alias_map.hmacPrefix are honoured —
tokens from a different conversation, or text that merely looks like a
placeholder, are rejected by design
(packages/reconstruction/src/alias-resolver.ts).
Rebuild receipt claim
The engine records the rebuild as attestation metadata — target format, requested
and achieved fidelity, input/output MIME, input/output hashes, and an
unresolved-token count. Any value greater than zero for unresolved tokens means
some placeholders in response_text could not be resolved against alias_map
(the file is still produced; those spans are left as literal placeholder text).
The expected shape, folded onto the OpenReceipt envelope alongside the
file-attachment attestation:
{ "claims": { "file_rebuilt": { "target_format": "docx", "fidelity_requested": "structural", "fidelity_achieved": "structural", "input_mime": "application/pdf", "output_mime": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "input_hash": "sha256:...", "output_hash": "sha256:...", "unresolved_tokens": 0, "partial": false } }}Rebuild stays on the device
Because reconstruction is client-side, the alias map and the rebuilt file do not reach CloakAPI’s servers. The gateway only relays the tokenised request and response, and when your client sends an idempotency key it keeps a tokenised copy of the response for 24 hours so a retry returns the same result. In the desktop app the rebuilt bytes are written straight to disk via the native save dialog, never round-tripped over the network.
Portal and desktop apps
Desktop app — a download button in the chat composer lets you pick an output
format; the app rebuilds the file locally (native Rust for md/txt/html/
docx/xlsx) and saves it via the native file dialog. At the max tier
detokenisation and rebuild both stay on-device.
Browser portal — a download button on the response bubble, next to the receipt strip, rebuilds the file from the alias map held in the browser session for that conversation, using the in-browser reconstruction engine.
Related
- File attachments — the ingest half of the round-trip
- Receipts — receipt envelope format and verification
- Privacy model — tokenisation tiers, including why
maxnever reaches the gateway with plaintext