Build SocialFaktory

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 personal video pipeline whose hard problems are proving a render is labelled as synthetic and keeping that marking through every re-encode, never paying twice for a render that timed out rather than failed, and keeping the publishing where the platform credentials already live.

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

RuntimeNode 22, Express and SQLite, one local page with a queue and a review screenScriptOne model call per brief returning a hook, a spoken script and a visual promptRenderA hosted video model through its asynchronous queue API, polled, never re-submitted blindLabellingffmpeg burns a visible label and a verification step re-checks the marking after every encodePublishingYour own self-hosted Postiz instance over its API, never a platform API directly

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 Runs the pipeline and burns the visible label into the video.

    Get it nodejs.org for Node, your package manager for ffmpeg. open ↗

    Verify node --version and ffmpeg -version both print

  2. API keypennies per script

    Why Writes the hook, script and visual prompt from the brief.

    Get it Create a key with the provider you prefer and set a spend limit in their console.

    Verify One brief returns three validated fields

  3. API keybilled per second of output; measure one render before planning a schedule

    Why This is the expensive call and the one that must never be made twice for the same script.

    Get it Create a key with a provider whose queue API returns a request identifier you can poll.

    Verify One submit returns a request id and one poll returns a finished file

  4. free software; your own hosting

    Why It holds the platform credentials and the labelling fields, and it is the reason this build does not need a single platform developer account.

    Get it Run Postiz yourself and connect the channels you post to, then create an API key in it. open ↗

    Verify A test post from this app appears in the Postiz queue

  5. decidefree

    Why Article 50 covers disclosure; a person's voice and face are a separate matter and the one that produces angry messages.

    Get it List the stock presenters you will use, and for any real person obtain and file written permission with a date.

    Verify Every presenters row of kind real_person has a consent reference and date

Data model

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

- briefs(id, topic, tone, target_seconds, created_at, brand_id)
- scripts(id, brief_id, hook, spoken, visual_prompt, model_id, prompt_sha, cost_minor, created_at)
- renders(id, script_id, provider, model_id, provider_request_id, state, mp4_path, seconds, cost_minor, submitted_at, finished_at) · provider_request_id is written before the first poll, not after
- presenters(id, kind, source, consent_ref, consent_granted_at, notes) · kind is stock or real_person, and a real person without a consent_ref cannot be selected
- disclosures(render_id, visible_label, burnt_in_at, marking_present_at_download, marking_present_after_encode, checked_at)
- publications(id, render_id, postiz_post_id, channel, scheduled_for, ai_flag_set, published_at, state)
- No row reaches publications without a disclosures row whose two marking checks are recorded, and no render starts without a presenter row that carries its consent evidence.

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
SCRIPT_API_KEYsecretrequiredsk-...redactedThe text model that writes scripts.
VIDEO_API_KEYsecretrequiredkey-...redactedThe video model. Set a provider-side spend limit as well.
VIDEO_MODEL_IDrequireda dated video model identifierStored per render, because output quality and pricing both move.
POSTIZ_URLrequiredhttps://postiz.your-host.exampleYour own instance. This app talks to nothing else on the network except the two model providers.
POSTIZ_API_KEYsecretrequiredpz-...redactedCreated inside your Postiz instance.
DISCLOSURE_LABELrequiredAI-generated videoBurnt into every render. Cannot be empty.
MEDIA_DIRrequired./mediaDownloaded and encoded files. These are the expensive artefacts, so back them up.
MONTHLY_COST_CEILINGrequired60.00Submissions stop here. Review and publishing keep working.

The build, in order

  1. A brief becomes a script you can read

    One model call turns a topic and tone into a hook, a spoken script and a visual prompt, all stored and editable.

    1. You will tune this weekly and need to know which version wrote what.

      Files prompts/script.mdsrc/script/generate.ts

    2. A missing hook is better as an error than as an empty caption.

      Files src/script/schema.tstests/script.test.ts

    3. The render is the expensive step and the script is free to fix.

      Files src/web/script.ejs

    done when · tick each as it passes
    watch out
    • Letting the script step feed straight into the render step means you pay for video before a human has read a word, and a script that misreads the brief costs a full render to discover. Put the script on screen as text with an edit box, and make rendering a separate deliberate action.
  2. A render that is never paid for twice

    Submitting to the video model records the request identifier before polling, and a restart resumes rather than resubmits.

    1. That identifier is the only proof you already paid.

      Files src/render/submit.tstests/resume.test.ts

    2. A crash is not a reason to buy the clip again.

      Files src/render/resume.ts

    3. The runaway retry loop is the failure mode with a price tag.

      Files src/render/cap.ts

    done when · tick each as it passes
    watch out
    • A polling loop that treats a slow job as a failure and submits again turns one twenty-second clip into two invoices, and video models are priced per second of output rather than per call, so this is the mistake that actually hurts. Store the request id before the first poll, resume from it after any crash, and put a hard cap on how many times a script may be submitted at all.
  3. The label, and keeping it through the encode

    Every finished video has a visible synthetic-content label and its machine-readable marking is checked twice.

    1. You need a before to have a loss.

      Files src/label/marking.ts

    2. Re-encoding is where provenance quietly dies.

      Files src/label/burn.tstests/label.test.ts

    3. Unknown is not the same as present.

      Files src/publish/gate.ts

    done when · tick each as it passes
    watch out
    • Most transcoding drops metadata it does not recognise, so the provenance marking a generator attached at creation is routinely lost the moment you re-encode to burn in a caption, and nobody notices because the video still plays. Check for the marking on the downloaded file and again on the encoded one, store both results, and treat a silent loss as a finding rather than an accident.
  4. Who is in this video, and did they agree

    A presenter is chosen from a register, and a real person cannot be used without recorded consent.

    1. A foreign key can be checked; a sentence cannot.

      Files src/db/migrations/003_presenters.sql

    2. A warning is a thing people click through at eleven at night.

      Files src/render/presenter.tstests/consent.test.ts

    3. The moment of approval is when it matters.

      Files src/web/review.ejs

    done when · tick each as it passes
    watch out
    • Treating the presenter as a string in the visual prompt means nothing in the system knows whether the face belongs to a stock model or to a colleague whose voice you sampled from a meeting recording, and the difference is the entire legal exposure. Make the presenter a foreign key, make consent evidence mandatory on real-person rows, and block the render rather than warning about it.
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 social media.