๐Ÿง’ The whole story in plain words

Brandon has 4 little online magazines on a website called Substack. He wanted a robot to write posts and put them up by itself, every hour, with no help.

The robot has been awake for weeks. It has put up zero posts. Not one. Here is why.

Big reason: Substack locked its front door. It will not give robots a key to post. So the only way in is to pretend to be a person clicking buttons in a web browser. That is slow and breaks easily.

The robot also never logged in. To click those buttons it needs to be signed in first. That one sign-in step was never done. So every hour the robot wakes up, finds no key, writes a draft in a drawer, and goes back to sleep. The drawer fills up. Nothing ever gets posted.

Three small bugs made it worse: the robot was blocked from reading its secret password file 118 times, it choked on the numbers "08" and "09" on the clock 21 times, and it kept running out of fresh things to write.

The fix that works: stop trying to do it in secret. Use the browser Brandon is already signed into, let the robot fill in the post, and let Brandon press the last button himself. Safe, and it actually works.

Diagnostic Report ยท Substack Automation

Why the Loop Runs Every Hour and Posts Nothing

Every Substack-automation chat, audited against the actual run logs and on-disk state. The wall is real, and it is fixable.

Verdict: 0 posts ever published

The loop is not broken from lack of code. It was built three times over. It has never crossed one manual login, the one step that hands a session cookie to the publisher (see the 2026-06-27 correction in section 5).

Across the entire log history the publisher fired for weeks and produced zero live posts. Seven drafts sit in a queue. The auth/ folder that would hold a logged-in session is empty.

1

The receipts (verified by hand this session)

๐Ÿ”ด
Posts published to Substack, ever โ€” both run logs
0
๐Ÿ“ญ
Logged-in sessions captured โ€” ~/Projects/_outputs/substack/auth/ is total 0
empty
๐Ÿšซ
Permission errors reading the .env โ€” macOS TCC blocks the cron job
118
๐Ÿ•—
Clock crashes โ€” date +%H read as octal at 08:00 and 09:00
21
๐Ÿ“
Drafts written, none with a published field or URL โ€” posted.json
7
๐Ÿ“‚
Executable files in the /substack-transfer skill โ€” only a SKILL.md
0
2

The setup: 4 lanes, 3 registered, 0 live

LaneSubstackTopicState
1keyofdavid2Christiancreated no posts
2brandonpag3Occult / Tarotcreated no posts
3onethingneededMusiclaunch draft ready no posts
4claudekitsLife / Codenever registered

A tell: the loop drafted a post for lane 4 on 2026-06-12 even though lane 4 has no registered Substack to post to. The generator and the publisher were never wired to the same reality.

3

Every attempt, and the exact wall it hit

2026-06-10 ยท CEO plan
The 10-phase launch plan
Produced CEO_10_PHASE_LAUNCH_PLAN.md. Phase 8 was "automated scheduling fires daily per lane." A plan, no working publish.
2026-06-10 / 11 ยท Build
The headless auto-poster (Playwright + Keychain vault)
First tried to lift the existing Chrome session cookie. It failed.
"Chrome encrypts session cookies with app-bound encryption now, so I can't lift them." ยท "cookies found: 7, substack.sid: MISSING."
2026-06-12 ยท Audit
Status board for the ten-Substack plan
The session named the founding constraint out loud and then built a status board instead of a publisher.
"Substack has no public write API." (this exact line appears 6 times across the chats and the plan doc)
2026-06-14 ยท Scheduler
The hourly loop (launchd com.drwu.substack-hourly)
The loop went live and has ticked ever since. It drafts, it pings the phone, it never posts.
"auth/ is EMPTY. Until a session is captured per lane, the hourly tick drafts and pushes a notification, but the post does not go live on Substack."
4

The root causes, ranked by how often they killed a run

1

Substack ships no public write API

There is no official endpoint to publish a post. Every programmatic path has to drive Substack's private, undocumented internal API through a logged-in browser session. This one fact forces every problem below.

stated in 6 sessions + the plan doc
2

The one-time session login was never run

The design needs node capture_session.js <lane> once to save a signed-in session. That step was never done, so auth/ stays empty and the publisher has nothing to authenticate with.

verified: auth/ is total 0
3

Chrome app-bound encryption blocked the shortcut

The faster plan was to reuse the cookie from the browser you are already signed into. Chrome now encrypts session cookies with app-bound encryption, so the cookie cannot be lifted out and replayed.

build session 2026-06-11
4

The headless cron job dies before it can post

Even with a session, three operational bugs crash it: macOS TCC blocks the launchd job from reading its .env (118 times), date +%H is parsed as octal and crashes at 08:00 and 09:00 (21 times), and the source picker runs dry ("no unposted source found").

verified: 118 + 21 in the logs
5

The skill is a shell with no engine

Typing /substack-transfer loads a SKILL.md and nothing else. The real pipeline lives in ~/Projects/_outputs/substack/, so the skill itself does no work.

verified: skill dir has only SKILL.md
6

The loop counts motion as delivery

It fires hourly, logs a tick, sends a notification, writes a draft to a drawer, and calls that a run. Seven drafts piled up. Not one carries a published field or a Substack URL.

verified: posted.json, 7 drafts, 0 published
5

What it actually takes to post (correction, 2026-06-27)

Update: an earlier version of this page said posting by plugin was impossible. That was wrong. A library called python-substack does publish, by carrying your logged-in session cookie. The rest of this page still stands.

The no-official-write-API part is true, but the conclusion was wrong. Substack has no public write endpoint, yet a session cookie reaches its private internal API, and that is enough to publish.

python-substack (ma2za, v0.1.22): drives Substack's private API with a session cookie, switches between publications, publishes and schedules posts, and ships an MCP server. So a connector-style path does exist after all.

Zapier / Make / n8n: their native Substack nodes really are RSS read-only, no publish action. That part of the original framing holds.

The corrected honest framing: the loop was the right shape and the missing piece is a saved session cookie, not an impossible API. A working poster now exists at ~/Projects/_outputs/substack/poster/post_substack.py (dry-run proven, 11 blocks), and the three loop bugs (octal lane date, .env TCC read, post.js save-as-draft) are fixed. What remains is the one manual step: capture a session once, then confirm before any real publish.

6

The two paths that actually publish

Recommended ยท works today

Drive your signed-in browser

  • You are already logged into Substack in Chrome. Use the Claude-in-Chrome extension to drive the editor with that live session.
  • No cookie lifting, no app-bound encryption wall, no separate capture_session.js step. Blockers 2 and 3 vanish.
  • The generator already writes clean drafts. Paste one into a new Substack draft and stop at Save draft.
  • You press Publish or Schedule. A human approves the subscriber blast.
Matches your standing rule: prefer Claude in Chrome over API. Reliable now.
If you want it unattended

Finish the headless loop

  • Run node capture_session.js <lane> once per lane to fill auth/. Re-run when the session expires.
  • Fix the .env read: move it off the offloaded Desktop path, or grant the launchd job Full Disk Access so TCC stops blocking it.
  • Fix the clock bug: LANE=$(( 10#$(date +%H) % 4 + 1 )).
  • Switch post.js from auto-publish to save as draft, so nothing blasts subscribers without you.
More work, more upkeep, and it still rides Substack's private API. Use only if hands-off is the goal.

The pattern worth naming

This loop is the completion-fraud pattern in machine form. It produces logs, notifications, and drafts, all the appearance of work, while delivering zero of the one thing asked: a live post. It looked productive for weeks. The honest metric was always one number, posts published, and that number was zero.

The fix is not more automation. It is closing the last mile: one login, three bug fixes, and a human on the Publish button.

Diagnosis grounded in run logs, posted.json, and 4 chat transcripts ยท verified 2026-06-25
Source pipeline: ~/Projects/_outputs/substack/ ยท Plan: Brandon-Vault/20 Projects/ten-substack-automation-plan.md
๐Ÿ›๏ธ Get the printable