how it runs · stages · stack

Six stages. Eighteen seconds. Thirteen feeds.

Between a prompt and a published post there's a pipeline. Six stages, a handful of models, and a queue that fans out to thirteen platforms in parallel. Here's exactly what happens — and on what.

the cycle

What runs, and on what.

  1. 01

    prompt

    +0.0s

    You type. PrintAIr accepts a one-line idea, a paragraph, or a topic pulled from your idea bank. Persona + locale + workflow context are stamped automatically.

  2. 02

    brief

    +0.2s

    xai/grok-4-1-fast

    The LLM derives a structured creative brief — topic, key message, mood, hashtags, image prompt. Persona context prepends so voice carries through every downstream call.

  3. 03

    master

    +11.4s

    google/nano-banana-2 · xai/grok-imagine-video

    One master asset gets generated — image for image posts, faceless video for story posts. Multi-ref-capable image models receive the persona's photos so the look stays consistent post over post.

  4. 04

    cuts

    +13.1s

    ffmpeg

    Native cuts per platform: 1:1 for IG feed, 9:16 for Reels / Shorts / TikTok, 16:9 for X / YouTube long. The cuts happen locally — no extra Wiro spend.

  5. 05

    variants

    +17.8s

    xai/grok-4-1-fast (×N)

    Per-platform text rewriters fire in parallel. Each variant respects the platform's character limit, voice conventions, and hashtag norms. Live SSE token streaming pushes drafts into the UI as they generate.

  6. 06

    publish

    +18.6s

    platform OAuth / paste-creds adapters

    A human approves on the web detail page or the Telegram bot. The publish fan-out sends one variant per account in parallel; partial failures are isolated, the rest still ship.

architecture

Five rows, two stores, no glue code.

browser
Next.js 15React 19Tailwind 4
app
Better AuthAPI routesSSE
queue
RabbitMQtopics + DLQRedis pub/sub
workers
tsx · 9 consumersgeneration / publishstory-video / persona-train
providers
Wiro AI (HMAC)13 platform OAuthkokoro TTS

persistence

Postgres 17

33 migrations · Drizzle ORM

MinIO · S3

all generated assets

primitives

Six concepts. Everything maps to one.

primitive

Persona

Who the post is written and rendered as. Photos seed multi-ref image models; mood/tone seed the LLM.

personas (
  id uuid, user_id text,
  name text, description text,
  characteristics text, mood text,
  speaking_tone text, master_image_url text,
  status persona_status — active|retired|candidate
)

primitive

Scene

A single beat of a story video. Carries the visual prompt + narration + planned duration.

scenes_json: [{
  index: number,
  act: 'intro'|'development'|'conclusion',
  visualPrompt: string,
  narration: string,
  durationSec: number,
  imageAssetId, videoAssetId, narrationAssetId,
}]

primitive

Variant

One platform-shaped take of a post. Per-account binding so two LinkedIn handles each get their own.

post_variants (
  id uuid, post_id uuid,
  platform real_platform,
  social_account_id uuid,
  text text, media_asset_ids uuid[],
  status variant_status — pending|ready|publishing|published|failed
)

primitive

Workflow

A scheduled prompt template. Pulls from an idea bank, fires on cron, optionally pins a persona pool.

workflows (
  id uuid, user_id text,
  template_key text, prompt_template text,
  topic_pool jsonb, idea_tag_filter jsonb,
  account_ids jsonb, persona_ids — via workflow_personas
)

primitive

Approval

The gating moment. Web detail page or Telegram bot — both write the same row and trigger the same publish.

posts.status: 'awaiting_approval'
  → POST /api/posts/:id/approve
  → publish fan-out (one job per variant)
  → variants flip to 'published' or 'failed'

primitive

Cost ceiling

Per-account monthly cap. Workflow ticks skip when the cap is exhausted; surface progress everywhere.

social_accounts (
  monthly_budget_usd numeric(10,2),
  budget_mode 'warn'|'enforce',
  spend_this_month_usd numeric(10,2),
  spend_month_key text — YYYY-MM
)

stack

Boring infrastructure. Sharp surface.

Next.js

15.5

app router · RSC · SSE

React

19

UI runtime

Tailwind

4

CSS · @theme tokens

Drizzle ORM

0.45

schema · migrations · types

Postgres

17

primary store · 33 migrations

RabbitMQ

9 queues · DLX · DLQs

Redis

pub/sub → SSE bridge

MinIO · S3

asset storage · CDN-fronted

Now that you've seen the pipes, plug something in.

Start freeDocs · soon
How PrintAIr runs · the stages, the stack, the primitives