Build Escourtly

KINDAreplaces $49/mosaves $588/yrback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A demo recorder built on DOM snapshots, whose hard problems are capturing enough of a page that the replay renders without calling home, masking by default rather than by accident, and replaying untrusted captured markup without letting it run.

estimated effort multi-daythe files for this build are in the project pack

CaptureA Chrome extension recording one DOM snapshot per click with rrwebMaskingInverted defaults: inputs and text masked at capture, unmasked deliberatelyStoragePostgres with Drizzle, snapshots stored as compressed documentsReplayThe snapshot rendered in a sandboxed frame with scripts disallowedAnalyticsFirst-party counters in your own database

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Builds the extension and runs the web application.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. installfree locally, a few pounds a month managed

    Why Snapshots are large documents and you will keep more versions of them than you plan to, which is a database question rather than a file question.

    Get it Docker, your package manager, or a managed instance. open ↗

    Verify The application connects and migrations run

  3. installfree

    Why The capture half only runs as an extension, and the whole loop is untestable until one is loaded.

    Get it Chrome, then enable developer mode on the extensions page.

    Verify The unpacked extension loads and its indicator appears on a page

  4. have readyfree

    Why Capture fidelity and masking are only testable against a real signed-in application, and it must be one you have permission to record.

    Get it Your own product in a staging environment, seeded with obviously fake data.

    Verify A signed-in page captures and replays with the origin blocked

  5. decidefree

    Why Both concern other people's data appearing in your database, and both are decisions that quietly default to the worst option if nobody makes them.

    Get it Write down what is masked by default, who may unmask, and how long unpublished captures are kept.

    Verify All three are in the configuration and the README

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- demos(id, slug, title, status, created_at, published_at, cta_label, cta_url) · slug is generated, not taken from the title
- steps(id, demo_id, ordinal, snapshot_ref, target_selector, target_box_json, title, description, reviewed_at)
- snapshots(id, document_compressed, bytes, asset_count, inline_failures_json, captured_url_host) · the host, not the full URL, because the path often carries identifiers
- assets(snapshot_id, original_url, kind, bytes, inlined, failure_reason) · what got inlined and what did not, so a blank replay is diagnosable
- redactions(step_id, selector, kind, applied_at) · every unmasking and every extra mask, recorded as an action somebody took
- views(demo_id, session_hash, started_at, last_step, completed_at, cta_clicked_at) · first-party, no third-party script anywhere
- A published demo is a set of frozen documents. Nothing in the replay path talks to the product that was captured, which is what makes it survive a deploy.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DATABASE_URLsecretrequiredpostgres://user:redacted@localhost:5432/demosSnapshots and analytics. Snapshots are the sensitive part.
PUBLIC_BASE_URLrequiredhttps://demos.example.comWhere published demos live. Used in embed snippets.
SNAPSHOT_MAX_MBrequired8Per step, after inlining. A capture over this is refused with the largest asset named.
EMBED_FRAME_ANCESTORSrequiredhttps://www.example.com https://example.comWho may embed a demo. Empty means nobody but you.
SLUG_BYTESrequired12Randomness in a share link. The link is the access control.
DRAFT_RETENTION_DAYSrequired90Unpublished captures expire, because a draft is still captured data.
LLM_API_KEYsecretoptionalsk-...redactedOptional first-draft step descriptions. Without it you write them yourself and nothing else changes.

The build, in order

  1. Capture with masking on from the first click

    The extension records a snapshot per click with input values and text masked by default.

    1. The defaults mask passwords; your users' data is not a password.

      Files extension/record.tstest/masking.test.ts

    2. The hotspot in the replay is only as good as what you captured about the click.

      Files extension/clicks.ts

    3. Any capture tool without a visible indicator is a surveillance tool by accident.

      Files extension/indicator.ts

    done when · tick each as it passes
    watch out
    • Recording with the library's defaults masks passwords and nothing else, so the first capture of a real admin screen carries live customer names and addresses into your database, where they are now your responsibility and your breach if it leaks. Set the masking options explicitly at capture, and write a test that captures a page of sample data and asserts none of it appears in the stored document.
  2. Snapshots that render without calling home

    Stylesheets, fonts and images are inlined, and whatever fails is recorded rather than silent.

    1. The replay has to be self-contained or it is a screenshot with extra steps.

      Files extension/inline.ts

    2. A blank replay with a reason is a bug; one without is a mystery.

      Files src/snapshots/assets.ts

    3. Snapshots grow without limit and the ceiling is what keeps the database usable.

      Files extension/limits.ts

    done when · tick each as it passes
    watch out
    • A snapshot that still references the origin's asset host renders perfectly on your machine, where you are signed in and inside the network, and renders as unstyled text for the prospect outside it, or three weeks later when the asset hash changes on a deploy. Inline everything at capture, record the failures, and refuse to publish a step whose stylesheet did not inline.
  3. A review screen nobody can skip

    Every captured text node is seen by a person before the demo can be published.

    1. The gate is the feature; a checklist in the documentation is not.

      Files src/review/gate.tstest/review-gate.test.ts

    2. Someone decided to reveal that. The record says who and when.

      Files src/review/redactions.ts

    3. People skim a page and read a list.

      Files src/review/TextList.tsx

    done when · tick each as it passes
    watch out
    • Making review optional means it happens for the first demo and never again, and the demo that leaks a customer name is always the sixth one, made in a hurry before a call. Gate publishing on a review timestamp per step, and reset every timestamp when a step is recaptured.
  4. Replaying captured markup without running it

    The snapshot renders as a page with a working hotspot and none of its scripts execute.

    1. Captured markup is input from another application. Treat it that way.

      Files src/replay/Frame.tsx

    2. A demo nobody can drive with a keyboard excludes people for no reason.

      Files src/replay/Hotspot.tsx

    3. A blank frame reads as a broken product, which is the opposite of the point.

      Files src/replay/Fallback.tsx

    done when · tick each as it passes
    watch out
    • Turning on script execution in the replay frame to make an interactive widget look right removes the protection that stops captured markup from running, and the library's own canvas replay option carries exactly that warning about adding script permission to the frame. Keep scripts off, accept that some widgets render as static, and say so in the product rather than trading a sandbox for a hover effect.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more screen recording.