Funrang is a social content engine for small teams. It discovers stories, ranks what matters, generates posts and images, and helps operators review, schedule, and publish them.
This codebase is 100% generated by Codex, guided by the author Fardin Hakimi. It is a learning side-project focused on learning AI engineering through building a real evolving product.
Today the product is centered around:
- source ingestion from RSS and scraping
- clustering and ranking of candidate stories
- AI-assisted post generation
- AI story generation from free-form prompts
- image generation through the Python media service
- review, moderation, scheduling, and publishing
- Facebook publishing today, with broader channel support evolving
Funrang now has two main creation paths:
-
Real-story content creation
- ingest sources
- extract articles
- cluster and rank stories
- generate fact-based posts
-
AI story generation
- start from a user prompt
- generate a short social story
- generate a matching image prompt
- translate, hashtag, store, and draft the result
Both paths now run through the internal agent-system, which contains:
agentsworkflowsruntimememorytoolsguardrails
This is the current agentic backend layer used for research/selection and generation workflows.
Main areas you will touch most often:
src/server.ts- Express app and UI/API boot
src/runner.ts- scheduled/headless runner entrypoint
src/application/- application services that orchestrate creation flows
src/agent-system/- agent runtime, memory, tools, guardrails, agents, workflows
src/ui/- Vue UI
media_service/- Python media generation service
The current default local stack is:
- Node/TypeScript app
- Postgres
- Redis
- LM Studio for text generation and embeddings
- Python media service for image/video/media jobs
OpenAI is still supported by parts of the codebase, but the current local-first setup uses LM Studio by default.
This part is important because the repo now uses multiple env files for different runtimes.
.env.dev- local development server
.env.test- tests
.env- local production-style runner usage outside Docker
.env.docker- app container used by
docker-compose.prod.yml
- app container used by
media_service/.env- media server runtime configuration
- Node.js 20+ recommended
- Docker + Docker Compose plugin
- Python 3
ffmpegandffprobeon PATH- LM Studio running locally if you want the default local LLM path
npm i
cp .env.example .env
cp .env.example .env.dev
cp .env.example .env.testThen update the copied env files to match the current local-first defaults before you start the app:
LLM_PROVIDER=lmstudio
LLM_BASE_URL=http://127.0.0.1:1234/v1
LLM_MODEL=qwen3-8b
AI_EMBEDDING_MODEL=text-embedding-nomic-embed-text-v1.5
AI_EMBEDDING_DIMENSIONS=192
WEB_SEARCH_PROVIDER=duckduckgo
WEB_SEARCH_TIMEOUT_MS=10000
MEDIA_SERVICE_URL=http://127.0.0.1:8091
MEDIA_CALLBACK_BASE_URL=http://127.0.0.1:3333docker compose up -dThis starts the local development infra stack:
- Postgres
- Redis
- pgAdmin
npm run media:venv
npm run media:installnpm run migrate:devIf you want the Node app and media service together:
npm run devIf you want to run only the Node app:
npm run uiDefault local URLs:
- app:
http://localhost:3333 - media service:
http://localhost:8091 - pgAdmin:
http://localhost:5050
npm testUseful checks:
npm run typecheck
npm run check
npm run eval:ai-story
docker compose ps
ffmpeg -version
ffprobe -versionThe current local default is LM Studio.
Typical local settings:
LLM_PROVIDER=lmstudio
LLM_BASE_URL=http://127.0.0.1:1234/v1
LLM_MODEL=qwen3-8b
AI_EMBEDDING_MODEL=text-embedding-nomic-embed-text-v1.5
AI_EMBEDDING_DIMENSIONS=192Recommended local workflow:
- Start LM Studio server
- Load your chat model
- Load your embedding model
- Start the Node app
The current local setup expects:
- a chat model such as
qwen3-8b - an embedding model such as
text-embedding-nomic-embed-text-v1.5
The current codebase supports:
- chat/text generation
- embeddings
- agent-loop execution through the
agent-system
The researcher agent can now use a web search tool.
Current search config:
WEB_SEARCH_PROVIDER=duckduckgo
WEB_SEARCH_TIMEOUT_MS=10000
WEB_SEARCH_BRAVE_API_KEY=Behavior:
- default: DuckDuckGo
- optional: Brave Search when
WEB_SEARCH_PROVIDER=braveandWEB_SEARCH_BRAVE_API_KEYis set
Image and media generation are handled by the Python service in media_service/.
Node delegates media jobs to that service rather than trying to run those pipelines in-process.
Important local Node-side settings:
MEDIA_SERVICE_URL=http://127.0.0.1:8091
MEDIA_CALLBACK_BASE_URL=http://127.0.0.1:3333
IMAGE_PROVIDER=localThe detailed model/runtime settings for image and video generation live primarily in:
media_service/.env
The production-style compose stack is available in:
docker-compose.prod.yml
This stack does not use .env as the main app env file.
It currently uses:
.env.dockerfor the app containermedia_service/.envfor the media service container
docker compose -f docker-compose.prod.yml up --build -ddocker compose -f docker-compose.prod.yml downdocker compose -f docker-compose.prod.yml --profile ops up -d- app
- media server
- Postgres
- Redis
- optional pgAdmin via profile
- the app container reads
.env.docker - the media server reads
media_service/.env - compose overrides internal service URLs for Postgres/Redis/media callbacks
- the media server expects access to host AI model files at
/mnt/storage/ai - GPU access is expected for the media server
At a high level:
- ingest sources
- extract documents
- cluster and rank candidate stories
- select stories
- generate post draft
- judge/retry if needed
- generate media
- save drafts
- publish or schedule
The agent-based parts currently include:
- research selection
- AI story writing
- AI/story judging
- post generation and judging
Operationally, generation also runs through:
- BullMQ-backed AI generation tasks
- the Node application layer in
src/application/ - the Python media service for image output
To run the runner outside Docker using .env:
npm run prodThis is the scheduled/headless path for ingestion and post generation.
The app currently includes:
- local auth
- Facebook auth/connect
- YouTube auth/connect
- super-admin/internal admin endpoints
The UI and APIs are served from the same Node app.
If you are reading older discussions or docs, these are the current realities:
agent-systemis now the canonical home for the agentic backend- LM Studio is the default local LLM path
- embeddings are configured for dimension
192 - web search exists and is configurable
- media generation is delegated to
media_service - Docker production-style setup uses
.env.docker, not.env, for the app container
The README is now aligned with the current codebase at a high level, but two adjacent docs/config surfaces are still worth keeping in sync over time:
.env.examplemedia_service/README.md
If you need to share env files safely, age is a simple option.
Install:
# Ubuntu/Debian
sudo apt-get install -y age
# macOS
brew install ageEncrypt:
age -p -o .env.age .envDecrypt:
age -d -o .env .env.ageGood practice:
- send encrypted env and passphrase in different channels
- use a one-time strong passphrase
- never commit decrypted secrets