Make the recipient page yours—carefully.
The share page is deliberately replaceable, but its interaction model is part of the security boundary. Brand freely; preserve these invariants.
Current customization path
There is not yet a supported theme file or runtime page-plugin API. The current share and request pages are self-contained HTML assets embedded in dove-core, packaged into the gate Lambda, and served at /d/<id> and /r/<id>. Source-level customization works like this:
- Fork the page source and change its styles, mark, type, layout, and copy.
- Build it as one self-contained HTML document—no required third-party scripts.
- Replace
dove-core/assets/share.htmland/ordove-core/assets/request.htmlwith the corresponding built static page. - Run the relevant tests and inspect the generated asset for network calls.
- Re-run
dove provision fullto update the existing gate Lambda code.
This is an operator fork today. Upstream updates will not automatically merge into a customized embedded page. Diff the security behavior on every update.
The gate routes the page uses
| Route | Behavior | Consumes a download? |
|---|---|---|
GET /d/<id> | Returns the static page. Served even before policy lookup. | No |
GET /meta/<id> | Returns expiry, size, budgets, encrypted metadata, PIN-required and locked flags. | No |
GET /verify/<id>?pin=… | Checks and rate-limits the PIN without releasing ciphertext. | No |
GET /dl/<id>?pin=… | Checks policy, atomically decrements, then redirects to a 15-minute S3 URL. | Yes, on success |
GET /og.png | Returns the generic branded share preview image. | No |
GET /r/<id> | Returns the static request upload page. | No |
GET /rmeta/<id> | Returns request status and opaque encrypted requester metadata. | No |
GET /up/<id>?pin=… | Authorizes an attempt and returns a short-lived presigned S3 POST form. | Counts a handout attempt |
POST /done/<id>?pin=… | Confirms the ciphertext object and completes the request. | Completes on success |
GET /og-request.png | Returns the branded request preview image. | No |
Non-negotiable interaction invariants
- Never transmit
location.hash. Do not add it to fetch URLs, analytics, errors, logs, query parameters, beacons, or crash reports. - Never start
/dlon load. Require an explicit human action so link unfurlers cannot spend one-time shares. - Metadata is free; ciphertext is not. Use
/metato render status and choose browser or CLI flow before the action. - Keep PIN verification separate. A PIN submit may call
/verify; it must not silently fetch the file. Announce remaining attempts and make locked state terminal. - Keep decryption local. Use WebCrypto or the CLI. Never send keys or plaintext to a convenience service.
- Preserve authenticated chunk handling. Reject wrong keys, changed data, reordering, and truncation; do not emit partial unauthenticated plaintext.
- Large files use the CLI. At 2 GB and above, show size and a complete
dove get <url>command instead of buffering the payload in-browser. - Install points to dove.sh. A share origin is operator-controlled and therefore not the canonical place to publish an inline installer.
Safe branding changes
Change color tokens, typefaces, border radius, layout density, illustrations, operator name, and the domain notice. Keep recognizable dove attribution near the local-decryption explanation, clearly display which domain hosts the page, and distinguish operator branding from a claim that dove.sh operates the share.
:root {
--paper: #fbfcff;
--ink: #111c2f;
--action: #164f8a;
--line: #d8e0ec;
} Prefer system or bundled fonts. A remote font or analytics script creates another network observer on a page holding a fragment secret and expands the page’s supply-chain surface.
States a custom page must cover
Share pages need ready, decrypting, browser save, large-file CLI handoff, missing fragment, unsupported browser, PIN required, verifying, wrong PIN with attempts remaining, locked, expired, download limit reached, missing share, integrity failure, and generic gate/network error. Request pages additionally need loading, waiting/ready, file selected, encrypting/uploading progress, complete, request already received, upload-attempt exhaustion, 512 MiB rejection, and upload failure. Focus the new state’s heading, use live regions for progress and errors, and support keyboard and paste on PIN input.
File-request pages
Status: shipped in the current CLI and core source. The request page reads the fragment locally, decrypts requester trust text, optionally verifies a PIN, and waits for an explicit Encrypt & send file action. It then encrypts one selected file in-browser and uploads ciphertext through a gate-issued presigned POST. The current ceiling is 512 MiB.
For requests, the PIN authorizes upload and the fragment secret encrypts content. Do not reuse outgoing-share copy that says the request PIN is folded into the content key. Preserve the gate-chosen object key, S3 content-length policy, completion check, no-action-on-load behavior, and distinct og-request.png preview.