Vibecode Ghostfeed
track this build4 phases, 12 steps, beginner friendly0%No model here was trained by Ghostfeed · every step is somebody else's hosted inference, spread across four vendors. A Gemini image-edit call swaps your avatar into frame 0 of an existing clip, an approval gate makes you accept that frame before anything expensive runs, and then the video step splits: the default clone family sends the approved frame plus the original clip to Kling motion-control, so the render inherits the source video's motion, while the prompt family animates the still alone on PixVerse, Seedance, Grok or Kling. Rebuild the loop for yourself with a fal.ai key, a Gemini key and ffmpeg over a long weekend and it works. What does not fall out of that weekend is the rest of it · a template library scraped and scene-cut into 9:16 clips, a director that writes slideshow copy and casts every background itself, a timeline editor with its own render workers, 44 agent-API tools, and a TikTok app that took platform review to get. Personal version, yes. The thing you would actually run daily, no.
You are building a lean indie version of Ghostfeed. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Ghostfeed · indie build An avatar video pipeline where consent and licensing are enforced as data rather than assumed, and the engineering is in driving a likeness from a performance convincingly enough to be watchable while marking every frame of output as synthetic. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Consent | A signed record per depicted person, with scope and revocation | the likeness right belongs to the person and can be withdrawn | | Avatars | Built only from footage whose subject consented | an avatar is a digital replica and that is the thing the statutes cover | | Driving | Motion and speech from a performance you own or licensed | the source performance is somebody's work as well as somebody's movement | | Rendering | A local or self-hosted pipeline with provenance embedded | output leaving unmarked is the failure the whole design prevents | | Runtime | Python 3.12 with a job queue | renders are long, fail, and need to be resumable | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Python 3.12 or newer** · free - Why: Orchestrates the pipeline and the job queue. - Get it: python.org or your package manager, then a virtual environment. - Verify: python3 --version prints 3.12 or higher - [ ] **A machine that can render locally** · free if you own the hardware; substantial to rent - Why: Sending someone's consented likeness to a third party service is a second disclosure they did not agree to, and renders are long enough that hosted costs add up quickly. - Get it: A machine with enough video memory for your chosen model, or a self-hosted instance you control. - Verify: A one minute render completes locally - [ ] **Your consent form and scope list** · free to draft; advice worth having if this is commercial - Why: Consent is enforced as data with named scopes, and inventing the scopes after people have signed means the signatures do not cover them. - Get it: Write the form, list the specific uses it permits, state the expiry and the withdrawal process. Have someone read it who is not you. - Verify: A written form exists naming specific permitted uses, an expiry and a withdrawal route - [ ] **Footage of someone who has actually consented** · free - Why: Every phase depends on there being a consented avatar, and the obvious first subject is yourself. - Get it: Record yourself, or someone who has signed the form, with the range of head movement the driving will need. - Verify: Footage exists with a matching signed consent record - [ ] **Which digital replica rules apply to you** · free to read - Why: Eight states have specific statutes and there is no federal right, so the rule depends on where the depicted person is and where the work is distributed. - Get it: Identify where your subjects are and where output will be seen, then take the strictest applicable position. - Verify: A written note names the jurisdictions and the rule applied ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Using a likeness without a consent record. - Ingesting clips from platforms. - Voice cloning without specific voice consent. - Legal advice on the state patchwork. - a template library scraped from TikTok and Instagram and scene-cut into ready 9:16 clips, instead of clips you source and trim yourself - avatar consistency across renders · one stored reference photo re-fed into every call, plus the draft-and-approve flow that keeps a face you picked - a director loop that writes the slideshow copy, searches for backgrounds, looks at the candidates and casts them, then rotates the deck into variants, plus a timeline editor with its own render workers - 44 agent-API tools · the same workspace drivable from Claude, which is the part with no weekend equivalent - a TikTok app that already cleared review, and a reaper that refunds most stalled renders instead of quietly eating them If one of those is essential to you, that is the reason to keep paying for Ghostfeed, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Ghostfeed The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me an avatar video tool that will not use a person or a clip you have no right to to replace Ghostfeed. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero The product this is modelled on drops your avatar into someone else's viral clip and clones its motion. That is two separate takings: the clip is a copyrighted work belonging to whoever shot it, and the person in it has rights in their own likeness and voice. The likeness side is no longer vague. Eight states now have digital replica statutes specifically covering AI-generated voice and likeness, including Tennessee's ELVIS Act in effect since July 2024, alongside California, Illinois, New York, Utah, Arkansas, Montana and Washington. The federal NO FAKES Act advanced out of the Senate Judiciary Committee in 2026 but is not law, so what exists is a patchwork of state rights rather than one rule. Separately, the EU AI Act's Article 50 transparency obligations require synthetic content to be marked and disclosed. So this build has one rule that shapes everything: an avatar requires a consent record from the person it depicts, and a source clip requires a licence record. No consent, no render. Every output is marked as synthetic. There is no path that takes a clip from a URL and no path that builds an avatar from footage of someone who did not agree. What remains is genuinely useful: your own avatar, your own or licensed footage, and a pipeline that produces short video quickly. That is what follows. ### Stack (fixed, do not substitute) - Consent: A signed record per depicted person, with scope and revocation · the likeness right belongs to the person and can be withdrawn - Avatars: Built only from footage whose subject consented · an avatar is a digital replica and that is the thing the statutes cover - Driving: Motion and speech from a performance you own or licensed · the source performance is somebody's work as well as somebody's movement - Rendering: A local or self-hosted pipeline with provenance embedded · output leaving unmarked is the failure the whole design prevents - Runtime: Python 3.12 with a job queue · renders are long, fail, and need to be resumable ### Data model (create this before Phase 1) - people(id, name, contact, created_at) - consents(id, person_id, scope_json, granted_at, evidence_path, expires_at, revoked_at) · scope names the uses permitted - avatars(id, person_id, consent_id, source_footage_path, built_at, disabled_at) - sources(id, kind, path, licence_id, rights_holder, obtained_from) · kind is performance, audio or background - licences(id, name, allows_derivative, allows_commercial, allows_synthetic_use, source_url) - renders(id, avatar_id, source_id, script_id, output_path, marked_synthetic, at) - revocation_actions(consent_id, at, renders_withdrawn, avatars_disabled) · because withdrawal has to actually do something - A render cannot exist without a live consent and a licensed source, and the foreign keys make that structural rather than procedural. ### Phase 1 · Consent as a record with teeth Build: Consent records naming the person, the permitted uses, an expiry and evidence of how it was given. Avatars reference a consent. Withdrawal disables the avatar and marks existing renders as withdrawn. Trap: A consent checkbox with no revocation path is not consent in any meaningful sense, and the first person who changes their mind will find there is nothing the tool can do. Build withdrawal in the same phase as consent, and make it actually disable the avatar and flag the renders rather than only setting a flag on a row. Done when: An avatar cannot be created without a live consent, enforced by the schema. Withdrawal disables the avatar and marks every render made from it, tested end to end. Consent scope names specific uses rather than being a single boolean. An expired consent blocks new renders automatically. Do not build yet: No source licensing, driving or rendering. ### Phase 2 · Source material you have rights to Build: Store sources with a licence recording derivative use, commercial use and synthetic use as separate permissions. Refuse to use a source lacking the permission the render needs. Provide no path that ingests a clip from a URL. Trap: A licence that permits derivative works does not necessarily permit using the footage to drive a synthetic performance, and treating those as the same question is how a permitted use becomes an unpermitted one. Record synthetic use separately, and default it to not permitted where the licence is silent. Done when: A source without a licence record cannot be used in a render, tested by attempting it. Synthetic use is recorded separately from derivative use and defaults to not permitted when the licence is silent. There is no path that fetches a clip from a URL, verified by looking for one. A render exports its full source provenance. Do not build yet: No driving or rendering yet. ### Phase 3 · Driving a likeness convincingly enough Build: Drive the avatar from a performance's motion and a speech track, keeping lip movement aligned to the audio's own timings. Handle head turns and occlusion. Detect output that falls into the range where it looks wrong and flag it rather than shipping it. Trap: Lip movement estimated from text rather than from the audio's actual phoneme timings drifts out of sync within a sentence, and unlike a caption drifting, a mouth out of sync is immediately and viscerally noticeable. Take the timings from the speech itself. Done when: Lip movement is driven by the audio's own timings and stays aligned over a two minute render, verified by watching. Head turns and partial occlusion do not produce artefacts. Output quality is scored and low scores are flagged rather than delivered. A render is resumable after interruption. Do not build yet: No marking or publishing yet. ### Phase 4 · Marking every output as what it is Build: Embed provenance metadata marking the output as AI generated, and burn in a visible indication where the intended distribution requires one. Make an unmarked render unexportable in the export path rather than by convention. Trap: Marking in the database and not in the file means the marking is lost the moment the file is copied anywhere, which is immediately. The mark has to travel with the bytes, and the export path has to read it back from the file rather than trusting a column. Done when: Every export carries provenance metadata read back from the file itself, not from the database. An unmarked file cannot be exported, tested by stripping the metadata and retrying. Visible marking is applied where the configured distribution requires it. The marking survives a re-encode by common tools. Do not build yet: No distribution or reporting yet. ### Phase 5 · Withdrawal that reaches published work Build: Record where every render was published. On withdrawal, list every published output derived from that person and support taking them down. Report what could not be withdrawn and why. Trap: Treating withdrawal as a future-only restriction leaves everything already published in place, which is exactly what the person is asking about when they withdraw. Track publication destinations per render so withdrawal produces an actionable list rather than an apology. Done when: Withdrawal produces a complete list of published outputs derived from that person, tested with renders published to two destinations. Takedown is supported where the destination allows it and reported where it does not. The withdrawal action and its outcome are recorded. A person can ask what exists of them and get a complete answer. Do not build yet: Nothing after this. ### Out of scope (and why) - Using anyone's likeness without a consent record, which is what the category is usually built to do and what the state digital replica statutes address. - Ingesting clips from platforms, which takes someone's copyrighted work without a licence and has no place in the build. - Voice cloning of a person who has not consented specifically to voice use, which several statutes treat separately from likeness. - Legal advice. The state patchwork differs and there is no federal right, so where you are matters. ### README must contain - That an avatar requires a live consent record and withdrawal actually disables it - That synthetic use is a separate licence permission from derivative use and defaults to refused - That there is no path to ingest a clip from a URL, deliberately - That provenance marking lives in the file and is read back from it before export - That withdrawal produces a list of published outputs rather than a future-only restriction ===== AGENTS.md ===== # Agent instructions · Ghostfeed indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: A signed record per depicted person, with scope and revocation, Built only from footage whose subject consented, Motion and speech from a performance you own or licensed, A local or self-hosted pipeline with provenance embedded, Python 3.12 with a job queue. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - A consent checkbox with no revocation path is not consent in any meaningful sense, and the first person who changes their mind will find there is nothing the tool can do. Build withdrawal in the same phase as consent, and make it actually disable the avatar and flag the renders rather than only setting a flag on a row. - A licence that permits derivative works does not necessarily permit using the footage to drive a synthetic performance, and treating those as the same question is how a permitted use becomes an unpermitted one. Record synthetic use separately, and default it to not permitted where the licence is silent. - Lip movement estimated from text rather than from the audio's actual phoneme timings drifts out of sync within a sentence, and unlike a caption drifting, a mouth out of sync is immediately and viscerally noticeable. Take the timings from the speech itself. - Marking in the database and not in the file means the marking is lost the moment the file is copied anywhere, which is immediately. The mark has to travel with the bytes, and the export path has to read it back from the file rather than trusting a column. ===== BUILD_PLAN.md ===== # Build plan · Ghostfeed An avatar video pipeline where consent and licensing are enforced as data rather than assumed, and the engineering is in driving a likeness from a performance convincingly enough to be watchable while marking every frame of output as synthetic. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Consent as a record with teeth An avatar cannot exist without a consent that can be withdrawn. ### Steps 1. Model scope as named permitted uses A single boolean is not consent to anything specific. Files: `src/consent.py` 2. Make avatars require a live consent A foreign key, not a check. Structural rather than procedural. Files: `src/schema.sql` 3. Implement withdrawal now Disabling the avatar and flagging renders. Consent without it is decorative. Files: `src/revoke.py`, `tests/test_revocation.py` ### Done when - [ ] An avatar requires a live consent - [ ] Withdrawal disables the avatar and flags renders - [ ] Scope names specific uses - [ ] An expired consent blocks new renders ### Watch out - A consent checkbox with no revocation path is not consent in any meaningful sense, and the first person who changes their mind will find there is nothing the tool can do. Build withdrawal in the same phase as consent, and make it actually disable the avatar and flag the renders rather than only setting a flag on a row. ## Phase 2 · Source material you have rights to Every clip, voice and background carries a licence permitting synthetic use. ### Steps 1. Record three permissions separately Derivative, commercial, synthetic. A licence may allow one and not the others. Files: `src/licences.py` 2. Default synthetic use to not permitted Where the licence is silent, which is most of the time. Files: `src/licences.py` 3. Provide no URL ingestion Its absence is the design. A clip from a URL has no licence record. Files: `src/sources.py` ### Done when - [ ] An unlicensed source cannot be used - [ ] Synthetic use is separate and defaults to refused - [ ] No URL ingestion path exists - [ ] Renders export their source provenance ### Watch out - A licence that permits derivative works does not necessarily permit using the footage to drive a synthetic performance, and treating those as the same question is how a permitted use becomes an unpermitted one. Record synthetic use separately, and default it to not permitted where the licence is silent. ## Phase 3 · Driving a likeness convincingly enough The avatar moves and speaks in a way that is watchable. ### Steps 1. Drive the mouth from audio timings Not from text. Estimated timing drifts within a sentence and is very visible. Files: `src/lipsync.py` 2. Handle turns and occlusion Where these pipelines produce their worst artefacts. Files: `src/drive.py` 3. Score and flag poor output Shipping a bad render is worse than reporting one. Files: `src/quality.py` ### Done when - [ ] Lip movement stays aligned over two minutes - [ ] Turns and occlusion do not produce artefacts - [ ] Low quality output is flagged, not delivered - [ ] An interrupted render resumes ### Watch out - Lip movement estimated from text rather than from the audio's actual phoneme timings drifts out of sync within a sentence, and unlike a caption drifting, a mouth out of sync is immediately and viscerally noticeable. Take the timings from the speech itself. ## Phase 4 · Marking every output as what it is No file leaves this pipeline without being identifiable as synthetic. ### Steps 1. Embed in the file, verify by reading back A database column is lost the moment the file is copied. Files: `src/marking.py` 2. Block export on missing marking Read from the file. Test by stripping it deliberately. Files: `src/export.py`, `tests/test_marking.py` 3. Apply visible marking where required Metadata satisfies machine readability, not human disclosure. Files: `src/overlay.py` ### Done when - [ ] Provenance is read back from the file - [ ] A stripped file cannot be exported - [ ] Visible marking applies where required - [ ] Marking survives a common re-encode ### Watch out - Marking in the database and not in the file means the marking is lost the moment the file is copied anywhere, which is immediately. The mark has to travel with the bytes, and the export path has to read it back from the file rather than trusting a column. ## Not in this build - Using a likeness without a consent record. - Ingesting clips from platforms. - Voice cloning without specific voice consent. - Legal advice on the state patchwork. ## After v1, if you want it - Consent renewal reminders, since expiry is how these records quietly lapse - A subject-facing page where a person can see what exists of them and withdraw - Batch rendering with per-render consent checks, once the single path is proven ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Consent records and provenance. The evidence you were permitted to make these. DATABASE_URL=postgres://localhost:5432/avatars # Required. Present as a constant so the answer is visible. There is no other value. CONSENT_REQUIRED=always # Required. Embedded in the file and verified before export. SYNTHETIC_MARKING=always # Required. Present as a constant. A clip from a URL arrives without a licence record. URL_INGESTION=disabled # Required. Outputs, each carrying provenance metadata in the file. RENDER_DIR=./renders # Required. Signed forms. Retained longer than the renders they permit. CONSENT_EVIDENCE_DIR=./consents
You are building a lean indie version of Ghostfeed. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Ghostfeed · indie build An avatar video pipeline where consent and licensing are enforced as data rather than assumed, and the engineering is in driving a likeness from a performance convincingly enough to be watchable while marking every frame of output as synthetic. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Consent | A signed record per depicted person, with scope and revocation | the likeness right belongs to the person and can be withdrawn | | Avatars | Built only from footage whose subject consented | an avatar is a digital replica and that is the thing the statutes cover | | Driving | Motion and speech from a performance you own or licensed | the source performance is somebody's work as well as somebody's movement | | Rendering | A local or self-hosted pipeline with provenance embedded | output leaving unmarked is the failure the whole design prevents | | Runtime | Python 3.12 with a job queue | renders are long, fail, and need to be resumable | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Python 3.12 or newer** · free - Why: Orchestrates the pipeline and the job queue. - Get it: python.org or your package manager, then a virtual environment. - Verify: python3 --version prints 3.12 or higher - [ ] **A machine that can render locally** · free if you own the hardware; substantial to rent - Why: Sending someone's consented likeness to a third party service is a second disclosure they did not agree to, and renders are long enough that hosted costs add up quickly. - Get it: A machine with enough video memory for your chosen model, or a self-hosted instance you control. - Verify: A one minute render completes locally - [ ] **Your consent form and scope list** · free to draft; advice worth having if this is commercial - Why: Consent is enforced as data with named scopes, and inventing the scopes after people have signed means the signatures do not cover them. - Get it: Write the form, list the specific uses it permits, state the expiry and the withdrawal process. Have someone read it who is not you. - Verify: A written form exists naming specific permitted uses, an expiry and a withdrawal route - [ ] **Footage of someone who has actually consented** · free - Why: Every phase depends on there being a consented avatar, and the obvious first subject is yourself. - Get it: Record yourself, or someone who has signed the form, with the range of head movement the driving will need. - Verify: Footage exists with a matching signed consent record - [ ] **Which digital replica rules apply to you** · free to read - Why: Eight states have specific statutes and there is no federal right, so the rule depends on where the depicted person is and where the work is distributed. - Get it: Identify where your subjects are and where output will be seen, then take the strictest applicable position. - Verify: A written note names the jurisdictions and the rule applied ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Using a likeness without a consent record. - Ingesting clips from platforms. - Voice cloning without specific voice consent. - Legal advice on the state patchwork. - a template library scraped from TikTok and Instagram and scene-cut into ready 9:16 clips, instead of clips you source and trim yourself - avatar consistency across renders · one stored reference photo re-fed into every call, plus the draft-and-approve flow that keeps a face you picked - a director loop that writes the slideshow copy, searches for backgrounds, looks at the candidates and casts them, then rotates the deck into variants, plus a timeline editor with its own render workers - 44 agent-API tools · the same workspace drivable from Claude, which is the part with no weekend equivalent - a TikTok app that already cleared review, and a reaper that refunds most stalled renders instead of quietly eating them If one of those is essential to you, that is the reason to keep paying for Ghostfeed, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Ghostfeed The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me an avatar video tool that will not use a person or a clip you have no right to to replace Ghostfeed. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero The product this is modelled on drops your avatar into someone else's viral clip and clones its motion. That is two separate takings: the clip is a copyrighted work belonging to whoever shot it, and the person in it has rights in their own likeness and voice. The likeness side is no longer vague. Eight states now have digital replica statutes specifically covering AI-generated voice and likeness, including Tennessee's ELVIS Act in effect since July 2024, alongside California, Illinois, New York, Utah, Arkansas, Montana and Washington. The federal NO FAKES Act advanced out of the Senate Judiciary Committee in 2026 but is not law, so what exists is a patchwork of state rights rather than one rule. Separately, the EU AI Act's Article 50 transparency obligations require synthetic content to be marked and disclosed. So this build has one rule that shapes everything: an avatar requires a consent record from the person it depicts, and a source clip requires a licence record. No consent, no render. Every output is marked as synthetic. There is no path that takes a clip from a URL and no path that builds an avatar from footage of someone who did not agree. What remains is genuinely useful: your own avatar, your own or licensed footage, and a pipeline that produces short video quickly. That is what follows. ### Stack (fixed, do not substitute) - Consent: A signed record per depicted person, with scope and revocation · the likeness right belongs to the person and can be withdrawn - Avatars: Built only from footage whose subject consented · an avatar is a digital replica and that is the thing the statutes cover - Driving: Motion and speech from a performance you own or licensed · the source performance is somebody's work as well as somebody's movement - Rendering: A local or self-hosted pipeline with provenance embedded · output leaving unmarked is the failure the whole design prevents - Runtime: Python 3.12 with a job queue · renders are long, fail, and need to be resumable ### Data model (create this before Phase 1) - people(id, name, contact, created_at) - consents(id, person_id, scope_json, granted_at, evidence_path, expires_at, revoked_at) · scope names the uses permitted - avatars(id, person_id, consent_id, source_footage_path, built_at, disabled_at) - sources(id, kind, path, licence_id, rights_holder, obtained_from) · kind is performance, audio or background - licences(id, name, allows_derivative, allows_commercial, allows_synthetic_use, source_url) - renders(id, avatar_id, source_id, script_id, output_path, marked_synthetic, at) - revocation_actions(consent_id, at, renders_withdrawn, avatars_disabled) · because withdrawal has to actually do something - A render cannot exist without a live consent and a licensed source, and the foreign keys make that structural rather than procedural. ### Phase 1 · Consent as a record with teeth Build: Consent records naming the person, the permitted uses, an expiry and evidence of how it was given. Avatars reference a consent. Withdrawal disables the avatar and marks existing renders as withdrawn. Trap: A consent checkbox with no revocation path is not consent in any meaningful sense, and the first person who changes their mind will find there is nothing the tool can do. Build withdrawal in the same phase as consent, and make it actually disable the avatar and flag the renders rather than only setting a flag on a row. Done when: An avatar cannot be created without a live consent, enforced by the schema. Withdrawal disables the avatar and marks every render made from it, tested end to end. Consent scope names specific uses rather than being a single boolean. An expired consent blocks new renders automatically. Do not build yet: No source licensing, driving or rendering. ### Phase 2 · Source material you have rights to Build: Store sources with a licence recording derivative use, commercial use and synthetic use as separate permissions. Refuse to use a source lacking the permission the render needs. Provide no path that ingests a clip from a URL. Trap: A licence that permits derivative works does not necessarily permit using the footage to drive a synthetic performance, and treating those as the same question is how a permitted use becomes an unpermitted one. Record synthetic use separately, and default it to not permitted where the licence is silent. Done when: A source without a licence record cannot be used in a render, tested by attempting it. Synthetic use is recorded separately from derivative use and defaults to not permitted when the licence is silent. There is no path that fetches a clip from a URL, verified by looking for one. A render exports its full source provenance. Do not build yet: No driving or rendering yet. ### Phase 3 · Driving a likeness convincingly enough Build: Drive the avatar from a performance's motion and a speech track, keeping lip movement aligned to the audio's own timings. Handle head turns and occlusion. Detect output that falls into the range where it looks wrong and flag it rather than shipping it. Trap: Lip movement estimated from text rather than from the audio's actual phoneme timings drifts out of sync within a sentence, and unlike a caption drifting, a mouth out of sync is immediately and viscerally noticeable. Take the timings from the speech itself. Done when: Lip movement is driven by the audio's own timings and stays aligned over a two minute render, verified by watching. Head turns and partial occlusion do not produce artefacts. Output quality is scored and low scores are flagged rather than delivered. A render is resumable after interruption. Do not build yet: No marking or publishing yet. ### Phase 4 · Marking every output as what it is Build: Embed provenance metadata marking the output as AI generated, and burn in a visible indication where the intended distribution requires one. Make an unmarked render unexportable in the export path rather than by convention. Trap: Marking in the database and not in the file means the marking is lost the moment the file is copied anywhere, which is immediately. The mark has to travel with the bytes, and the export path has to read it back from the file rather than trusting a column. Done when: Every export carries provenance metadata read back from the file itself, not from the database. An unmarked file cannot be exported, tested by stripping the metadata and retrying. Visible marking is applied where the configured distribution requires it. The marking survives a re-encode by common tools. Do not build yet: No distribution or reporting yet. ### Phase 5 · Withdrawal that reaches published work Build: Record where every render was published. On withdrawal, list every published output derived from that person and support taking them down. Report what could not be withdrawn and why. Trap: Treating withdrawal as a future-only restriction leaves everything already published in place, which is exactly what the person is asking about when they withdraw. Track publication destinations per render so withdrawal produces an actionable list rather than an apology. Done when: Withdrawal produces a complete list of published outputs derived from that person, tested with renders published to two destinations. Takedown is supported where the destination allows it and reported where it does not. The withdrawal action and its outcome are recorded. A person can ask what exists of them and get a complete answer. Do not build yet: Nothing after this. ### Out of scope (and why) - Using anyone's likeness without a consent record, which is what the category is usually built to do and what the state digital replica statutes address. - Ingesting clips from platforms, which takes someone's copyrighted work without a licence and has no place in the build. - Voice cloning of a person who has not consented specifically to voice use, which several statutes treat separately from likeness. - Legal advice. The state patchwork differs and there is no federal right, so where you are matters. ### README must contain - That an avatar requires a live consent record and withdrawal actually disables it - That synthetic use is a separate licence permission from derivative use and defaults to refused - That there is no path to ingest a clip from a URL, deliberately - That provenance marking lives in the file and is read back from it before export - That withdrawal produces a list of published outputs rather than a future-only restriction ===== AGENTS.md ===== # Agent instructions · Ghostfeed indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: A signed record per depicted person, with scope and revocation, Built only from footage whose subject consented, Motion and speech from a performance you own or licensed, A local or self-hosted pipeline with provenance embedded, Python 3.12 with a job queue. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - A consent checkbox with no revocation path is not consent in any meaningful sense, and the first person who changes their mind will find there is nothing the tool can do. Build withdrawal in the same phase as consent, and make it actually disable the avatar and flag the renders rather than only setting a flag on a row. - A licence that permits derivative works does not necessarily permit using the footage to drive a synthetic performance, and treating those as the same question is how a permitted use becomes an unpermitted one. Record synthetic use separately, and default it to not permitted where the licence is silent. - Lip movement estimated from text rather than from the audio's actual phoneme timings drifts out of sync within a sentence, and unlike a caption drifting, a mouth out of sync is immediately and viscerally noticeable. Take the timings from the speech itself. - Marking in the database and not in the file means the marking is lost the moment the file is copied anywhere, which is immediately. The mark has to travel with the bytes, and the export path has to read it back from the file rather than trusting a column. ===== BUILD_PLAN.md ===== # Build plan · Ghostfeed An avatar video pipeline where consent and licensing are enforced as data rather than assumed, and the engineering is in driving a likeness from a performance convincingly enough to be watchable while marking every frame of output as synthetic. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Consent as a record with teeth An avatar cannot exist without a consent that can be withdrawn. ### Steps 1. Model scope as named permitted uses A single boolean is not consent to anything specific. Files: `src/consent.py` 2. Make avatars require a live consent A foreign key, not a check. Structural rather than procedural. Files: `src/schema.sql` 3. Implement withdrawal now Disabling the avatar and flagging renders. Consent without it is decorative. Files: `src/revoke.py`, `tests/test_revocation.py` ### Done when - [ ] An avatar requires a live consent - [ ] Withdrawal disables the avatar and flags renders - [ ] Scope names specific uses - [ ] An expired consent blocks new renders ### Watch out - A consent checkbox with no revocation path is not consent in any meaningful sense, and the first person who changes their mind will find there is nothing the tool can do. Build withdrawal in the same phase as consent, and make it actually disable the avatar and flag the renders rather than only setting a flag on a row. ## Phase 2 · Source material you have rights to Every clip, voice and background carries a licence permitting synthetic use. ### Steps 1. Record three permissions separately Derivative, commercial, synthetic. A licence may allow one and not the others. Files: `src/licences.py` 2. Default synthetic use to not permitted Where the licence is silent, which is most of the time. Files: `src/licences.py` 3. Provide no URL ingestion Its absence is the design. A clip from a URL has no licence record. Files: `src/sources.py` ### Done when - [ ] An unlicensed source cannot be used - [ ] Synthetic use is separate and defaults to refused - [ ] No URL ingestion path exists - [ ] Renders export their source provenance ### Watch out - A licence that permits derivative works does not necessarily permit using the footage to drive a synthetic performance, and treating those as the same question is how a permitted use becomes an unpermitted one. Record synthetic use separately, and default it to not permitted where the licence is silent. ## Phase 3 · Driving a likeness convincingly enough The avatar moves and speaks in a way that is watchable. ### Steps 1. Drive the mouth from audio timings Not from text. Estimated timing drifts within a sentence and is very visible. Files: `src/lipsync.py` 2. Handle turns and occlusion Where these pipelines produce their worst artefacts. Files: `src/drive.py` 3. Score and flag poor output Shipping a bad render is worse than reporting one. Files: `src/quality.py` ### Done when - [ ] Lip movement stays aligned over two minutes - [ ] Turns and occlusion do not produce artefacts - [ ] Low quality output is flagged, not delivered - [ ] An interrupted render resumes ### Watch out - Lip movement estimated from text rather than from the audio's actual phoneme timings drifts out of sync within a sentence, and unlike a caption drifting, a mouth out of sync is immediately and viscerally noticeable. Take the timings from the speech itself. ## Phase 4 · Marking every output as what it is No file leaves this pipeline without being identifiable as synthetic. ### Steps 1. Embed in the file, verify by reading back A database column is lost the moment the file is copied. Files: `src/marking.py` 2. Block export on missing marking Read from the file. Test by stripping it deliberately. Files: `src/export.py`, `tests/test_marking.py` 3. Apply visible marking where required Metadata satisfies machine readability, not human disclosure. Files: `src/overlay.py` ### Done when - [ ] Provenance is read back from the file - [ ] A stripped file cannot be exported - [ ] Visible marking applies where required - [ ] Marking survives a common re-encode ### Watch out - Marking in the database and not in the file means the marking is lost the moment the file is copied anywhere, which is immediately. The mark has to travel with the bytes, and the export path has to read it back from the file rather than trusting a column. ## Not in this build - Using a likeness without a consent record. - Ingesting clips from platforms. - Voice cloning without specific voice consent. - Legal advice on the state patchwork. ## After v1, if you want it - Consent renewal reminders, since expiry is how these records quietly lapse - A subject-facing page where a person can see what exists of them and withdraw - Batch rendering with per-render consent checks, once the single path is proven ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Consent records and provenance. The evidence you were permitted to make these. DATABASE_URL=postgres://localhost:5432/avatars # Required. Present as a constant so the answer is visible. There is no other value. CONSENT_REQUIRED=always # Required. Embedded in the file and verified before export. SYNTHETIC_MARKING=always # Required. Present as a constant. A clip from a URL arrives without a licence record. URL_INGESTION=disabled # Required. Outputs, each carrying provenance metadata in the file. RENDER_DIR=./renders # Required. Signed forms. Retained longer than the renders they permit. CONSENT_EVIDENCE_DIR=./consents
You are building a production product version of Ghostfeed. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # Ghostfeed · product brief ## Problem No model here was trained by Ghostfeed · every step is somebody else's hosted inference, spread across four vendors. A Gemini image-edit call swaps your avatar into frame 0 of an existing clip, an approval gate makes you accept that frame before anything expensive runs, and then the video step splits: the default clone family sends the approved frame plus the original clip to Kling motion-control, so the render inherits the source video's motion, while the prompt family animates the still alone on PixVerse, Seedance, Grok or Kling. Rebuild the loop for yourself with a fal.ai key, a Gemini key and ffmpeg over a long weekend and it works. What does not fall out of that weekend is the rest of it · a template library scraped and scene-cut into 9:16 clips, a director that writes slideshow copy and casts every background itself, a timeline editor with its own render workers, 44 agent-API tools, and a TikTok app that took platform review to get. Personal version, yes. The thing you would actually run daily, no. ## Product outcome An avatar pipeline that cannot depict someone who did not agree, cannot use footage you have no licence to, marks everything it produces, and can tell a person exactly what exists of them. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - fal.ai key for Kling motion-control and image-to-video - Gemini or GPT-image key for the first-frame swap - ffmpeg - reaction clips you have the right to reuse - an always-on box for the polling loop ## Explicit non-goals for v1 - Using a likeness without a consent record. - Ingesting clips from platforms. - Voice cloning without specific voice consent. - Legal advice on the state patchwork. - a template library scraped from TikTok and Instagram and scene-cut into ready 9:16 clips, instead of clips you source and trim yourself - avatar consistency across renders · one stored reference photo re-fed into every call, plus the draft-and-approve flow that keeps a face you picked - a director loop that writes the slideshow copy, searches for backgrounds, looks at the candidates and casts them, then rotates the deck into variants, plus a timeline editor with its own render workers - 44 agent-API tools · the same workspace drivable from Claude, which is the part with no weekend equivalent - a TikTok app that already cleared review, and a reaper that refunds most stalled renders instead of quietly eating them ## Success criteria - An avatar without a live consent is confirmed impossible - Withdrawal is confirmed to disable the avatar and flag renders - An unlicensed source is confirmed unusable - A stripped file is confirmed unexportable - Withdrawal is confirmed to list published outputs ===== BRIEF.md ===== # Build brief · Ghostfeed The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me an avatar video tool that will not use a person or a clip you have no right to to replace Ghostfeed. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero The product this is modelled on drops your avatar into someone else's viral clip and clones its motion. That is two separate takings: the clip is a copyrighted work belonging to whoever shot it, and the person in it has rights in their own likeness and voice. The likeness side is no longer vague. Eight states now have digital replica statutes specifically covering AI-generated voice and likeness, including Tennessee's ELVIS Act in effect since July 2024, alongside California, Illinois, New York, Utah, Arkansas, Montana and Washington. The federal NO FAKES Act advanced out of the Senate Judiciary Committee in 2026 but is not law, so what exists is a patchwork of state rights rather than one rule. Separately, the EU AI Act's Article 50 transparency obligations require synthetic content to be marked and disclosed. So this build has one rule that shapes everything: an avatar requires a consent record from the person it depicts, and a source clip requires a licence record. No consent, no render. Every output is marked as synthetic. There is no path that takes a clip from a URL and no path that builds an avatar from footage of someone who did not agree. What remains is genuinely useful: your own avatar, your own or licensed footage, and a pipeline that produces short video quickly. That is what follows. ### Stack (fixed, do not substitute) - Consent: A signed record per depicted person, with scope and revocation · the likeness right belongs to the person and can be withdrawn - Avatars: Built only from footage whose subject consented · an avatar is a digital replica and that is the thing the statutes cover - Driving: Motion and speech from a performance you own or licensed · the source performance is somebody's work as well as somebody's movement - Rendering: A local or self-hosted pipeline with provenance embedded · output leaving unmarked is the failure the whole design prevents - Runtime: Python 3.12 with a job queue · renders are long, fail, and need to be resumable ### Data model (create this before Phase 1) - people(id, name, contact, created_at) - consents(id, person_id, scope_json, granted_at, evidence_path, expires_at, revoked_at) · scope names the uses permitted - avatars(id, person_id, consent_id, source_footage_path, built_at, disabled_at) - sources(id, kind, path, licence_id, rights_holder, obtained_from) · kind is performance, audio or background - licences(id, name, allows_derivative, allows_commercial, allows_synthetic_use, source_url) - renders(id, avatar_id, source_id, script_id, output_path, marked_synthetic, at) - revocation_actions(consent_id, at, renders_withdrawn, avatars_disabled) · because withdrawal has to actually do something - A render cannot exist without a live consent and a licensed source, and the foreign keys make that structural rather than procedural. ### Phase 1 · Consent as a record with teeth Build: Consent records naming the person, the permitted uses, an expiry and evidence of how it was given. Avatars reference a consent. Withdrawal disables the avatar and marks existing renders as withdrawn. Trap: A consent checkbox with no revocation path is not consent in any meaningful sense, and the first person who changes their mind will find there is nothing the tool can do. Build withdrawal in the same phase as consent, and make it actually disable the avatar and flag the renders rather than only setting a flag on a row. Done when: An avatar cannot be created without a live consent, enforced by the schema. Withdrawal disables the avatar and marks every render made from it, tested end to end. Consent scope names specific uses rather than being a single boolean. An expired consent blocks new renders automatically. Do not build yet: No source licensing, driving or rendering. ### Phase 2 · Source material you have rights to Build: Store sources with a licence recording derivative use, commercial use and synthetic use as separate permissions. Refuse to use a source lacking the permission the render needs. Provide no path that ingests a clip from a URL. Trap: A licence that permits derivative works does not necessarily permit using the footage to drive a synthetic performance, and treating those as the same question is how a permitted use becomes an unpermitted one. Record synthetic use separately, and default it to not permitted where the licence is silent. Done when: A source without a licence record cannot be used in a render, tested by attempting it. Synthetic use is recorded separately from derivative use and defaults to not permitted when the licence is silent. There is no path that fetches a clip from a URL, verified by looking for one. A render exports its full source provenance. Do not build yet: No driving or rendering yet. ### Phase 3 · Driving a likeness convincingly enough Build: Drive the avatar from a performance's motion and a speech track, keeping lip movement aligned to the audio's own timings. Handle head turns and occlusion. Detect output that falls into the range where it looks wrong and flag it rather than shipping it. Trap: Lip movement estimated from text rather than from the audio's actual phoneme timings drifts out of sync within a sentence, and unlike a caption drifting, a mouth out of sync is immediately and viscerally noticeable. Take the timings from the speech itself. Done when: Lip movement is driven by the audio's own timings and stays aligned over a two minute render, verified by watching. Head turns and partial occlusion do not produce artefacts. Output quality is scored and low scores are flagged rather than delivered. A render is resumable after interruption. Do not build yet: No marking or publishing yet. ### Phase 4 · Marking every output as what it is Build: Embed provenance metadata marking the output as AI generated, and burn in a visible indication where the intended distribution requires one. Make an unmarked render unexportable in the export path rather than by convention. Trap: Marking in the database and not in the file means the marking is lost the moment the file is copied anywhere, which is immediately. The mark has to travel with the bytes, and the export path has to read it back from the file rather than trusting a column. Done when: Every export carries provenance metadata read back from the file itself, not from the database. An unmarked file cannot be exported, tested by stripping the metadata and retrying. Visible marking is applied where the configured distribution requires it. The marking survives a re-encode by common tools. Do not build yet: No distribution or reporting yet. ### Phase 5 · Withdrawal that reaches published work Build: Record where every render was published. On withdrawal, list every published output derived from that person and support taking them down. Report what could not be withdrawn and why. Trap: Treating withdrawal as a future-only restriction leaves everything already published in place, which is exactly what the person is asking about when they withdraw. Track publication destinations per render so withdrawal produces an actionable list rather than an apology. Done when: Withdrawal produces a complete list of published outputs derived from that person, tested with renders published to two destinations. Takedown is supported where the destination allows it and reported where it does not. The withdrawal action and its outcome are recorded. A person can ask what exists of them and get a complete answer. Do not build yet: Nothing after this. ### Out of scope (and why) - Using anyone's likeness without a consent record, which is what the category is usually built to do and what the state digital replica statutes address. - Ingesting clips from platforms, which takes someone's copyrighted work without a licence and has no place in the build. - Voice cloning of a person who has not consented specifically to voice use, which several statutes treat separately from likeness. - Legal advice. The state patchwork differs and there is no federal right, so where you are matters. ### README must contain - That an avatar requires a live consent record and withdrawal actually disables it - That synthetic use is a separate licence permission from derivative use and defaults to refused - That there is no path to ingest a clip from a URL, deliberately - That provenance marking lives in the file and is read back from it before export - That withdrawal produces a list of published outputs rather than a future-only restriction ===== ARCHITECTURE.md ===== # Architecture · Ghostfeed ## Stack | Part | Choice | Why | | --- | --- | --- | | Consent | A signed record per depicted person, with scope and revocation | the likeness right belongs to the person and can be withdrawn | | Avatars | Built only from footage whose subject consented | an avatar is a digital replica and that is the thing the statutes cover | | Driving | Motion and speech from a performance you own or licensed | the source performance is somebody's work as well as somebody's movement | | Rendering | A local or self-hosted pipeline with provenance embedded | output leaving unmarked is the failure the whole design prevents | | Runtime | Python 3.12 with a job queue | renders are long, fail, and need to be resumable | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Consent | Scoped records and working withdrawal. | Not swappable for a checkbox. Withdrawal that does nothing is not consent. | | Licensing | Three separate source permissions. | Any store, provided synthetic use is separate and defaults to refused. | | Driving | Motion and audio-timed lip movement. | Any model, provided lip timing comes from the audio. | | Marking | File-embedded provenance verified at export. | Not swappable for a database flag, which is lost on the first copy. | | Withdrawal | Finding and addressing published output. | Any implementation, provided it produces a list rather than a future-only rule. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DATABASE_URL` · required, secret · Consent records and provenance. The evidence you were permitted to make these. - `CONSENT_REQUIRED` · required · Present as a constant so the answer is visible. There is no other value. - `SYNTHETIC_MARKING` · required · Embedded in the file and verified before export. - `URL_INGESTION` · required · Present as a constant. A clip from a URL arrives without a licence record. - `RENDER_DIR` · required · Outputs, each carrying provenance metadata in the file. - `CONSENT_EVIDENCE_DIR` · required · Signed forms. Retained longer than the renders they permit. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · Ghostfeed product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: A signed record per depicted person, with scope and revocation, Built only from footage whose subject consented, Motion and speech from a performance you own or licensed, A local or self-hosted pipeline with provenance embedded, Python 3.12 with a job queue. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - A consent checkbox with no revocation path is not consent in any meaningful sense, and the first person who changes their mind will find there is nothing the tool can do. Build withdrawal in the same phase as consent, and make it actually disable the avatar and flag the renders rather than only setting a flag on a row. - A licence that permits derivative works does not necessarily permit using the footage to drive a synthetic performance, and treating those as the same question is how a permitted use becomes an unpermitted one. Record synthetic use separately, and default it to not permitted where the licence is silent. - Lip movement estimated from text rather than from the audio's actual phoneme timings drifts out of sync within a sentence, and unlike a caption drifting, a mouth out of sync is immediately and viscerally noticeable. Take the timings from the speech itself. - Marking in the database and not in the file means the marking is lost the moment the file is copied anywhere, which is immediately. The mark has to travel with the bytes, and the export path has to read it back from the file rather than trusting a column. - Treating withdrawal as a future-only restriction leaves everything already published in place, which is exactly what the person is asking about when they withdraw. Track publication destinations per render so withdrawal produces an actionable list rather than an apology. ===== MILESTONES.md ===== # Delivery milestones · Ghostfeed Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Consent as a record with teeth An avatar cannot exist without a consent that can be withdrawn. ### Steps 1. Model scope as named permitted uses A single boolean is not consent to anything specific. Files: `src/consent.py` 2. Make avatars require a live consent A foreign key, not a check. Structural rather than procedural. Files: `src/schema.sql` 3. Implement withdrawal now Disabling the avatar and flagging renders. Consent without it is decorative. Files: `src/revoke.py`, `tests/test_revocation.py` ### Done when - [ ] An avatar requires a live consent - [ ] Withdrawal disables the avatar and flags renders - [ ] Scope names specific uses - [ ] An expired consent blocks new renders ### Watch out - A consent checkbox with no revocation path is not consent in any meaningful sense, and the first person who changes their mind will find there is nothing the tool can do. Build withdrawal in the same phase as consent, and make it actually disable the avatar and flag the renders rather than only setting a flag on a row. ## M2 · Source material you have rights to Every clip, voice and background carries a licence permitting synthetic use. ### Steps 1. Record three permissions separately Derivative, commercial, synthetic. A licence may allow one and not the others. Files: `src/licences.py` 2. Default synthetic use to not permitted Where the licence is silent, which is most of the time. Files: `src/licences.py` 3. Provide no URL ingestion Its absence is the design. A clip from a URL has no licence record. Files: `src/sources.py` ### Done when - [ ] An unlicensed source cannot be used - [ ] Synthetic use is separate and defaults to refused - [ ] No URL ingestion path exists - [ ] Renders export their source provenance ### Watch out - A licence that permits derivative works does not necessarily permit using the footage to drive a synthetic performance, and treating those as the same question is how a permitted use becomes an unpermitted one. Record synthetic use separately, and default it to not permitted where the licence is silent. ## M3 · Driving a likeness convincingly enough The avatar moves and speaks in a way that is watchable. ### Steps 1. Drive the mouth from audio timings Not from text. Estimated timing drifts within a sentence and is very visible. Files: `src/lipsync.py` 2. Handle turns and occlusion Where these pipelines produce their worst artefacts. Files: `src/drive.py` 3. Score and flag poor output Shipping a bad render is worse than reporting one. Files: `src/quality.py` ### Done when - [ ] Lip movement stays aligned over two minutes - [ ] Turns and occlusion do not produce artefacts - [ ] Low quality output is flagged, not delivered - [ ] An interrupted render resumes ### Watch out - Lip movement estimated from text rather than from the audio's actual phoneme timings drifts out of sync within a sentence, and unlike a caption drifting, a mouth out of sync is immediately and viscerally noticeable. Take the timings from the speech itself. ## M4 · Marking every output as what it is No file leaves this pipeline without being identifiable as synthetic. ### Steps 1. Embed in the file, verify by reading back A database column is lost the moment the file is copied. Files: `src/marking.py` 2. Block export on missing marking Read from the file. Test by stripping it deliberately. Files: `src/export.py`, `tests/test_marking.py` 3. Apply visible marking where required Metadata satisfies machine readability, not human disclosure. Files: `src/overlay.py` ### Done when - [ ] Provenance is read back from the file - [ ] A stripped file cannot be exported - [ ] Visible marking applies where required - [ ] Marking survives a common re-encode ### Watch out - Marking in the database and not in the file means the marking is lost the moment the file is copied anywhere, which is immediately. The mark has to travel with the bytes, and the export path has to read it back from the file rather than trusting a column. ## M5 · Withdrawal that reaches published work (production only) When someone withdraws consent, you can find and address what is already out. ### Steps 1. Record publication destinations per render Withdrawal is only actionable if you know where things went. Files: `src/publications.py` 2. Produce a takedown list on withdrawal Actionable, not an apology. Files: `src/revoke.py` 3. Answer what exists of a person They are entitled to ask and you should be able to say. Files: `src/subject-report.py` ### Done when - [ ] Withdrawal lists every published output - [ ] Takedown works or reports why it cannot - [ ] Withdrawal actions and outcomes are recorded - [ ] A person can be told what exists of them ### Watch out - Treating withdrawal as a future-only restriction leaves everything already published in place, which is exactly what the person is asking about when they withdraw. Track publication destinations per render so withdrawal produces an actionable list rather than an apology. ===== OPERATIONS.md ===== # Operations · Ghostfeed ## Backup Consent evidence is the most important thing here and must outlive the renders. Back it up separately and with longer retention, since a render whose consent record is lost should be treated as unpermitted. ## Restore Restore and confirm consent records and their revocation states survived, since restoring a revoked consent as live would re-enable an avatar someone asked you to stop using. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Consents approaching expiry and renders whose consent has been revoked but not yet withdrawn from publication. The second is the one with a person waiting on the other end of it. ## Incident checklist A published output derived from a withdrawn consent is the failure that matters, and the publication records are what turn it from an unbounded problem into a list. That is the reason they exist. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] An avatar without a live consent is confirmed impossible - [ ] Withdrawal is confirmed to disable the avatar and flag renders - [ ] An unlicensed source is confirmed unusable - [ ] A stripped file is confirmed unexportable - [ ] Withdrawal is confirmed to list published outputs ## Launch constraint Do not market omitted Ghostfeed capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Consent records and provenance. The evidence you were permitted to make these. DATABASE_URL=postgres://localhost:5432/avatars # Required. Present as a constant so the answer is visible. There is no other value. CONSENT_REQUIRED=always # Required. Embedded in the file and verified before export. SYNTHETIC_MARKING=always # Required. Present as a constant. A clip from a URL arrives without a licence record. URL_INGESTION=disabled # Required. Outputs, each carrying provenance metadata in the file. RENDER_DIR=./renders # Required. Signed forms. Retained longer than the renders they permit. CONSENT_EVIDENCE_DIR=./consents
# Ghostfeed · indie build An avatar video pipeline where consent and licensing are enforced as data rather than assumed, and the engineering is in driving a likeness from a performance convincingly enough to be watchable while marking every frame of output as synthetic. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Consent | A signed record per depicted person, with scope and revocation | the likeness right belongs to the person and can be withdrawn | | Avatars | Built only from footage whose subject consented | an avatar is a digital replica and that is the thing the statutes cover | | Driving | Motion and speech from a performance you own or licensed | the source performance is somebody's work as well as somebody's movement | | Rendering | A local or self-hosted pipeline with provenance embedded | output leaving unmarked is the failure the whole design prevents | | Runtime | Python 3.12 with a job queue | renders are long, fail, and need to be resumable | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Python 3.12 or newer** · free - Why: Orchestrates the pipeline and the job queue. - Get it: python.org or your package manager, then a virtual environment. - Verify: python3 --version prints 3.12 or higher - [ ] **A machine that can render locally** · free if you own the hardware; substantial to rent - Why: Sending someone's consented likeness to a third party service is a second disclosure they did not agree to, and renders are long enough that hosted costs add up quickly. - Get it: A machine with enough video memory for your chosen model, or a self-hosted instance you control. - Verify: A one minute render completes locally - [ ] **Your consent form and scope list** · free to draft; advice worth having if this is commercial - Why: Consent is enforced as data with named scopes, and inventing the scopes after people have signed means the signatures do not cover them. - Get it: Write the form, list the specific uses it permits, state the expiry and the withdrawal process. Have someone read it who is not you. - Verify: A written form exists naming specific permitted uses, an expiry and a withdrawal route - [ ] **Footage of someone who has actually consented** · free - Why: Every phase depends on there being a consented avatar, and the obvious first subject is yourself. - Get it: Record yourself, or someone who has signed the form, with the range of head movement the driving will need. - Verify: Footage exists with a matching signed consent record - [ ] **Which digital replica rules apply to you** · free to read - Why: Eight states have specific statutes and there is no federal right, so the rule depends on where the depicted person is and where the work is distributed. - Get it: Identify where your subjects are and where output will be seen, then take the strictest applicable position. - Verify: A written note names the jurisdictions and the rule applied ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Using a likeness without a consent record. - Ingesting clips from platforms. - Voice cloning without specific voice consent. - Legal advice on the state patchwork. - a template library scraped from TikTok and Instagram and scene-cut into ready 9:16 clips, instead of clips you source and trim yourself - avatar consistency across renders · one stored reference photo re-fed into every call, plus the draft-and-approve flow that keeps a face you picked - a director loop that writes the slideshow copy, searches for backgrounds, looks at the candidates and casts them, then rotates the deck into variants, plus a timeline editor with its own render workers - 44 agent-API tools · the same workspace drivable from Claude, which is the part with no weekend equivalent - a TikTok app that already cleared review, and a reaper that refunds most stalled renders instead of quietly eating them If one of those is essential to you, that is the reason to keep paying for Ghostfeed, and the README should say so rather than pretend.
# Build brief · Ghostfeed The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me an avatar video tool that will not use a person or a clip you have no right to to replace Ghostfeed. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero The product this is modelled on drops your avatar into someone else's viral clip and clones its motion. That is two separate takings: the clip is a copyrighted work belonging to whoever shot it, and the person in it has rights in their own likeness and voice. The likeness side is no longer vague. Eight states now have digital replica statutes specifically covering AI-generated voice and likeness, including Tennessee's ELVIS Act in effect since July 2024, alongside California, Illinois, New York, Utah, Arkansas, Montana and Washington. The federal NO FAKES Act advanced out of the Senate Judiciary Committee in 2026 but is not law, so what exists is a patchwork of state rights rather than one rule. Separately, the EU AI Act's Article 50 transparency obligations require synthetic content to be marked and disclosed. So this build has one rule that shapes everything: an avatar requires a consent record from the person it depicts, and a source clip requires a licence record. No consent, no render. Every output is marked as synthetic. There is no path that takes a clip from a URL and no path that builds an avatar from footage of someone who did not agree. What remains is genuinely useful: your own avatar, your own or licensed footage, and a pipeline that produces short video quickly. That is what follows. ### Stack (fixed, do not substitute) - Consent: A signed record per depicted person, with scope and revocation · the likeness right belongs to the person and can be withdrawn - Avatars: Built only from footage whose subject consented · an avatar is a digital replica and that is the thing the statutes cover - Driving: Motion and speech from a performance you own or licensed · the source performance is somebody's work as well as somebody's movement - Rendering: A local or self-hosted pipeline with provenance embedded · output leaving unmarked is the failure the whole design prevents - Runtime: Python 3.12 with a job queue · renders are long, fail, and need to be resumable ### Data model (create this before Phase 1) - people(id, name, contact, created_at) - consents(id, person_id, scope_json, granted_at, evidence_path, expires_at, revoked_at) · scope names the uses permitted - avatars(id, person_id, consent_id, source_footage_path, built_at, disabled_at) - sources(id, kind, path, licence_id, rights_holder, obtained_from) · kind is performance, audio or background - licences(id, name, allows_derivative, allows_commercial, allows_synthetic_use, source_url) - renders(id, avatar_id, source_id, script_id, output_path, marked_synthetic, at) - revocation_actions(consent_id, at, renders_withdrawn, avatars_disabled) · because withdrawal has to actually do something - A render cannot exist without a live consent and a licensed source, and the foreign keys make that structural rather than procedural. ### Phase 1 · Consent as a record with teeth Build: Consent records naming the person, the permitted uses, an expiry and evidence of how it was given. Avatars reference a consent. Withdrawal disables the avatar and marks existing renders as withdrawn. Trap: A consent checkbox with no revocation path is not consent in any meaningful sense, and the first person who changes their mind will find there is nothing the tool can do. Build withdrawal in the same phase as consent, and make it actually disable the avatar and flag the renders rather than only setting a flag on a row. Done when: An avatar cannot be created without a live consent, enforced by the schema. Withdrawal disables the avatar and marks every render made from it, tested end to end. Consent scope names specific uses rather than being a single boolean. An expired consent blocks new renders automatically. Do not build yet: No source licensing, driving or rendering. ### Phase 2 · Source material you have rights to Build: Store sources with a licence recording derivative use, commercial use and synthetic use as separate permissions. Refuse to use a source lacking the permission the render needs. Provide no path that ingests a clip from a URL. Trap: A licence that permits derivative works does not necessarily permit using the footage to drive a synthetic performance, and treating those as the same question is how a permitted use becomes an unpermitted one. Record synthetic use separately, and default it to not permitted where the licence is silent. Done when: A source without a licence record cannot be used in a render, tested by attempting it. Synthetic use is recorded separately from derivative use and defaults to not permitted when the licence is silent. There is no path that fetches a clip from a URL, verified by looking for one. A render exports its full source provenance. Do not build yet: No driving or rendering yet. ### Phase 3 · Driving a likeness convincingly enough Build: Drive the avatar from a performance's motion and a speech track, keeping lip movement aligned to the audio's own timings. Handle head turns and occlusion. Detect output that falls into the range where it looks wrong and flag it rather than shipping it. Trap: Lip movement estimated from text rather than from the audio's actual phoneme timings drifts out of sync within a sentence, and unlike a caption drifting, a mouth out of sync is immediately and viscerally noticeable. Take the timings from the speech itself. Done when: Lip movement is driven by the audio's own timings and stays aligned over a two minute render, verified by watching. Head turns and partial occlusion do not produce artefacts. Output quality is scored and low scores are flagged rather than delivered. A render is resumable after interruption. Do not build yet: No marking or publishing yet. ### Phase 4 · Marking every output as what it is Build: Embed provenance metadata marking the output as AI generated, and burn in a visible indication where the intended distribution requires one. Make an unmarked render unexportable in the export path rather than by convention. Trap: Marking in the database and not in the file means the marking is lost the moment the file is copied anywhere, which is immediately. The mark has to travel with the bytes, and the export path has to read it back from the file rather than trusting a column. Done when: Every export carries provenance metadata read back from the file itself, not from the database. An unmarked file cannot be exported, tested by stripping the metadata and retrying. Visible marking is applied where the configured distribution requires it. The marking survives a re-encode by common tools. Do not build yet: No distribution or reporting yet. ### Phase 5 · Withdrawal that reaches published work Build: Record where every render was published. On withdrawal, list every published output derived from that person and support taking them down. Report what could not be withdrawn and why. Trap: Treating withdrawal as a future-only restriction leaves everything already published in place, which is exactly what the person is asking about when they withdraw. Track publication destinations per render so withdrawal produces an actionable list rather than an apology. Done when: Withdrawal produces a complete list of published outputs derived from that person, tested with renders published to two destinations. Takedown is supported where the destination allows it and reported where it does not. The withdrawal action and its outcome are recorded. A person can ask what exists of them and get a complete answer. Do not build yet: Nothing after this. ### Out of scope (and why) - Using anyone's likeness without a consent record, which is what the category is usually built to do and what the state digital replica statutes address. - Ingesting clips from platforms, which takes someone's copyrighted work without a licence and has no place in the build. - Voice cloning of a person who has not consented specifically to voice use, which several statutes treat separately from likeness. - Legal advice. The state patchwork differs and there is no federal right, so where you are matters. ### README must contain - That an avatar requires a live consent record and withdrawal actually disables it - That synthetic use is a separate licence permission from derivative use and defaults to refused - That there is no path to ingest a clip from a URL, deliberately - That provenance marking lives in the file and is read back from it before export - That withdrawal produces a list of published outputs rather than a future-only restriction
# Agent instructions · Ghostfeed indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: A signed record per depicted person, with scope and revocation, Built only from footage whose subject consented, Motion and speech from a performance you own or licensed, A local or self-hosted pipeline with provenance embedded, Python 3.12 with a job queue. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - A consent checkbox with no revocation path is not consent in any meaningful sense, and the first person who changes their mind will find there is nothing the tool can do. Build withdrawal in the same phase as consent, and make it actually disable the avatar and flag the renders rather than only setting a flag on a row. - A licence that permits derivative works does not necessarily permit using the footage to drive a synthetic performance, and treating those as the same question is how a permitted use becomes an unpermitted one. Record synthetic use separately, and default it to not permitted where the licence is silent. - Lip movement estimated from text rather than from the audio's actual phoneme timings drifts out of sync within a sentence, and unlike a caption drifting, a mouth out of sync is immediately and viscerally noticeable. Take the timings from the speech itself. - Marking in the database and not in the file means the marking is lost the moment the file is copied anywhere, which is immediately. The mark has to travel with the bytes, and the export path has to read it back from the file rather than trusting a column.
# Build plan · Ghostfeed An avatar video pipeline where consent and licensing are enforced as data rather than assumed, and the engineering is in driving a likeness from a performance convincingly enough to be watchable while marking every frame of output as synthetic. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Consent as a record with teeth An avatar cannot exist without a consent that can be withdrawn. ### Steps 1. Model scope as named permitted uses A single boolean is not consent to anything specific. Files: `src/consent.py` 2. Make avatars require a live consent A foreign key, not a check. Structural rather than procedural. Files: `src/schema.sql` 3. Implement withdrawal now Disabling the avatar and flagging renders. Consent without it is decorative. Files: `src/revoke.py`, `tests/test_revocation.py` ### Done when - [ ] An avatar requires a live consent - [ ] Withdrawal disables the avatar and flags renders - [ ] Scope names specific uses - [ ] An expired consent blocks new renders ### Watch out - A consent checkbox with no revocation path is not consent in any meaningful sense, and the first person who changes their mind will find there is nothing the tool can do. Build withdrawal in the same phase as consent, and make it actually disable the avatar and flag the renders rather than only setting a flag on a row. ## Phase 2 · Source material you have rights to Every clip, voice and background carries a licence permitting synthetic use. ### Steps 1. Record three permissions separately Derivative, commercial, synthetic. A licence may allow one and not the others. Files: `src/licences.py` 2. Default synthetic use to not permitted Where the licence is silent, which is most of the time. Files: `src/licences.py` 3. Provide no URL ingestion Its absence is the design. A clip from a URL has no licence record. Files: `src/sources.py` ### Done when - [ ] An unlicensed source cannot be used - [ ] Synthetic use is separate and defaults to refused - [ ] No URL ingestion path exists - [ ] Renders export their source provenance ### Watch out - A licence that permits derivative works does not necessarily permit using the footage to drive a synthetic performance, and treating those as the same question is how a permitted use becomes an unpermitted one. Record synthetic use separately, and default it to not permitted where the licence is silent. ## Phase 3 · Driving a likeness convincingly enough The avatar moves and speaks in a way that is watchable. ### Steps 1. Drive the mouth from audio timings Not from text. Estimated timing drifts within a sentence and is very visible. Files: `src/lipsync.py` 2. Handle turns and occlusion Where these pipelines produce their worst artefacts. Files: `src/drive.py` 3. Score and flag poor output Shipping a bad render is worse than reporting one. Files: `src/quality.py` ### Done when - [ ] Lip movement stays aligned over two minutes - [ ] Turns and occlusion do not produce artefacts - [ ] Low quality output is flagged, not delivered - [ ] An interrupted render resumes ### Watch out - Lip movement estimated from text rather than from the audio's actual phoneme timings drifts out of sync within a sentence, and unlike a caption drifting, a mouth out of sync is immediately and viscerally noticeable. Take the timings from the speech itself. ## Phase 4 · Marking every output as what it is No file leaves this pipeline without being identifiable as synthetic. ### Steps 1. Embed in the file, verify by reading back A database column is lost the moment the file is copied. Files: `src/marking.py` 2. Block export on missing marking Read from the file. Test by stripping it deliberately. Files: `src/export.py`, `tests/test_marking.py` 3. Apply visible marking where required Metadata satisfies machine readability, not human disclosure. Files: `src/overlay.py` ### Done when - [ ] Provenance is read back from the file - [ ] A stripped file cannot be exported - [ ] Visible marking applies where required - [ ] Marking survives a common re-encode ### Watch out - Marking in the database and not in the file means the marking is lost the moment the file is copied anywhere, which is immediately. The mark has to travel with the bytes, and the export path has to read it back from the file rather than trusting a column. ## Not in this build - Using a likeness without a consent record. - Ingesting clips from platforms. - Voice cloning without specific voice consent. - Legal advice on the state patchwork. ## After v1, if you want it - Consent renewal reminders, since expiry is how these records quietly lapse - A subject-facing page where a person can see what exists of them and withdraw - Batch rendering with per-render consent checks, once the single path is proven
# Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Consent records and provenance. The evidence you were permitted to make these. DATABASE_URL=postgres://localhost:5432/avatars # Required. Present as a constant so the answer is visible. There is no other value. CONSENT_REQUIRED=always # Required. Embedded in the file and verified before export. SYNTHETIC_MARKING=always # Required. Present as a constant. A clip from a URL arrives without a licence record. URL_INGESTION=disabled # Required. Outputs, each carrying provenance metadata in the file. RENDER_DIR=./renders # Required. Signed forms. Retained longer than the renders they permit. CONSENT_EVIDENCE_DIR=./consents
# Ghostfeed · product brief ## Problem No model here was trained by Ghostfeed · every step is somebody else's hosted inference, spread across four vendors. A Gemini image-edit call swaps your avatar into frame 0 of an existing clip, an approval gate makes you accept that frame before anything expensive runs, and then the video step splits: the default clone family sends the approved frame plus the original clip to Kling motion-control, so the render inherits the source video's motion, while the prompt family animates the still alone on PixVerse, Seedance, Grok or Kling. Rebuild the loop for yourself with a fal.ai key, a Gemini key and ffmpeg over a long weekend and it works. What does not fall out of that weekend is the rest of it · a template library scraped and scene-cut into 9:16 clips, a director that writes slideshow copy and casts every background itself, a timeline editor with its own render workers, 44 agent-API tools, and a TikTok app that took platform review to get. Personal version, yes. The thing you would actually run daily, no. ## Product outcome An avatar pipeline that cannot depict someone who did not agree, cannot use footage you have no licence to, marks everything it produces, and can tell a person exactly what exists of them. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - fal.ai key for Kling motion-control and image-to-video - Gemini or GPT-image key for the first-frame swap - ffmpeg - reaction clips you have the right to reuse - an always-on box for the polling loop ## Explicit non-goals for v1 - Using a likeness without a consent record. - Ingesting clips from platforms. - Voice cloning without specific voice consent. - Legal advice on the state patchwork. - a template library scraped from TikTok and Instagram and scene-cut into ready 9:16 clips, instead of clips you source and trim yourself - avatar consistency across renders · one stored reference photo re-fed into every call, plus the draft-and-approve flow that keeps a face you picked - a director loop that writes the slideshow copy, searches for backgrounds, looks at the candidates and casts them, then rotates the deck into variants, plus a timeline editor with its own render workers - 44 agent-API tools · the same workspace drivable from Claude, which is the part with no weekend equivalent - a TikTok app that already cleared review, and a reaper that refunds most stalled renders instead of quietly eating them ## Success criteria - An avatar without a live consent is confirmed impossible - Withdrawal is confirmed to disable the avatar and flag renders - An unlicensed source is confirmed unusable - A stripped file is confirmed unexportable - Withdrawal is confirmed to list published outputs
# Build brief · Ghostfeed The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me an avatar video tool that will not use a person or a clip you have no right to to replace Ghostfeed. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero The product this is modelled on drops your avatar into someone else's viral clip and clones its motion. That is two separate takings: the clip is a copyrighted work belonging to whoever shot it, and the person in it has rights in their own likeness and voice. The likeness side is no longer vague. Eight states now have digital replica statutes specifically covering AI-generated voice and likeness, including Tennessee's ELVIS Act in effect since July 2024, alongside California, Illinois, New York, Utah, Arkansas, Montana and Washington. The federal NO FAKES Act advanced out of the Senate Judiciary Committee in 2026 but is not law, so what exists is a patchwork of state rights rather than one rule. Separately, the EU AI Act's Article 50 transparency obligations require synthetic content to be marked and disclosed. So this build has one rule that shapes everything: an avatar requires a consent record from the person it depicts, and a source clip requires a licence record. No consent, no render. Every output is marked as synthetic. There is no path that takes a clip from a URL and no path that builds an avatar from footage of someone who did not agree. What remains is genuinely useful: your own avatar, your own or licensed footage, and a pipeline that produces short video quickly. That is what follows. ### Stack (fixed, do not substitute) - Consent: A signed record per depicted person, with scope and revocation · the likeness right belongs to the person and can be withdrawn - Avatars: Built only from footage whose subject consented · an avatar is a digital replica and that is the thing the statutes cover - Driving: Motion and speech from a performance you own or licensed · the source performance is somebody's work as well as somebody's movement - Rendering: A local or self-hosted pipeline with provenance embedded · output leaving unmarked is the failure the whole design prevents - Runtime: Python 3.12 with a job queue · renders are long, fail, and need to be resumable ### Data model (create this before Phase 1) - people(id, name, contact, created_at) - consents(id, person_id, scope_json, granted_at, evidence_path, expires_at, revoked_at) · scope names the uses permitted - avatars(id, person_id, consent_id, source_footage_path, built_at, disabled_at) - sources(id, kind, path, licence_id, rights_holder, obtained_from) · kind is performance, audio or background - licences(id, name, allows_derivative, allows_commercial, allows_synthetic_use, source_url) - renders(id, avatar_id, source_id, script_id, output_path, marked_synthetic, at) - revocation_actions(consent_id, at, renders_withdrawn, avatars_disabled) · because withdrawal has to actually do something - A render cannot exist without a live consent and a licensed source, and the foreign keys make that structural rather than procedural. ### Phase 1 · Consent as a record with teeth Build: Consent records naming the person, the permitted uses, an expiry and evidence of how it was given. Avatars reference a consent. Withdrawal disables the avatar and marks existing renders as withdrawn. Trap: A consent checkbox with no revocation path is not consent in any meaningful sense, and the first person who changes their mind will find there is nothing the tool can do. Build withdrawal in the same phase as consent, and make it actually disable the avatar and flag the renders rather than only setting a flag on a row. Done when: An avatar cannot be created without a live consent, enforced by the schema. Withdrawal disables the avatar and marks every render made from it, tested end to end. Consent scope names specific uses rather than being a single boolean. An expired consent blocks new renders automatically. Do not build yet: No source licensing, driving or rendering. ### Phase 2 · Source material you have rights to Build: Store sources with a licence recording derivative use, commercial use and synthetic use as separate permissions. Refuse to use a source lacking the permission the render needs. Provide no path that ingests a clip from a URL. Trap: A licence that permits derivative works does not necessarily permit using the footage to drive a synthetic performance, and treating those as the same question is how a permitted use becomes an unpermitted one. Record synthetic use separately, and default it to not permitted where the licence is silent. Done when: A source without a licence record cannot be used in a render, tested by attempting it. Synthetic use is recorded separately from derivative use and defaults to not permitted when the licence is silent. There is no path that fetches a clip from a URL, verified by looking for one. A render exports its full source provenance. Do not build yet: No driving or rendering yet. ### Phase 3 · Driving a likeness convincingly enough Build: Drive the avatar from a performance's motion and a speech track, keeping lip movement aligned to the audio's own timings. Handle head turns and occlusion. Detect output that falls into the range where it looks wrong and flag it rather than shipping it. Trap: Lip movement estimated from text rather than from the audio's actual phoneme timings drifts out of sync within a sentence, and unlike a caption drifting, a mouth out of sync is immediately and viscerally noticeable. Take the timings from the speech itself. Done when: Lip movement is driven by the audio's own timings and stays aligned over a two minute render, verified by watching. Head turns and partial occlusion do not produce artefacts. Output quality is scored and low scores are flagged rather than delivered. A render is resumable after interruption. Do not build yet: No marking or publishing yet. ### Phase 4 · Marking every output as what it is Build: Embed provenance metadata marking the output as AI generated, and burn in a visible indication where the intended distribution requires one. Make an unmarked render unexportable in the export path rather than by convention. Trap: Marking in the database and not in the file means the marking is lost the moment the file is copied anywhere, which is immediately. The mark has to travel with the bytes, and the export path has to read it back from the file rather than trusting a column. Done when: Every export carries provenance metadata read back from the file itself, not from the database. An unmarked file cannot be exported, tested by stripping the metadata and retrying. Visible marking is applied where the configured distribution requires it. The marking survives a re-encode by common tools. Do not build yet: No distribution or reporting yet. ### Phase 5 · Withdrawal that reaches published work Build: Record where every render was published. On withdrawal, list every published output derived from that person and support taking them down. Report what could not be withdrawn and why. Trap: Treating withdrawal as a future-only restriction leaves everything already published in place, which is exactly what the person is asking about when they withdraw. Track publication destinations per render so withdrawal produces an actionable list rather than an apology. Done when: Withdrawal produces a complete list of published outputs derived from that person, tested with renders published to two destinations. Takedown is supported where the destination allows it and reported where it does not. The withdrawal action and its outcome are recorded. A person can ask what exists of them and get a complete answer. Do not build yet: Nothing after this. ### Out of scope (and why) - Using anyone's likeness without a consent record, which is what the category is usually built to do and what the state digital replica statutes address. - Ingesting clips from platforms, which takes someone's copyrighted work without a licence and has no place in the build. - Voice cloning of a person who has not consented specifically to voice use, which several statutes treat separately from likeness. - Legal advice. The state patchwork differs and there is no federal right, so where you are matters. ### README must contain - That an avatar requires a live consent record and withdrawal actually disables it - That synthetic use is a separate licence permission from derivative use and defaults to refused - That there is no path to ingest a clip from a URL, deliberately - That provenance marking lives in the file and is read back from it before export - That withdrawal produces a list of published outputs rather than a future-only restriction
# Architecture · Ghostfeed ## Stack | Part | Choice | Why | | --- | --- | --- | | Consent | A signed record per depicted person, with scope and revocation | the likeness right belongs to the person and can be withdrawn | | Avatars | Built only from footage whose subject consented | an avatar is a digital replica and that is the thing the statutes cover | | Driving | Motion and speech from a performance you own or licensed | the source performance is somebody's work as well as somebody's movement | | Rendering | A local or self-hosted pipeline with provenance embedded | output leaving unmarked is the failure the whole design prevents | | Runtime | Python 3.12 with a job queue | renders are long, fail, and need to be resumable | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Consent | Scoped records and working withdrawal. | Not swappable for a checkbox. Withdrawal that does nothing is not consent. | | Licensing | Three separate source permissions. | Any store, provided synthetic use is separate and defaults to refused. | | Driving | Motion and audio-timed lip movement. | Any model, provided lip timing comes from the audio. | | Marking | File-embedded provenance verified at export. | Not swappable for a database flag, which is lost on the first copy. | | Withdrawal | Finding and addressing published output. | Any implementation, provided it produces a list rather than a future-only rule. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DATABASE_URL` · required, secret · Consent records and provenance. The evidence you were permitted to make these. - `CONSENT_REQUIRED` · required · Present as a constant so the answer is visible. There is no other value. - `SYNTHETIC_MARKING` · required · Embedded in the file and verified before export. - `URL_INGESTION` · required · Present as a constant. A clip from a URL arrives without a licence record. - `RENDER_DIR` · required · Outputs, each carrying provenance metadata in the file. - `CONSENT_EVIDENCE_DIR` · required · Signed forms. Retained longer than the renders they permit. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · Ghostfeed product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: A signed record per depicted person, with scope and revocation, Built only from footage whose subject consented, Motion and speech from a performance you own or licensed, A local or self-hosted pipeline with provenance embedded, Python 3.12 with a job queue. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - A consent checkbox with no revocation path is not consent in any meaningful sense, and the first person who changes their mind will find there is nothing the tool can do. Build withdrawal in the same phase as consent, and make it actually disable the avatar and flag the renders rather than only setting a flag on a row. - A licence that permits derivative works does not necessarily permit using the footage to drive a synthetic performance, and treating those as the same question is how a permitted use becomes an unpermitted one. Record synthetic use separately, and default it to not permitted where the licence is silent. - Lip movement estimated from text rather than from the audio's actual phoneme timings drifts out of sync within a sentence, and unlike a caption drifting, a mouth out of sync is immediately and viscerally noticeable. Take the timings from the speech itself. - Marking in the database and not in the file means the marking is lost the moment the file is copied anywhere, which is immediately. The mark has to travel with the bytes, and the export path has to read it back from the file rather than trusting a column. - Treating withdrawal as a future-only restriction leaves everything already published in place, which is exactly what the person is asking about when they withdraw. Track publication destinations per render so withdrawal produces an actionable list rather than an apology.
# Delivery milestones · Ghostfeed Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Consent as a record with teeth An avatar cannot exist without a consent that can be withdrawn. ### Steps 1. Model scope as named permitted uses A single boolean is not consent to anything specific. Files: `src/consent.py` 2. Make avatars require a live consent A foreign key, not a check. Structural rather than procedural. Files: `src/schema.sql` 3. Implement withdrawal now Disabling the avatar and flagging renders. Consent without it is decorative. Files: `src/revoke.py`, `tests/test_revocation.py` ### Done when - [ ] An avatar requires a live consent - [ ] Withdrawal disables the avatar and flags renders - [ ] Scope names specific uses - [ ] An expired consent blocks new renders ### Watch out - A consent checkbox with no revocation path is not consent in any meaningful sense, and the first person who changes their mind will find there is nothing the tool can do. Build withdrawal in the same phase as consent, and make it actually disable the avatar and flag the renders rather than only setting a flag on a row. ## M2 · Source material you have rights to Every clip, voice and background carries a licence permitting synthetic use. ### Steps 1. Record three permissions separately Derivative, commercial, synthetic. A licence may allow one and not the others. Files: `src/licences.py` 2. Default synthetic use to not permitted Where the licence is silent, which is most of the time. Files: `src/licences.py` 3. Provide no URL ingestion Its absence is the design. A clip from a URL has no licence record. Files: `src/sources.py` ### Done when - [ ] An unlicensed source cannot be used - [ ] Synthetic use is separate and defaults to refused - [ ] No URL ingestion path exists - [ ] Renders export their source provenance ### Watch out - A licence that permits derivative works does not necessarily permit using the footage to drive a synthetic performance, and treating those as the same question is how a permitted use becomes an unpermitted one. Record synthetic use separately, and default it to not permitted where the licence is silent. ## M3 · Driving a likeness convincingly enough The avatar moves and speaks in a way that is watchable. ### Steps 1. Drive the mouth from audio timings Not from text. Estimated timing drifts within a sentence and is very visible. Files: `src/lipsync.py` 2. Handle turns and occlusion Where these pipelines produce their worst artefacts. Files: `src/drive.py` 3. Score and flag poor output Shipping a bad render is worse than reporting one. Files: `src/quality.py` ### Done when - [ ] Lip movement stays aligned over two minutes - [ ] Turns and occlusion do not produce artefacts - [ ] Low quality output is flagged, not delivered - [ ] An interrupted render resumes ### Watch out - Lip movement estimated from text rather than from the audio's actual phoneme timings drifts out of sync within a sentence, and unlike a caption drifting, a mouth out of sync is immediately and viscerally noticeable. Take the timings from the speech itself. ## M4 · Marking every output as what it is No file leaves this pipeline without being identifiable as synthetic. ### Steps 1. Embed in the file, verify by reading back A database column is lost the moment the file is copied. Files: `src/marking.py` 2. Block export on missing marking Read from the file. Test by stripping it deliberately. Files: `src/export.py`, `tests/test_marking.py` 3. Apply visible marking where required Metadata satisfies machine readability, not human disclosure. Files: `src/overlay.py` ### Done when - [ ] Provenance is read back from the file - [ ] A stripped file cannot be exported - [ ] Visible marking applies where required - [ ] Marking survives a common re-encode ### Watch out - Marking in the database and not in the file means the marking is lost the moment the file is copied anywhere, which is immediately. The mark has to travel with the bytes, and the export path has to read it back from the file rather than trusting a column. ## M5 · Withdrawal that reaches published work (production only) When someone withdraws consent, you can find and address what is already out. ### Steps 1. Record publication destinations per render Withdrawal is only actionable if you know where things went. Files: `src/publications.py` 2. Produce a takedown list on withdrawal Actionable, not an apology. Files: `src/revoke.py` 3. Answer what exists of a person They are entitled to ask and you should be able to say. Files: `src/subject-report.py` ### Done when - [ ] Withdrawal lists every published output - [ ] Takedown works or reports why it cannot - [ ] Withdrawal actions and outcomes are recorded - [ ] A person can be told what exists of them ### Watch out - Treating withdrawal as a future-only restriction leaves everything already published in place, which is exactly what the person is asking about when they withdraw. Track publication destinations per render so withdrawal produces an actionable list rather than an apology.
# Operations · Ghostfeed ## Backup Consent evidence is the most important thing here and must outlive the renders. Back it up separately and with longer retention, since a render whose consent record is lost should be treated as unpermitted. ## Restore Restore and confirm consent records and their revocation states survived, since restoring a revoked consent as live would re-enable an avatar someone asked you to stop using. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Consents approaching expiry and renders whose consent has been revoked but not yet withdrawn from publication. The second is the one with a person waiting on the other end of it. ## Incident checklist A published output derived from a withdrawn consent is the failure that matters, and the publication records are what turn it from an unbounded problem into a list. That is the reason they exist. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] An avatar without a live consent is confirmed impossible - [ ] Withdrawal is confirmed to disable the avatar and flag renders - [ ] An unlicensed source is confirmed unusable - [ ] A stripped file is confirmed unexportable - [ ] Withdrawal is confirmed to list published outputs ## Launch constraint Do not market omitted Ghostfeed capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Consent records and provenance. The evidence you were permitted to make these. DATABASE_URL=postgres://localhost:5432/avatars # Required. Present as a constant so the answer is visible. There is no other value. CONSENT_REQUIRED=always # Required. Embedded in the file and verified before export. SYNTHETIC_MARKING=always # Required. Present as a constant. A clip from a URL arrives without a licence record. URL_INGESTION=disabled # Required. Outputs, each carrying provenance metadata in the file. RENDER_DIR=./renders # Required. Signed forms. Retained longer than the renders they permit. CONSENT_EVIDENCE_DIR=./consents
$ choose a build depth, inspect the files, then open the complete pack in your agent
The generation stack spans Google, fal.ai, Replicate and BytePlus, and any one of them can change price, quality or content filters in a week · the subscription outsources that churn and turns per-second render invoices into one number. Stalled renders get reaped and refunded rather than billed, which a DIY build does not do for you. And the TikTok side arrives already through platform review, which is the weeks a solo builder spends before posting anything.
xa template library scraped from TikTok and Instagram and scene-cut into ready 9:16 clips, instead of clips you source and trim yourself
xavatar consistency across renders · one stored reference photo re-fed into every call, plus the draft-and-approve flow that keeps a face you picked
xa director loop that writes the slideshow copy, searches for backgrounds, looks at the candidates and casts them, then rotates the deck into variants, plus a timeline editor with its own render workers
x44 agent-API tools · the same workspace drivable from Claude, which is the part with no weekend equivalent
xa TikTok app that already cleared review, and a reaper that refunds most stalled renders instead of quietly eating them
Ghostfeed pricing
| plan | monthly | annual (per mo) | what you get |
|---|---|---|---|
| free | $0 | $0 | 1 workspace; 1 slideshow project; 3 variants; 0 included video credits. |
| starter | $29 | — | 150 credits/month, advertised as about 25 videos; 1 workspace; extra credits $0.18 each. |
| growth | $49 | — | 300 credits/month, advertised as about 50 videos; 3 workspaces; extra credits $0.16 each. |
| agency | $99 | — | 700 credits/month, advertised as about 115 videos; 10 workspaces; extra credits $0.14 each. |
free tier1 workspace, 1 slideshow project and 3 variants; 0 included video credits, so the free plan renders no paid-credit video.
billingmonthly only; no annual plan is displayed
hidden costsIncluded monthly credits reset and do not roll over; bought extra credits do not expire. Video generally costs 1 credit per rendered second and images cost 1 credit each.
verified 2026-08-14 · source ↗
Is Ghostfeed free?
The free plan renders no video at all: one slideshow project with three variants, one workspace, library and posting access, and the agent API, which is ungated on every plan including this one. Paid is Growth at $49/mo (checked 2026-08-07).
Vibecode Ghostfeed
Kinda. The core of Ghostfeed is buildable in a weekend with the prompt on this page, but there are real gaps: a template library scraped from TikTok and Instagram and scene-cut into ready 9:16 clips, instead of clips you source and trim yourself, avatar consistency across renders · one stored reference photo re-fed into every call, plus the draft-and-approve flow that keeps a face you picked. Read the honest list above before committing.
How much does Ghostfeed cost?
Ghostfeed costs about $49/month (Growth, checked 2026-08-07), which is $588 per year.
What do I lose by replacing Ghostfeed?
Honestly: a template library scraped from TikTok and Instagram and scene-cut into ready 9:16 clips, instead of clips you source and trim yourself; avatar consistency across renders · one stored reference photo re-fed into every call, plus the draft-and-approve flow that keeps a face you picked; a director loop that writes the slideshow copy, searches for backgrounds, looks at the candidates and casts them, then rotates the deck into variants, plus a timeline editor with its own render workers; 44 agent-API tools · the same workspace drivable from Claude, which is the part with no weekend equivalent; a TikTok app that already cleared review, and a reaper that refunds most stalled renders instead of quietly eating them. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Ghostfeed?
Yes: ComfyUI (Node graph for running open image and video models locally · the render pipeline without the hosted invoice.), Wan2.2 (Apache-2.0 open video model with image-to-video, the free stand-in for the hosted animation step if you own a GPU.), Postiz (Open-source social publishing · the scheduling and OAuth half, self-hostable, so you never touch the TikTok API yourself.). Using prior art is also vibecoding; the prompt is for when you want it exactly your way.