sync
Refreshes SQLite from one or both archive sources.
By default, sync runs both live/local sources and does not import the Git snapshot first:
- Discord bot-token sync for bot-visible guild data
- local Discord Desktop cache import for classifiable cached messages and proven DMs
Use update when you want to pull/import the shared Git snapshot. Routine imports upsert changed shards without deleting local cache rows. If you intentionally want a sync run to import the snapshot before live deltas, pass --update=auto for the configured stale update mode (merge by default) or --update=force for an exact replacement. --no-update is accepted as an explicit no-op alias for the default.
Run one explicit --full pass when you want a complete historical guild archive. Use plain sync afterward for frequent latest-message and desktop-cache refreshes.
#Usage
discrawl sync
discrawl sync --update=auto
discrawl sync --update=force
discrawl sync --no-update
discrawl sync --full
discrawl sync --full --all
discrawl sync --guild 123456789012345678
discrawl sync --guilds 123,456 --concurrency 8
discrawl sync --source both # default: bot API + desktop cache
discrawl sync --source discord # bot API only; aliases: key, bot, api
discrawl sync --source wiretap # desktop cache only; aliases: desktop, cache
discrawl sync --guild 123456789012345678 --all-channels
discrawl sync --channels 111,222 --since 2026-03-01T00:00:00Z
discrawl sync --with-embeddings
discrawl sync --with-media
#Sources
| Source | Reads from | Stores |
|---|---|---|
both | Discord bot API and local Discord Desktop cache | bot-visible guild data plus classifiable cached desktop messages |
discord / key | Discord bot API | guilds, channels, threads, members, and messages the bot can access |
wiretap | local Discord Desktop cache files | classifiable cached messages; proven DMs are stored under @me |
#Bot sync modes
| Command | Use when | Behavior |
|---|---|---|
discrawl sync | routine refresh | skips member refreshes, discovers active and newly archived threads, fully indexes new threads, fetches one newest page for other cursorless channels, and otherwise fetches only new messages |
discrawl sync --update=auto | hybrid Git/live refresh | applies the configured stale snapshot update mode first, then runs the routine live refresh |
discrawl sync --update=force | intentional exact reconciliation | replaces public snapshot tables first, then runs the routine live refresh |
discrawl sync --all-channels | repair pass | broad incremental sweep across every stored channel/thread, including archived threads |
discrawl sync --full | historical backfill | crawls older history until channels are complete |
#Flags
--source <both|discord|wiretap>- which archive sources to read--update <auto|force|never>- apply the configured stale snapshot update mode, force an exact replacement, or skip snapshot import before live deltas--full- historical backfill (slow on large guilds)--all-channels- broader incremental sweep across every stored channel/thread--latest-only- explicit latest-only run (also the default for untargetedsync)--all- ignoredefault_guild_idand fan out across every discovered guild--guild <id>/--guilds <id,id>- target specific guilds--channels <id,id>- target specific channels (forum ids expand to threads)--since <RFC3339>- limit initial history and--fullbackfill to messages at or after this timestamp--concurrency <n>- override worker count (default auto-sized: floor 8, cap 32)--skip-members- refresh guild/channel/message data without crawling members--with-members- refresh guild members even during the default latest-only sync; fail if the member crawl cannot complete--with-embeddings- also enqueue changed messages intoembedding_jobs--with-media- after sync, download missing attachment media intocache_dir/media
#Notes
--latest-onlyis the default for untargetedsync. Use--all-channelsto opt out without doing a full historical crawl.--with-mediarecords expired or removed Discord CDN URLs as failed fetches with the HTTP status, commonly404.--with-mediaupdates the local cache only; runpublish --pushafterward to include cached non-DM media in the Git backup as gzip-compressed files.--sincedoes not mark older history as complete, so a latersync --fullwithout--sincecan continue the backfill.- Targeted bot syncs (
--source discord --channels <ids>) refresh Desktop-imported channel placeholders and their parent metadata from Discord before crawling messages. Unavailable metadata or missing parent or permission evidence fails the sync visibly; ordinary cached channels keep their existing behavior. Ancestor metadata refreshes do not crawl ancestor messages or reset history and archive cursors. This is still a message sync:--sincelimits initial history and backfill, not fetching new messages after an existing cursor. - Long runs emit periodic progress logs to stderr.
- Heartbeat logs (
message sync waiting) name the oldest active channel and per-channel page activity if in-flight channels stop completing for a while. - Every run ends with a
message sync finishedsummary. - Each channel crawl has a bounded runtime budget; pathological channels are deferred and retried next sync.
- Guild and archived-thread pagination reports a cursor error if Discord repeats a page instead of continuing indefinitely.
- Full message pages with missing or repeated cursors stop the channel crawl with an error and preserve its last usable backfill checkpoint.
- Retryable failures and unavailable-channel markers are tracked per channel; stale unavailable markers are cleared after a later successful crawl.
- A channel that fails with missing access carries an unavailable marker, and routine syncs pass over it while the marker is inside its seven-day window. The next routine sync after the marker reaches seven days retries the channel. To retry sooner:
sync --guild <id> --fullattempts every channel in the guild immediately, marked ones included, andsync --channels <id>retries a single channel immediately.doctorreportsunavailable_markers_active(inside the window, so currently passed over) andunavailable_markers_expired(due for another attempt). - Full sync retries marked channels even when their older history is complete. When only unavailable channels remain in the cached backlog, it also discovers new channels; the retry and discovery phases do not request the same channel twice.
- Marker cleanup is best-effort, so one missing local sync-state row cannot crash the run.
- Member refresh is best-effort and gives up after five minutes without a caller-supplied deadline. Routine latest-only syncs skip it unless
--with-membersis set. - Routine refreshes keep a per-parent archived-thread cursor, so they discover threads archived between runs without rescanning the historical thread catalog.
- When the archive is already complete,
sync --fullreuses backlog markers and the same incremental thread discovery instead of revisiting every stored archived thread. - Completed channels with no stored messages are verified with a full crawl when Discord still reports a last message. Routine sync skips channels verified empty;
--fullrechecks them. A--sincewindow does not initiate this recovery. - Interrupted recovery resumes from its own saved page checkpoint on the next unwindowed sync, including after cancellation or process termination. Messages ingested in the meantime do not mark the older history complete.
- Failed recovery leaves partial history resumable. After cancellation, restoring a still-empty channel's completion marker uses the existing five-second failure-cleanup budget.