dove
DocumentationCustom share pages
Customization

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:

  1. Fork the page source and change its styles, mark, type, layout, and copy.
  2. Build it as one self-contained HTML document—no required third-party scripts.
  3. Replace dove-core/assets/share.html and/or dove-core/assets/request.html with the corresponding built static page.
  4. Run the relevant tests and inspect the generated asset for network calls.
  5. Re-run dove provision full to 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

RouteBehaviorConsumes 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.pngReturns 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.pngReturns 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 /dl on load. Require an explicit human action so link unfurlers cannot spend one-time shares.
  • Metadata is free; ciphertext is not. Use /meta to 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.