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 link is live before the bytes are
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." (
shareNoticein 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." (
shareNoticein 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).
Password links
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).
What a stranger learns from a dead or guessed link: nothing
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.
Trash kills the link
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."