Build Feedspace
KINDAreplaces $29/mosaves $348/yrback to the verdict
A tool for collecting written, audio and video testimonials and embedding them, where the entire design constraint is that the record must stay honest: originals preserved, incentives and relationships recorded, edits visible, and suppression measured rather than hidden.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the server, the widget build and the transcoding worker.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - installfree locally
Why Immutable originals, append-only publication history and consent records want real constraints.
Get it Docker or your package manager. open ↗
Verify
psql connects and an insert-only table can be enforced - accountpennies per month at small volume
Why Video testimonials are large and uploading them through your application server is how the first long submission takes the site down.
Get it Any provider supporting presigned direct uploads. Configure a lifecycle policy before storing anything.
Verify
A large file uploads directly from a browser without passing through your server - decidefree to read; an hour of advice if this is commercial
Why This build encodes the United States position, which took effect on 21 October 2024 with civil penalties currently $51,744 per violation. Your jurisdiction may differ or be stricter, and the tool's behaviour should match the stricter one.
Get it Identify where your customers are, read the applicable consumer protection guidance, and write down what you concluded. open ↗
Verify
A written note names the jurisdictions and what each requires of testimonials - have readyfree
Why The interesting cases are the ones the tool is meant to handle: a review from an employee, one where the person got a discount, and one that is lukewarm rather than glowing.
Get it Collect real ones with permission, and deliberately include an insider and an incentivised submission to test the disclosure path.
Verify
Ten exist, including at least one insider and one incentivised
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- testimonials(id, kind, author_name, author_email, submitted_at, source, status) · source is direct, imported or manual - originals(testimonial_id, text, media_path, captured_at) · immutable, written once, never updated - revisions(testimonial_id, ordinal, text, edited_by, reason, at) · every change to displayed text, with the original always available - disclosures(testimonial_id, incentive_kind, incentive_value, relationship) · relationship is customer, employee, investor, family or other - consents(testimonial_id, scope, granted_at, evidence, withdrawn_at) · scope is what they agreed you could do with it - publications(testimonial_id, published, changed_by, reason, at) · append only, so unpublishing is a recorded act - collection_stats(period, received, published, unpublished) · the suppression figure, computed and shown rather than available on request
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.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DATABASE_URLsecret | required | postgres://localhost:5432/testimonials | Testimonials, originals and consent evidence. Personal data about identified people. |
MEDIA_BUCKET | required | s3://testimonials-media | Audio and video. Direct uploads, with a lifecycle policy set before anything is stored. |
WIDGET_ORIGIN | required | https://embed.example.com | Where the widget is served from. Separate origin, so a host page cannot reach into it. |
SHOW_SUPPRESSION_RATIO | required | always | Present as a constant so the answer is visible in configuration. There is no other value. |
CONSENT_RETENTION_YEARS | required | 7 | Consent evidence outlives the testimonial, because it is what proves you were allowed to publish it. |
The build, in order
Collection that captures the disclosures with the words
A testimonial cannot be submitted without the facts you will later need to disclose.
Incentive and relationship. Both required, answered by the person themselves.
Files
src/collect.tsNo update path in the schema. It is the evidence.
Files
src/schema.sqlThrough your server is how the first long video takes the site down.
Files
src/uploads.ts
done when · tick each as it passeswatch out- Asking about incentives and relationships later, when preparing to publish, produces answers reconstructed from memory or simply guessed, which is exactly the record that will not hold up. Ask at collection, when the person themselves can answer, and make both fields required rather than optional.
Editing that stays honest
You can tidy a testimonial without being able to change what it says.
Append only. The original is always one click away.
Files
src/revisions.tsLength changes and meaning changes are different acts and deserve different friction.
Files
src/diff.tssrc/diff.test.tsOn display, not in an admin panel nobody reads.
Files
src/render.ts
done when · tick each as it passeswatch out- An unlogged edit field turns a testimonial into a quotation you wrote yourself, and the person who said it has no idea. Every displayed version is a revision with the original beside it, and trimming for length is a different act from changing a word, so make the tool distinguish them.
Publishing, and the number nobody wants to see
You can choose what to show, and you can see what that choice looks like.
Who, when and why. Unpublishing is an act, not a state.
Files
src/publications.tsIn the dashboard. A number in a report nobody opens is not a control.
Files
src/stats.tsAn average excluding unpublished testimonials must say so beside the number.
Files
src/aggregate.ts
done when · tick each as it passeswatch out- A publish toggle with no visible consequence is a review suppression mechanism, and the operator using it will not think of it that way until a regulator does. Compute the suppression ratio, display it in the dashboard rather than in a report, and make an aggregate rating that excludes unpublished testimonials say so.
Showing them, with the disclosure attached
An embedded testimonial carries its disclosure wherever it appears.
Same view, comparable visual weight to the author's name.
Files
widget/render.tsIt is on someone else's page. It must not slow it down.
Files
widget/embed.tsA widget that inherits the host's styling breaks unpredictably.
Files
widget/isolate.ts
done when · tick each as it passeswatch out- Putting the disclosure behind a link, in a tooltip, or in small print below the fold makes it a disclosure that was technically present and practically absent, which is the arrangement the rule was written about. Render it adjacent to the testimonial in the same visual weight as the author's name.
Imports, which arrive without their provenanceproduct builder
Testimonials brought in from elsewhere are marked as what they are.
Until provenance is answered. Mixing them with collected ones is irreversible.
Files
src/import.tsWhere it came from, when, and who imported it.
Files
src/import.tsOriginals, revisions, disclosures and consent. It is the evidence.
Files
src/export.ts
done when · tick each as it passeswatch out- An import that lands testimonials in the same state as directly collected ones erases the distinction between a testimonial you have consent evidence for and one you copied from somewhere, and once they are mixed you cannot tell them apart again. Mark on import, block publication until reviewed, and keep the original source recorded.
That is the whole plan for Feedspace. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Legal advice about your jurisdiction.
- Automated video editing and captioning.
- Review platform integrations.
- Sentiment ranking of testimonials.
- 150+ review sources with 120+ on auto-sync, on an extraction engine Feedspace owns and runs in-house
- the upkeep · sources change and keeping the fetching working is someone else's job
- rule-based curation · matching new reviews join a widget or wall page by themselves, so the embed never goes stale
- in-browser video and audio recording that works on mobile without an app, and the media pipeline behind it
- aggregate rating badges that stay accurate because the auto-syncs keep feeding them
- A request flow that asks past customers at the right moment, which is where collection volume comes from
- Response to a testimonial, since replying publicly is often better than hiding a lukewarm one
- Per-page widget variants, once the disclosure rendering is proven
Need the files? The project pack on the verdict page hands your agent the whole brief · more testimonials.