Show HN: I built a social media management tool in 3 weeks with Claude and Codex

BrightBean Studio is an open-source, self-hostable social media management platform built in just three weeks using AI tools, offering a free alternative to costly SaaS platforms.
Open-source social media management for creators, agencies, and SMBs.
BrightBean Studio is an open-source, self-hostable social media management platform built for creators, agencies and SMBs. It does what Sendible, SocialPilot, or ContentStudio do, but free and without per-seat, per-channel, or per-workspace limits. Plan, compose, schedule, approve, publish, and monitor content across Facebook, Instagram, LinkedIn, TikTok, YouTube, Pinterest, Threads, Bluesky, Google Business Profile, and Mastodon from a single multi-workspace dashboard.
It's for people managing many client accounts under one roof who'd rather own their social stack than pay $100–300/month to a SaaS vendor. Every feature is available to every user. No paid tier, no feature gate, no upsell.
You can deploy it with a one-click button on Heroku, Render, or Railway, run it on your own VPS via Docker, or run it locally. All platform integrations talk directly to the official first-party APIs using your own developer credentials, so there's no aggregator middleman, no vendor lock-in, and no third party sitting between you and your data.
Multi-workspace & teams | Unlimited orgs → workspaces → members. Granular RBAC with custom roles, invitations, and a separate Client role for external collaborators. | Content composer | Rich editor with per-platform caption/media overrides, version history, reusable templates, content categories & tags, a Kanban idea board. | Calendar & scheduling | Visual calendar with recurring weekly posting slots per account and named queues that auto-assign posts to the next available slot. | Publishing engine | Direct first-party API integrations (no aggregator), automatic retries, per-account rate-limit tracking, and a 90-day publish audit log. | Approval workflows | Configurable stages (none / optional / internal / internal + client), threaded internal & external comments, reminders, and a full audit trail. | Unified social inbox | Comments, mentions, DMs, and reviews from every connected platform in one place, with sentiment analysis, assignments, threaded replies, and historical backfill. | Media library | Org- and workspace-scoped libraries with nested folders, auto-generated platform-optimized variants, and alt text. | Client portal | Passwordless 30-day magic-link access so clients can approve or reject posts without creating an account. | Notifications | In-app, email, and webhook delivery with per-user preferences for every event type. | Security & ops | Encrypted token & credential storage, optional 2FA (TOTP), Google/GitHub SSO, Sentry support, and a 7-day reversible org-deletion grace period. | White-label friendly | Per-workspace branding (logo, colors) and workspace defaults for hashtags, first comments, and posting templates. |
| Heroku | Render | Railway | |---|---|---|
After deploying, set these environment variables in your platform's dashboard:
| Variable | Required | Description |
|---|---|---|
SECRET_KEY |
Auto-generated | Django secret key. Set automatically by the deploy button. |
ENCRYPTION_KEY_SALT |
Auto-generated | Encryption salt. Set automatically by the deploy button. |
DATABASE_URL |
Auto-provisioned | PostgreSQL connection string. Set automatically. |
ALLOWED_HOSTS |
Yes | Your app's domain, e.g. your-app.herokuapp.com |
APP_URL |
Yes | Full public URL, e.g. https://your-app.herokuapp.com |
STORAGE_BACKEND |
No | Set to s3 for S3/R2 storage. Default: local . Heroku, Render, and Railway have ephemeral filesystems, so uploaded files are lost on redeploy without S3. |
S3_ENDPOINT_URL |
If using S3 | S3-compatible endpoint URL |
S3_ACCESS_KEY_ID |
If using S3 | S3 access key |
S3_SECRET_ACCESS_KEY |
If using S3 | S3 secret key |
S3_BUCKET_NAME |
If using S3 | S3 bucket name |
EMAIL_HOST |
No | SMTP server for sending invitations and password resets |
EMAIL_PORT |
No | SMTP port (default: 587 ) |
EMAIL_HOST_USER |
No | SMTP username |
EMAIL_HOST_PASSWORD |
No | SMTP password |
GOOGLE_AUTH_CLIENT_ID |
No | For Google OAuth login. Get from Google Cloud Console → Credentials. |
GOOGLE_AUTH_CLIENT_SECRET |
No | Google OAuth secret |
For social media API keys, see Platform Credentials. Full variable reference: .env.example
.
git clone https://github.com/brightbeanxyz/brightbean-studio.git
cd brightbean-studio
cp .env.example .env
Edit .env
- change
DATABASE_URL
to point to the Docker service name:
DATABASE_URL=postgres://postgres:postgres@postgres:5432/brightbean
Then start everything:
docker compose up -d
docker compose exec app python manage.py migrate
docker compose exec app python manage.py createsuperuser
Open http://localhost:8000 - you're running.
Run everything natively - no Docker, no PostgreSQL install. Uses SQLite for the database.
- Python 3.12+
- Node.js 20+
1. Clone and configure
git clone https://github.com/brightbeanxyz/brightbean-studio.git
cd brightbean-studio
cp .env.example .env
2. Switch to SQLite
Open .env
and replace the DATABASE_URL
line:
DATABASE_URL=sqlite:///db.sqlite3
That's it - no database server to install or manage.
3. Set up Python
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
4. Set up Tailwind CSS
cd theme/static_src
npm install
cd ../..
5. Run database migrations
python manage.py migrate
6. Create your admin account
python manage.py createsuperuser
7. Start the app (3 terminal tabs)
Tab 1 - Tailwind watcher:
cd theme/static_src && npm run start
Tab 2 - Django dev server:
source .venv/bin/activate
python manage.py runserver
Tab 3 - Background worker:
source .venv/bin/activate
python manage.py process_tasks
Open http://localhost:8000 and log in with the superuser you created.
source .venv/bin/activate # activate Python env
python manage.py runserver # start web server
# (open another tab)
python manage.py process_tasks # start worker
Note:SQLite is fine for local development and small deployments. For production or heavy concurrent usage, switch to PostgreSQL.
pytest
With coverage:
pytest --cov=apps --cov-report=term-missing
ruff check . # lint
ruff format --check . # format check
mypy apps/ config/ --ignore-missing-imports # type check
Auto-fix lint issues:
ruff check --fix .
ruff format .
# On your server:
git clone https://github.com/brightbeanxyz/brightbean-studio.git
cd brightbean-studio
cp .env.example .env
# Edit .env:
# SECRET_KEY=<generate a random 50+ char string>
# DEBUG=false
# ALLOWED_HOSTS=yourdomain.com
# APP_URL=https://yourdomain.com
# DATABASE_URL=postgres://postgres:<strong-password>@postgres:5432/brightbean
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
docker compose exec app python manage.py migrate
docker compose exec app python manage.py createsuperuser
This starts 4 containers: app (Gunicorn), worker, PostgreSQL, and Caddy (auto-HTTPS). Edit the Caddyfile
with your domain.
To update:
git pull
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build
docker compose exec app python manage.py migrate
| Platform | Config file | Notes |
|---|---|---|
Heroku |
Procfile + app.json |
Deploy-button ready. Must use Basic+ dynos (Eco dynos break the worker). |
Railway |
railway.toml |
Three services: web, worker, managed PostgreSQL. |
Render |
render.yaml |
Blueprint with web, worker, PostgreSQL. Must use paid tier. |
All platforms with ephemeral filesystems require STORAGE_BACKEND=s3
- see
.env.example
for S3 configuration.
See architecture.md
for detailed per-platform instructions and cost breakdowns.
brightbean-studio/
├── config/
│ ├── settings/
│ │ ├── base.py # Shared settings
│ │ ├── development.py # Local dev overrides
│ │ ├── production.py # Production hardening
│ │ └── test.py # Test overrides
│ ├── urls.py # Root URL configuration
│ ├── wsgi.py
│ └── asgi.py
├── apps/
│ ├── accounts/ # Custom User model, auth, OAuth, sessions
│ ├── organizations/ # Organization management
│ ├── workspaces/ # Workspace CRUD
│ ├── members/ # RBAC, invitations, middleware, decorators
│ ├── settings_manager/ # Configurable defaults with cascade logic
│ ├── credentials/ # Platform API credential storage (encrypted)
│ └── common/ # Shared: encrypted fields, scoped model managers
├── providers/ # Social platform API modules (one file per platform)
├── templates/ # Django templates
│ ├── base.html # Layout with sidebar + nav
│ └── components/ # Reusable HTMX partials
├── static/
│ └── js/ # Vendored HTMX + Alpine.js
├── theme/ # django-tailwind theme app
│ └── static_src/
│ ├── src/styles.css # Tailwind directives
│ └── tailwind.config.js
├── Dockerfile
├── docker-compose.yml # Dev: app + worker + postgres
├── docker-compose.prod.yml # Prod override: adds Caddy, uses Gunicorn
├── Caddyfile # Reverse proxy + auto-HTTPS config
├── .env.example # All environment variables
├── Procfile # Heroku
├── app.json # Heroku deploy button
├── railway.toml # Railway config
└── render.yaml # Render blueprint
All configuration is via environment variables. See .env.example
for the full list.
Key variables for local development:
| Variable | Default | Description |
|---|---|---|
SECRET_KEY |
(required) | Django secret key. Any random string for dev. |
DEBUG |
false |
Set to true for local development. |
DATABASE_URL |
- | PostgreSQL connection string. |
STORAGE_BACKEND|local|localfor filesystem,s3for S3-compatible storage. |EMAIL_BACKEND_TYPE|smtp| Set tosmtpfor SMTP or leave default (console in dev). |
To connect social media accounts, you need API credentials from each platform's developer portal. You can set these via environment variables in .env
(see .env.example
) or through the admin UI at Settings → Platform Credentials.
Redirect URI: When registering your app on any platform, set the OAuth redirect URI to:
{APP_URL}/social-accounts/callback/{platform}/
For example, if your APP_URL
is https://brightbean.example.com
, the Facebook redirect URI would be https://brightbean.example.com/social-accounts/callback/facebook/
.
Facebook, Instagram, and Threads all use the same Meta app credentials.
Go to Meta for Developers and create a new app (type:
Business) - Under
App Settings → Basic, copy yourApp IDandApp Secret - In the App Dashboard, go to
Use casesand add the following four use cases. For each use case, click into it and go toPermissions and featuresto add the required optional permissions:Use case: "Manage everything on your Page"(Facebook)- This use case auto-includes
business_management
,pages_show_list
, andpublic_profile
- Add these optional permissions:
pages_manage_posts
,pages_read_engagement
,pages_read_user_content
,pages_manage_metadata
Use case: "Messenger from Meta"(Facebook Messaging)- Required to enable the
pages_messaging
permission, which is not available under the "Manage Pages" use case - Add the optional permission:
pages_messaging
Use case: "Manage messaging & content on Instagram"(Instagram)- Add these permissions:
instagram_basic
,instagram_content_publish
,instagram_manage_comments
,instagram_manage_insights
Use case: "Access the Threads API"(Threads)- This use case auto-includes
threads_basic
- Add these optional permissions:
threads_content_publish
,threads_manage_insights
,threads_manage_replies
- This use case auto-includes
Under
Facebook Login → Settings → Valid OAuth Redirect URIs, add the following redirect URIs:{APP_URL}/social-accounts/callback/facebook/ {APP_URL}/social-accounts/callback/instagram/ {APP_URL}/social-accounts/callback/threads/
Set the environment variables:
PLATFORM_FACEBOOK_APP_ID=your-app-id PLATFORM_FACEBOOK_APP_SECRET=your-app-secret
The Instagram (Personal) connector uses the Instagram API with Instagram Login - a separate OAuth flow from the Facebook Login-based Instagram connector above. This supports personal, creator, and business Instagram accounts without requiring a linked Facebook Page.
- In the same Meta app, go to
Use casesand add the**"Instagram API"use case - Under
API setup with Instagram Login, note yourInstagram App IDandInstagram App Secret**(these are different from your Facebook App ID/Secret) - Go to
Permissions and featuresand add the required permissions:
instagram_business_basic
,instagram_business_content_publish
,instagram_business_manage_comments
,instagram_business_manage_messages
-
Under API setup with Instagram Login → Step 4: Set up Instagram business login, clickSet upand add the redirect URI:
{APP_URL}/social-accounts/callback/instagram_personal/ -
Set the environment variables:
PLATFORM_INSTAGRAM_APP_ID=your-instagram-app-id PLATFORM_INSTAGRAM_APP_SECRET=your-instagram-app-secret
Brightbean uses a single LinkedIn app (with Community Management API) for both personal profile and Company Page connections - each flow simply requests different OAuth scopes. The connect page shows two cards: LinkedIn (Personal Profile) and LinkedIn (Company Page).
-
Go to the LinkedIn Developer Portal and create a new app
-
Verify your app's association with a LinkedIn Company Page
-
Under Products, request access to:Community Management API*(restricted - requires LinkedIn review)*
-
Under Auth, addbothredirect URIs:
{APP_URL}/social-accounts/callback/linkedin_personal/ {APP_URL}/social-accounts/callback/linkedin_company/ -
Scopes are requested per connection type: Personal profile:
r_basicprofile
,w_member_social
,r_member_social
Company page:r_basicprofile
,w_member_social
,w_organization_social
,r_organization_social
,rw_organization_admin
- Set the environment variables:
PLATFORM_LINKEDIN_CLIENT_ID=your-client-id PLATFORM_LINKEDIN_CLIENT_SECRET=your-client-secret
Refresh tokens:Community Management API provides refresh tokens natively (365-day lifetime, access tokens last 60 days). No Advertising API product is needed. Note: theShare on LinkedInandCommunity Management APIproducts are mutually exclusive on the same app - use Community Management API as it covers both personal and organization scopes.
-
Go to the TikTok Developer Portal and create a new app
-
Add the products Login Kit,Content Posting API, andComment API - Configure the redirect URI under your app's settings:
{APP_URL}/social-accounts/callback/tiktok/ -
Required scopes:
user.info.basic
,video.publish
,video.upload
,comment.list
,comment.list.manage
- Note: TikTok uses
Client Key(not Client ID). Copy theClient KeyandClient Secretfrom your app dashboard - Set the environment variables:
PLATFORM_TIKTOK_CLIENT_KEY=your-client-key PLATFORM_TIKTOK_CLIENT_SECRET=your-client-secret
YouTube and Google Business Profile share the same Google Cloud credentials.
-
Go to the Google Cloud Console and create a new project (or select an existing one)
-
Enable the following APIs under APIs & Services → Library:YouTube Data API v3(for YouTube)My Business Account Management API,My Business Business Information API, andGoogle My Business API(for Google Business Profile)
-
Go to APIs & Services → Credentialsand create anOAuth 2.0 Client ID(type: Web application) - Add the following redirect URIs under Authorized redirect URIs:
{APP_URL}/social-accounts/callback/youtube/ {APP_URL}/social-accounts/callback/google_business/ -
Copy the Client IDandClient Secret - Required scopes: YouTube:
https://www.googleapis.com/auth/youtube.upload
,https://www.googleapis.com/auth/youtube.readonly
,https://www.googleapis.com/auth/youtube.force-ssl
Google Business Profile:https://www.googleapis.com/auth/business.manage
-
Set the environment variables:
PLATFORM_GOOGLE_CLIENT_ID=your-client-id PLATFORM_GOOGLE_CLIENT_SECRET=your-client-secret -
Go to the Pinterest Developer Portal and create a new app
-
Under your app settings, add the redirect URI:
{APP_URL}/social-accounts/callback/pinterest/ -
Copy the App IDandApp Secret - Required scopes:
boards:read
,pins:read
,pins:write
- Set the environment variables:
PLATFORM_PINTEREST_APP_ID=your-app-id PLATFORM_PINTEREST_APP_SECRET=your-app-secret
No developer app registration needed. Users connect by entering their Bluesky handle and an App Password:
- Log in to Bluesky
- Go to Settings → App Passwords - Create a new app password and use it when connecting your account in Brightbean
No developer app registration needed. Brightbean automatically registers an OAuth application on each Mastodon instance when a user connects their account. Users just need to enter their instance URL (e.g., mastodon.social
).
See the Supported Platforms matrix above for per-platform inbox capabilities.
To import historical messages (e.g., from the last 7 days):
python manage.py backfill_inbox --days 7
Options:
--days N
-
Number of days to backfill (default: 7)
--platform NAME -
Only backfill a specific platform (e.g.,
youtube
,linkedin
,tiktok
)--account-id UUID
- Only backfill a specific account
| Layer | Technology | |---|---| | Backend | Django 5.x | | Frontend | Django templates, HTMX, Alpine.js | | CSS | Tailwind CSS 4 via django-tailwind | | Database | PostgreSQL 16+ | | Background jobs | django-background-tasks (no Redis required) | | Auth | django-allauth (email + Google OAuth) | | Media | Pillow (images), FFmpeg (video) | | Deployment | Docker, Gunicorn, Caddy |
Docker: postgres container is unhealthy Wait 10-15 seconds after
docker compose up
for the health check to pass, then retry your command. Check logs with docker compose logs postgres
.** python manage.py migrate fails with connection errors** Make sure PostgreSQL is running and healthy. For Docker:
docker compose ps
should show postgres as "healthy". For local: verify the DATABASE_URL
in .env
matches your setup.Tailwind CSS changes not appearing
Make sure the Tailwind watcher is running: cd theme/static_src && npm run start
. If styles still don't update, try npm run build
for a full rebuild.
OAuth callback errors ("redirect URI mismatch")
The redirect URI registered on the platform must exactly match {APP_URL}/social-accounts/callback/{platform}/
. Check that APP_URL
in .env
matches the URL you're accessing (including http
vs https
and port number).
Background tasks not running (posts not publishing)
Make sure the worker is running: python manage.py process_tasks
. In Docker: check docker compose logs worker
.
See CONTRIBUTING.md for development setup, coding guidelines, and how to submit pull requests.
To report a security vulnerability, see SECURITY.md. Do not open a public issue.
AGPL-3.0 - see LICENSE for details.
Source: Hacker News
















