Knowledge base

Sharing

Share links, passwords, unfurls, the MP4 note, and exactly what a stranger can and cannot learn from a link.

None of the shipped strings quoted here contains an em dash. They are verbatim, except that the curly apostrophe in "This page isn't available" is shown straight.

A share link works the moment the row is registered, before any bytes arrive. The viewer says so itself: "Still processing. This page will show it as soon as it is ready - the link already works, so you can send it now." (shareNotice in apps/web/src/lib/share-access.ts; joined-to-code: apps/web/src/app/s/[token]/share-viewer.test.ts).

The other two notice sentences, verbatim:

  • Failed transcode: "This one could not be processed, so there is nothing to play. Whoever shared it will need to send it again." (shareNotice in apps/web/src/lib/share-access.ts)
  • Every other no-player state: "There is nothing here to show. Whoever shared it will need to send it again." (shareNotice in apps/web/src/lib/share-access.ts)

Deny-by-default: only a row explicitly in processing is told to wait; everything else gets the dead-end sentence (shareNotice, apps/web/src/lib/share-access.ts; joined-to-code).

The gate is a plain form that works with JavaScript off and inside email preview panes. Wrong password: "That password is not right." (apps/web/src/app/s/[token]/page.tsx) Too many tries: "Too many attempts. Wait a few minutes and try again." (apps/web/src/app/s/[token]/page.tsx; the attempt limit is ten per link per ten minutes, MAX_ATTEMPTS and WINDOW_MS in apps/web/src/lib/share-password.ts). The unlock cookie is minted per link: the token is inside the signed message, so a cookie for link A proves nothing about link B (apps/web/src/lib/share-password.ts; joined-to-code: its unit tests and apps/web/e2e/share-password.spec.ts).

A gated link gives up nothing to a channel: unfurls and the tab title read "Password protected", never the real name, because the title is frequently the whole secret (shareTitle in apps/web/src/lib/share-access.ts; joined-to-code: oembed.test.ts and share-viewer.test.ts).

A link that does not exist, has expired or was turned off, a link to a capture that was deleted (in the trash or for good), and a link to a recording that carries a mark that covers part of the picture (Redact, Grey out, Blur, Pixelate or Filled box) all collapse into one identical page titled "This page isn't available" (apps/web/src/app/not-found.tsx), on purpose. The error page does not distinguish "never existed" from "existed and was deleted", so probing tokens teaches nothing.

A recording with such a mark is refused whatever the mark was drawn for, because the mark is drawn over the video, not into it. Its page in your library on the web says so under the recording: "Marks that cover part of the picture are drawn over this recording, not into it - the file still contains what they cover, so its share link does not open while they are on it. Burn them in to write a new file with the pixels changed, and share that copy." (OVERLAY_MARKS_NOTE in apps/web/src/lib/media.ts) On that page an editor sees a Burn in button once the recording has an MP4 download and is out of the trash. A copy it saves is a new recording in your library, with a link of its own (createBrowserRecording in apps/web/src/lib/library/recording-actions.ts). It saves nothing for a recording too long for the browser's 200 MB upload (about three minutes at 1920x1080), in a browser that cannot record video, or while the workspace is at its plan's item limit.

In your library on the web, deleting a capture kills its share link immediately and restoring revives it. Pinned live by apps/web/e2e/library.spec.ts: 200, then 404 in trash, then 200 after restore (joined-to-code). See library-and-trash.md.

Downloads

The MP4 download note reads "MP4 re-encoded by Cloudflare - not the original file." (DOWNLOAD_NOTE in apps/web/src/lib/media.ts; one shared constant rendered on both the share viewer and the dashboard asset page; joined-to-code: share-viewer.test.ts and player.test.ts).

The Download link deliberately still appears when the player area shows a notice, because the file can be finished while the player is not, and in that state the link is the only thing on the page with the capture behind it (apps/web/src/app/s/[token]/page.tsx; joined-to-code: share-viewer.test.ts download gating).

allowDownload is an affordance, not protection: it decides whether the link is rendered, and the file URL is world-readable either way. Do not describe it as access control.

No analytics on the viewer

The share viewer carries no analytics. PostHog mounts only inside the signed-in dashboard layout (apps/web/src/app/dashboard/layout.tsx), and scripts/check-promises.mjs fails the build if it ever moves back to the root layout (joined-to-code).

The sentence a support agent can paste

"The link starts working as soon as it is created; if the page says it is still processing, the recording just has not finished uploading or converting yet. Send the link whenever you like."