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 is
a blind relay: it never receives the detokenised text, the alias map, or the
rebuilt file. 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 — sees only tokens, never plaintext) │ 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 never leave the machine that holds the alias map. Nothing about the reconstruction touches CloakAPI’s servers.
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, no data leaves the device. At the max privacy tier — where detokenisation itself happens only on-device — the desktop app rebuilds the file locally too, so the gateway never sees plaintext. - 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 gateway never sees the plaintext or the file:
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 } }}Zero-payload invariant
Because reconstruction is client-side, no request or response body, alias-map entry, or rebuilt file ever reaches CloakAPI’s servers — there is nothing on the gateway to leak. 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