Two local automation workflows powered by n8n + Claude CLI over SSH.
Send a rough idea to your Telegram bot → Claude writes a LinkedIn post → approve/regenerate/cancel with inline buttons → post to LinkedIn.
Telegram message (idea)
→ n8n (Docker)
→ SSH → Mac host → claude -p "..."
→ Draft sent to Telegram with buttons
→ ✅ Post → LinkedIn
→ 🔄 Regen → Claude rewrites → new draft
→ ❌ Cancel → Done
Open a local URL in your browser → type an idea → Claude writes a blog post, LinkedIn post, and tweet in sequence → review and approve each before it publishes.
Browser form (idea)
→ n8n (Docker)
→ SSH → Mac host → generate_content.sh blog:...
→ Review page in browser
→ ✅ Approve → Dev.to publishes
→ SSH → linkedin:...|<mediumUrl>
→ Review → ✅ Approve → LinkedIn posts
→ SSH → tweet:...|<mediumUrl>
→ Review → ✅ Approve → Twitter posts
State is tracked in $getWorkflowStaticData('global') across requests. The pipeline is resumable — if Claude fails mid-chain, a retry button appears without losing prior published content.
- Docker Desktop
- Claude CLI installed and authenticated (
~/.local/bin/claude -p "test"works) - Telegram bot token — for the LinkedIn pipeline only
- LinkedIn Developer App with Share on LinkedIn + Sign In with LinkedIn using OpenID Connect products (requires a LinkedIn Company Page)
- Dev.to API key — for the blog pipeline (Settings → Extensions → DEV API Keys → Generate)
- Twitter OAuth2 credentials — for the blog pipeline
cp .env.example .envGENERIC_TIMEZONE=America/New_York
N8N_ENCRYPTION_KEY=<run: openssl rand -hex 16>
WEBHOOK_URL=https://<your-cloudflare-tunnel>.trycloudflare.com # LinkedIn pipeline onlyn8n runs in Docker and can't access the Mac's claude binary directly. A forced-command SSH key bridges the gap — the key can only run the specified script, nothing else on the machine is accessible.
LinkedIn pipeline — uses generate_post.sh:
ssh-keygen -t ed25519 -f ~/.ssh/n8n_docker_key -N ""Add to ~/.ssh/authorized_keys:
restrict,command="/path/to/scripts/generate_post.sh" ssh-ed25519 AAAA...your-public-key...
Blog pipeline — uses generate_content.sh:
ssh-keygen -t ed25519 -f ~/.ssh/n8n_blog_key -N ""Add to ~/.ssh/authorized_keys:
restrict,command="/path/to/scripts/generate_content.sh" ssh-ed25519 AAAA...your-public-key...
Enable Remote Login: System Settings → General → Sharing → Remote Login → ON
docker compose up -dThe Telegram webhook requires a public HTTPS URL:
cloudflared tunnel --url http://localhost:5678 --no-autoupdate &Copy the https://...trycloudflare.com URL into WEBHOOK_URL in .env, then restart n8n and re-publish the workflow. The URL changes every time the process restarts (free tier).
| Name | Type | Used by |
|---|---|---|
| Telegram Bot | Telegram API | LinkedIn pipeline |
| Mac SSH (LinkedIn) | SSH | LinkedIn pipeline |
| Mac SSH (Blog) | SSH | Blog pipeline |
| LinkedIn OAuth2 API | LinkedIn OAuth2 API | Both |
| Dev.to | Header Auth | Blog pipeline |
| Twitter/X | Twitter OAuth2 API | Blog pipeline |
SSH credential config: host host.docker.internal, port 22, user anmolsahu2k, auth via private key.
LinkedIn OAuth2 redirect URL: http://localhost:5678/rest/oauth2-credential/callback
- Workflows → Import from File →
workflows/linkedin-pipeline.json - Open Auth Gate node → replace
YOUR_CHAT_IDwith your Telegram Chat ID (get it from @userinfobot) - Assign credentials to each node
- Click Publish
- Workflows → Import from File →
workflows/blog-pipeline.json - Assign credentials to each node
- Click Publish
- Open
http://localhost:5678/webhook/blog-pipelinein your browser
# Test generate_post.sh (LinkedIn pipeline)
SSH_ORIGINAL_COMMAND="Why shipping fast beats waiting for perfect" \
./scripts/generate_post.sh
# Test generate_content.sh (blog pipeline)
SSH_ORIGINAL_COMMAND="blog:$(echo 'Why shipping fast beats waiting for perfect' | base64)" \
./scripts/generate_content.sh
# Test SSH from n8n's perspective (LinkedIn)
ssh -i ~/.ssh/n8n_docker_key anmolsahu2k@host.docker.internal "your idea here"
# View logs
docker compose logs -f n8n.
├── docker-compose.yml # n8n service (port 5678, localhost only)
├── .env # secrets (gitignored)
├── scripts/
│ ├── generate_post.sh # LinkedIn pipeline: called via SSH, runs claude -p
│ └── generate_content.sh # Blog pipeline: handles blog/linkedin/tweet commands
└── workflows/
├── linkedin-pipeline.json # Telegram → LinkedIn
├── blog-pipeline.json # Browser form → Dev.to + LinkedIn + Twitter
└── blog-pipeline-simple.json # Simplified version (for reference)