What I Built
Social Hustle generates a week's worth of on-brand social posts for small-business clients — research, angle-picking, drafting, and publishing — as a single durable Temporal workflow. You give it a client profile (voice, audience, keywords to avoid) and it comes back with drafts you approve before anything goes out. Nothing publishes without a human saying yes.
It's built for the operator, not the client: one CLI command starts a run, the Temporal UI shows each activity light up, and a small FastAPI dashboard lets you review and tick the drafts you want published. Posting goes to Bluesky; without creds it dry-runs to a per-client outbox.jsonl so you can demo it with zero setup.
Who pays: agencies and freelancers managing social for several small clients. Instead of a weekly hour of "what should we post today," they get drafts in their clients' actual voice, with the human approval still in the loop.
Demo
No deployed link yet — it runs locally. To demo:
ollama serve
temporal server start-dev
python social_hustle.py worker
python social_hustle.py run clients/brew-lane/brand.json # -> prints a workflow id
python social_hustle.py show <workflow_id> # review drafts
python social_hustle.py approve <workflow_id> 0 2 # publish #0 and #2
Or start the dashboard instead of the CLI: python web.py → http://localhost:8000.
Code
https://github.com/aijadugar/social-hustle
How I Built It
The whole LLM layer runs on Gemma 3 via Ollama — an open-weight model, local inference, $0 per post, and client data never leaves the machine. Every LLM call uses Ollama's structured-output mode (JSON schema), and the response is validated and repaired before it's used: Gemma occasionally truncates mid-string, so a single O(n) scan closes unterminated strings and open containers rather than failing the run.
Orchestration is Temporal (Python SDK). One durable ContentRun workflow per client per day: research (Google News RSS) → learn (what performed last week) → write (Gemma picks angles and writes the posts in one call) → a durable human-approval signal → publish (Bluesky, idempotent). Temporal means retries, time-skipping approval windows, and no lost state if you approve tomorrow.
Stack is deliberately tiny — stdlib + temporalio + fastapi/uvicorn, one static HTML file, no build step, no JS framework. Per-client data lives in clients/<slug>/ and is never mixed between clients.
Why Does Open Innovation Matter?
This project only works because the AI is open. Three things a closed API couldn't give it:
- Data locality. Client brand voices, drafts, and posting history are sensitive business data. Running Gemma locally means nothing ever ships to a vendor — a real requirement for an agency handling multiple clients' accounts.
- Zero marginal cost. At $0 per post, the economics work for small-business clients who can't afford $20/month AI subscriptions. An API bill scales with output; open inference doesn't.
-
You can actually inspect and fix it. When Gemma 3 truncated its output mid-string and killed the activity with
JSONDecodeError, I could read the model's raw response, write a repair pass, and tune the retry/backoff — rather than file a support ticket against a black box. The failure modes are observable and fixable, which is what makes this a product and not a demo.
My Agent Session
My First User
Prize Categories
- Open-Source AI — the entire LLM layer is open-weight Gemma 3 via Ollama
- Side Hustle — it's literally a done-for-you side hustle for social media
Top comments (0)