A local Python CLI (./slackbackup) that backs up Slack channel history via slackdump, then
exports it into two LLM-ready documents for newsletter generation — a cross-workspace digest
(messages, activity counts, inferred leadership) and a user profile report (full membership
roster with titles and Slack roles) — plus per-month channel archives and a live search. No Slack
admin access, no app install, no cloud infrastructure. See docs/DESIGN.md for
the full architecture and a data-flow diagram of what this app does versus what slackdump does.
Status: Active development
Everything below runs locally via the ./slackbackup CLI — no Slack workspace admin access
required. Secrets (the xoxc- token and xoxd- cookie) stay on your machine in
~/.slackdump-tokens.json and slackdump's own local session store — never in this repo
(see .gitignore's "Secrets" section).
- slackdump binary installed —
scripts/install-slackdump.shdownloads and checksum-verifies a pinned release to~/bin - Python ≥3.10;
pip install -e ".[test]"from the repo root
-
Log into each Slack workspace in your browser (the web app, not just the login page).
-
Open DevTools Console (F12 → Console). If Chrome blocks pasting, type
allow pastingand press Enter first, then paste:Object.fromEntries( Object.values(JSON.parse(localStorage.localConfig_v2).teams) .map(t => [(t.domain || t.name).toLowerCase(), t.token]) )
This lists every workspace you're logged into in that browser at once — run it once per browser session, not once per workspace.
-
Merge the resulting
{workspace: xoxc-token}object into~/.slackdump-tokens.json(create it if it doesn't exist), e.g.:{ "f3pugetsound": "xoxc-...", "f3kirkland": "xoxc-..." } -
Get the cookie — one cookie works for every workspace under the same account, so this is a one-time step, not once per workspace: DevTools → Application/Storage → Cookies →
https://<any-workspace>.slack.com→ cookie namedd→ copy its Value (starts withxoxd-).
./slackbackup workspace register <workspace> <xoxd-cookie>
./slackbackup workspace list # shows known vs. registered status<workspace> is matched against the tokens file keys after lowercasing and stripping a
leading https:// and trailing .slack.com — f3kirkland, f3kirkland.slack.com, and
https://f3kirkland.slack.com all resolve the same way. Registration calls slackdump workspace import under the hood, which needs both the token (from the file) and the
cookie (passed fresh — slackdump doesn't persist cookies, since they expire) every time
you register or re-register.
Sessions expire. When they do, backups fail with
authentication details expired. The full re-auth workflow — detecting which workspaces are stale, the one-commandscripts/auth-refreshhelper for interactive re-login, andscripts/auth-refresh/keepalive.shfor headless nightly session refresh (already wired intoscripts/nightly-backup-digest.sh) — is documented in docs/OPERATIONS.md §Authorization / Session Lifecycle.
./slackbackup channel list <workspace> # see what's available
./slackbackup channel register <workspace> <#channel-or-id> # adds it to channels.json
./slackbackup channel validate channels.json./slackbackup backup run channels.json ~/slack-backupsPer-channel-month exports (a different, narrower artifact than the digest below) are
also available: ./slackbackup export monthly --from ... --to ... --workspace ... --channel ... --archive-root ~/slack-backups --out ~/slack-exports.
./slackbackup export digest --archive-root ~/slack-backups --workspace 'f3*'
# -> ~/slack-exports/f3-digest-<today>.json
# Defaults: trailing 180 days. --workspace is required unless --jobs is given.
# Override with --days, --as-of, --out.One merged JSON document: all in-range messages with thread nesting, per-channel and per-workspace activity counts, inferred leadership roles (from display names and profile titles), and non-image file/Canvas content.
For repeatable, per-recipient runs, drive the digest from job files instead of flags:
./slackbackup export digest --jobs 'jobs/*.json'. Each gitignored jobs/*.json fully
specifies its own archive root, channels file, workspaces, day window, output path
({as_of}-templated), optional companion user roster, and leadership handler — one digest per
job. This is what the nightly script runs after the blanket digest. See
docs/DESIGN-export.md §Report jobs.
./slackbackup export users --archive-root ~/slack-backups --workspace 'f3*'
# -> ~/slack-exports/f3-user-profiles-<today>.json
# --workspace is required.
# Override with --out.The full per-workspace user roster — display names, profile titles, Slack account roles (admin/owner/bot/etc.) — for every workspace matching the glob, not just recent posters. This is a separate document from the digest and covers the complete cross-region membership picture, including people who haven't posted in the digest window.
Feed both the digest JSON and the user profile JSON, plus
docs/newsletter-prompt.md, to an LLM (e.g. paste all three
into Claude, or use the Claude CLI/API with the files as attachments) to produce the actual
newsletter. The prompt defines the regional structure, event/leadership handling, and sourcing
rules — it expects the digest's messages/channels/leadership.by_region schema and the
user profiles' per-workspace profiles list as-is, so don't reshape the JSON first.
Feed the digest JSON plus docs/fng-getting-started-prompt.md
to an LLM to produce a "Slack: Start Here / FAQ" guide for new members, sourced only from the
digest's actual channels/roles/events — same pattern as the newsletter above.
To read a backed-up channel interactively, point slackdump's built-in viewer at that
channel's archive directory (<archive-root>/<workspace>/<channel>):
slackdump view ~/slack-backups/f3puget/generalThis starts a local, read-only web viewer — threads in a side panel, downloaded images and
files inline, no Slack token or network needed (it reads only the local slackdump.sqlite).
Add DEBUG=1 before the command if a message fails to render. Nothing is modified.
./slackbackup channel-digest run <workspace> '<glob>' <out_dir> archives every channel
matching an fnmatch glob (e.g. 'shuttered-*') and writes a single merge-aware JSON digest of
surviving messages, files, and orphaned Canvases — for pulling content out of channels that
aren't in channels.json (e.g. Canvases stranded by a region migration). On-demand, not part of
the nightly run. See docs/DESIGN-files.md §Channel digest.
Run ./slackbackup help for the full command list.
| Document | Purpose |
|---|---|
| CONTEXT.md | Purpose, capabilities, use cases |
| DESIGN.md | Per-channel backup architecture, modules, key decisions |
| DESIGN-export.md | Export pipeline: monthly per-channel JSON, cross-workspace LLM digest, user-profile roster, report jobs, pluggable leadership handlers |
| DESIGN-files.md | Channel catalog + untracked-channel digest (implemented) + canvas/file harvesting (designed, not yet ported to Python) |
| references/slackdump-cli-notes.md | slackdump CLI behavior, costs, and gotchas learned the hard way — check before re-deriving |
| ADRs | Architecture decision records |
MIT — see LICENSE for details.