CLI autoposter vs hosted dashboard is a custody question, not a skill question
Most writeups frame this as "technical founders run CLI tools, everyone else uses dashboards." That's the wrong axis. The axis that actually matters is custody: who holds the platform cookies, who pays for the LLM, who eats the per-account rate-limit risk when the queue scales. A hosted dashboard takes all three from you in exchange for convenience. The CLI pattern keeps all three as filesystem paths on your own machine. This page walks each path with the file names.
Direct answer (verified 2026-05-07)
Should you run a CLI autoposter or a hosted dashboard? Pick CLI when you want platform cookies and LLM keys to stay on your own machine, and you're willing to maintain a handful of launchd plists for cadence. Pick hosted when you'd rather pay a vendor to take that custody and accept the platform's posting limits as theirs to manage.
- Cookies. CLI keeps them in
~/.claude/browser-profiles/{platform}/; hosted keeps them in their database. - LLM key. CLI puts it in
.envon your machine; hosted bills you a margin-included per-seat or per-post price. - Cadence. CLI is a launchd
StartIntervalinteger you can edit; hosted is a vendor-side cron you cannot. - Audit trail. CLI writes plain log files; hosted shows you whatever the vendor surfaces.
Verified against the open-source CLI implementation at github.com/m13v/social-autoposter, specifically launchd/, browser-agent-configs/, and skill/lock.sh.
What the "dev vs non-dev" framing gets wrong
Most articles compare a CLI autoposter to a hosted dashboard by difficulty. CLI is "harder", hosted is "easier", pick based on your terminal comfort level. That framing collapses on first contact with a real operator question.
The real questions are not about syntax. They're about who is accountable when something goes sideways. When Reddit suspends the account on a Tuesday morning, who logs in to fix it. When the model provider has a bad day, whose dashboard goes red. When the platform tightens posting limits, whose cron logic adapts. A hosted dashboard gives one set of answers; a CLI gives another. Neither is universally right, but the comparison is more useful if you know which question each setup is actually answering.
The four sections below cover the four custody axes that decide the answer. Each one names a concrete file path in an open-source CLI implementation, so the claims are checkable.
Custody 1: where the platform cookies actually live
A hosted dashboard stores your platform login server-side. You sign in once via OAuth or by typing your password into their form, and the vendor holds a session token from then on. They replay it on their IP space against the platform's API or web app on whatever cadence their scheduler is set to.
The CLI variant stores the same session, but in a directory you can cd into. browser-agent-configs/reddit-agent.json sets userDataDir to __HOME__/.claude/browser-profiles/reddit. The installer at bin/cli.js substitutes __HOME__ for your actual home directory at npx social-autoposter init time. Same for twitter-agent.json. Each platform gets its own persistent Chromium profile.
Why the directory matters: it's the same cookie format your real Chrome uses. The IP that talks to Reddit is your residential IP. The user-agent string is your machine's. The fingerprint Reddit reads when it scores trust is the one your everyday browsing already established. A hosted dashboard cannot replicate that without you handing over the session, and once you have, you cannot revoke it without changing your password and breaking the integration.
The cleanup story is also concrete. To rotate a session, you rm -rf ~/.claude/browser-profiles/reddit and the next launchd tick prompts you to log in again. There is no vendor side state to clear.
Custody 2: who pays for the model
Hosted dashboards bundle the LLM into the seat price. You see one invoice line, in the vendor's currency, with their margin baked in. You don't see which model handled which post, you don't see the token count, and you can't pick a cheaper or more capable model for the threads that need it.
The CLI variant puts the key in your ~/social-autoposter/.env, the run shells out with claude -p, and the cost lands on the same Anthropic invoice as the rest of your work. There is no per-post fee. scripts/log_run.py records cost per run in dollars, so when one Reddit thread costs ten times another, you see why and you can decide whether to skip threads with that shape going forward.
The trade-off is variance. A long thread the model decides to read in full costs more tokens than a short one it skips. A hosted dashboard flattens that variance into a fixed price; the CLI variant exposes it. If you want predictability above transparency, that's a real reason to pick hosted.
Custody 3: cadence as a file you own
Hosted dashboards expose cadence as a dropdown: every hour, every six hours, twice a day. The vendor's scheduler interprets it on their infrastructure. If their cron drops a tick, backs up under load, or silently retries, you find out by checking the inbox.
The CLI variant ships 46 launchd plists in ~/social-autoposter/launchd/, one per pipeline. Engagement, DM outreach, DM replies, link edit, audit, scan, stats. The Reddit engagement plist (com.m13v.social-engage-reddit.plist) sets StartInterval to 600. That's the entire cadence configuration. Edit the integer, unload, reload, done.
Per-run lifecycle (one tick of one pipeline)
Each tick spawns a child Claude process with --strict-mcp-config pointed at exactly one platform's browser MCP config. The strict flag means a Reddit run cannot accidentally drive the Twitter profile, even if a tool call goes wrong. The platform-browser lock serializes access to the persistent profile so two pipelines that share a profile (e.g. engage-reddit and dm-replies-reddit) never interleave Chrome operations.
Custody 4: rate-limit risk and what happens when the platform changes
Every platform tightens its anti-automation posture eventually. When that happens, the question is who carries the risk.
On a hosted dashboard, the vendor decides what counts as "safe" and rate-limits you to that ceiling. If the platform tightens further next week, the vendor adapts (or doesn't) and you find out by reading their changelog. Your account's reputation is shared across whatever pool of accounts the vendor is running through the same scheduler. If another customer abuses the system and gets the vendor's IP flagged, your trust score takes a hit.
On the CLI variant, the cap is two integers you can read. The launchd StartInterval (how often a tick fires) times the per-tick action count in the script (how many things one tick does) is your effective rate. When the platform tightens, you change the integer. When the platform suspends an account, you log in on your own machine and re-prove the fingerprint, instead of opening a vendor support ticket.
“launchd plists at ~/social-autoposter/launchd/ matching com.m13v.social-*.plist, counted on 2026-05-07. One per pipeline, each with its own cadence integer.”
ls ~/social-autoposter/launchd/com.m13v.social-*.plist | wc -l
Side-by-side, on the dimensions that actually matter
The grid below collapses the four custody axes plus a few operational ones into the format hosted-vs-CLI comparisons usually skip.
| Feature | Hosted dashboard | CLI autoposter (S4L) |
|---|---|---|
| Where the platform login cookies live | Encrypted on the vendor's servers, accessible to anyone with vendor credentials and to whatever subprocessor has DB access. You hand over a session and you cannot revoke it without changing your password and breaking the integration. | On disk at ~/.claude/browser-profiles/{platform}/, written by Playwright MCP via the userDataDir field in browser-agent-configs/{platform}-agent.json. You can `rm -rf` the directory and the next run will prompt you to log in again. No third party ever sees the cookie. |
| Who pays for the LLM tokens | The vendor. Margin gets baked into a per-post or per-seat price. You see a number on your invoice; you don't see what model was used or how many tokens it took to draft. | You. Anthropic OAuth or an API key in ~/social-autoposter/.env. The orchestrator shells out to `claude -p`. You pay raw cost for the model you pick. The cost line in scripts/log_run.py is denominated in your dollars, not the vendor's. |
| What happens when the platform suspends an account | Vendor support ticket. The cookie is in their vault, the IP is theirs, you're explaining a third-party situation to a first-party platform. Recovery time is whatever the vendor's queue says. | You log in on your own machine, write the new cookie back into the persistent profile, and the next launchd tick picks up. The IP, the user agent, and the userDataDir are all yours. The platform sees you, not a fleet. |
| Cadence | A scheduler the vendor runs. You tell it 'every 4 hours' and trust the dashboard. If their cron drops a tick or backs up under load, you find out by checking the inbox. | A launchd plist on your laptop, e.g. com.m13v.social-engage-reddit.plist with StartInterval = 600. You can `cat` the plist, change the integer, `launchctl unload` and `launchctl load`. The cadence is a file you own. |
| Concurrency safety when two jobs touch the same browser profile | Vendor's problem; usually solved by serializing per-account, but you have no visibility into how. | skill/lock.sh in the repo. Per-platform locks named reddit-browser, twitter-browser. Pipeline-specific locks like 'engage-reddit' acquired with timeout 0 (skip if held). Browser-profile locks acquired before pipeline locks to avoid cross-pipeline deadlock. The whole policy is 50 lines you can read. |
| Audit trail | Vendor's logs, if exposed. Usually a sanitized 'posted at HH:MM' table. The model output, the prompt, and the failure reason are upstream of your view. | Plain text. Each run writes ~/social-autoposter/skill/logs/engage-reddit-YYYY-MM-DD_HHMMSS.log with stdout/stderr from the Claude process, plus a row in scripts/log_run.py with posted/skipped/failed/cost/elapsed. `grep` is your dashboard. |
| Rate-limit risk when the queue scales | Yours, but mediated. The vendor caps you to whatever they think is safe. If the platform tightens limits next week, the vendor adapts (or doesn't) and you find out by reading their changelog. | Yours, and visible. The cap is the launchd interval times the per-tick action count, both readable in the plist and the script. You change the integer when the platform tightens. |
Six things you can verify in the source
Every claim above traces to a file. The repo is open, so you can read the same paths and confirm the behaviour described.
Verifiable in github.com/m13v/social-autoposter
- 46 launchd plists in ~/social-autoposter/launchd/ matching com.m13v.social-*.plist (counted on 2026-05-07). One per pipeline: engage-{reddit,twitter}, dm-{outreach,replies}-{reddit,twitter}, link-edit-*, audit-*, scan-*, plus stats and dashboard.
- browser-agent-configs/reddit-agent.json sets userDataDir to __HOME__/.claude/browser-profiles/reddit. The installer (bin/cli.js) substitutes __HOME__ at npx-init time so the path is real on your machine.
- The locking policy in skill/lock.sh defines acquire_lock(name, timeout). Per-platform browser locks (reddit-browser, twitter-browser) are acquired before pipeline-specific locks; pipeline locks use timeout 0 so a still-running prior tick is skipped, never killed mid-API-call.
- The plist com.m13v.social-engage-reddit.plist sets StartInterval to 600. To change cadence, edit the integer and `launchctl unload && launchctl load`. There is no other place cadence lives.
- scripts/log_run.py records posted/skipped/failed/cost/elapsed per run. Cost is in raw model dollars, not vendor markup. The dashboard at localhost:3141 reads that table; the data never leaves your machine.
- Each run shells out with `claude -p --strict-mcp-config --mcp-config ~/.claude/browser-agent-configs/{platform}-agent-mcp.json`. The --strict flag means the spawned process can only see one platform's browser MCP, so a Reddit run cannot drive the Twitter profile.
When the hosted dashboard is the right answer
Three honest cases where you should not run the CLI variant.
- You don't have a Mac that runs 24/7. The CLI variant is launchd-driven and macOS-only as shipped. The cron-equivalent on Linux exists in the setup notes but the plists won't apply without translation. If your only always-on machine is a Linux box you don't want to maintain Python on, hosted is less work.
- You don't want to keep an LLM key on your laptop. Anthropic OAuth on a personal device is a real attack surface. If you'd rather pay a vendor to hold that key in their vault and bill you a flat per-seat price, the CLI's "raw cost, no markup" argument doesn't outweigh the security simplification.
- A non-technical teammate operates day-to-day. The CLI dashboard at localhost:3141 helps once installed, but the install requires a terminal, npx, and a one-time Playwright login per platform. If that's a hard line for the operator, you want a vendor whose UX is the dashboard, not a repo.
Outside those, the comparison stops being "CLI or hosted" and starts being "which of the four custody axes do you want to own, and which do you want to outsource."
Walk through the file paths on a call
Twenty minutes is enough to install the CLI variant against your repo, log in once on each platform, and watch the first launchd tick land a real comment.
Frequently asked questions
Is a CLI autoposter only for developers?
No, but it asks more of you than a hosted dashboard does. The setup wizard installs via `npx social-autoposter init` which copies scripts, generates launchd plists with your actual $HOME, installs psycopg2-binary, and symlinks a Claude Code skill. You run it once, log in to each platform once via Playwright MCP, and it runs on a 10-minute tick. After that, day-to-day operation is a dashboard at localhost:3141 with Pause All / Resume All buttons. The 'developer-only' framing is real on day one and inaccurate on day two.
What does 'custody of cookies' actually mean and why should I care?
When you sign in to a hosted autoposter, you hand over a session. The vendor stores it (encrypted, hopefully) and replays it from their IP space against the platform's API or web app. Two consequences. First, anyone with vendor admin access has a path to your account; their security posture becomes yours. Second, when the platform's anti-automation flags fire on the vendor's IP, your account gets the ban, not theirs. The CLI variant keeps the cookie in ~/.claude/browser-profiles/{platform}/, written there by the same Chromium your browser uses. The IP, the user agent, the device fingerprint are all yours. If you don't trust the vendor with a permanent session, the question answers itself.
Who pays for the LLM if I run a CLI autoposter?
You. Anthropic OAuth via Claude Code or an API key in ~/social-autoposter/.env. Each run shells out to `claude -p` and the cost lands on your bill. There's no markup, no per-post fee, no minimum. The trade-off: you eat the variance. A long thread that the model decides to read in full costs more than a short one that gets skipped. scripts/log_run.py records cost per run in dollars so you can see exactly where the spend went.
How is cadence configured in the CLI variant?
launchd plists at ~/social-autoposter/launchd/. Each pipeline has one. com.m13v.social-engage-reddit.plist has a StartInterval key set to 600 (seconds). Other plists use StartCalendarInterval for fixed wall-clock times. To change cadence, edit the integer, then `launchctl unload ~/Library/LaunchAgents/com.m13v.social-engage-reddit.plist && launchctl load ~/Library/LaunchAgents/com.m13v.social-engage-reddit.plist`. To pause everything, the dashboard at localhost:3141 has a Pause All button that unloads every com.m13v.social-* plist in one shot.
What stops two pipelines from clobbering each other when they share a browser profile?
skill/lock.sh. Two layers. The platform-browser lock (reddit-browser, twitter-browser) is acquired first by anything that wants to drive the profile. Pipeline-specific locks (engage-reddit, dm-replies-reddit) are acquired second. The browser lock is what serializes access; the pipeline lock prevents two ticks of the same job from racing. Pipeline locks use timeout 0 which means 'skip if held, do not wait'. That's deliberate: a still-running prior tick is the watchdog's responsibility (scripts/watchdog_hung_runs.py), not a peer pipeline's. Earlier versions killed the holder's process group at 600 seconds and clobbered unrelated steps; the timeout-0 policy fixed that.
What does the audit trail look like?
Plain files. Each run writes ~/social-autoposter/skill/logs/engage-reddit-YYYY-MM-DD_HHMMSS.log with the entire stdout and stderr of the Claude subprocess, prefixed by the run start timestamp. After the run, scripts/log_run.py writes one row to a Postgres table with columns posted, skipped, failed, cost, elapsed. The dashboard at localhost:3141 reads that table and renders charts. The data never leaves your machine, except via the Postgres URL you configured in .env (which is your own database, your own credentials).
When does a hosted dashboard win?
Three honest cases. First, you don't have a Mac that runs 24/7. The CLI variant is launchd-driven and macOS-only as shipped (the cron-equivalent on Linux is in setup/SKILL.md but the plists won't apply). Second, you don't want to keep an LLM key on your laptop and don't want to maintain it. The vendor's bundled price stops being expensive once you account for the time you'd spend rotating keys and watching dashboards. Third, you want a non-technical teammate to operate it. The CLI dashboard helps, but the install requires a terminal and `npx`. If any of those is a hard line for you, hosted is the right answer.
What does the per-platform setup actually require on first run?
One `npx social-autoposter init` to generate the plists and copy the scripts. Then a one-time login per platform: the wizard fires up a Playwright MCP browser pointed at ~/.claude/browser-profiles/{platform}/, you log in once, the cookie persists into the userDataDir. After that, the cron runs without intervention. The only time you re-log in is when the platform forces a re-auth or you `rm -rf` the profile.
Where can I read the source for everything claimed on this page?
github.com/m13v/social-autoposter. The repo's README has the full runtime diagram. The specific paths referenced here: launchd/ (the 46 plists), browser-agent-configs/{reddit,twitter}-agent.json (Playwright MCP configs with userDataDir set to a persistent path), skill/lock.sh (the cross-pipeline locking policy), skill/engage-reddit.sh (the run wrapper), scripts/scan_reddit_replies.py (the inbox-discovery loop fired every 600 seconds), scripts/log_run.py (the per-run audit row writer).
Comments (••)
Leave a comment to see what others are saying.Public and anonymous. No signup.