dbxignore applies the Dropbox ignore marker to paths that match a .dropboxignore file. .dropboxignore files use gitignore syntax and can be placed in any folder under a Dropbox root; matching paths are excluded from Dropbox sync. Markers are written using NTFS alternate data streams on Windows and extended attributes on Linux and macOS.
Requirements
Windows 10/11 (NTFS), or a modern Linux distro with a systemd user session, or macOS (Apple Silicon for pre-built binaries; Intel via PyPI)
Dropbox desktop client installed
Python ≥ 3.11 with uv. The Scoop bucket (Windows), Homebrew tap (macOS + Linux), and pre-built binaries (Windows .exe, macOS arm64 Mach-O, Linux x86_64 .tar.gz) are alternatives that don't require Python.
The bucket repo is at kiloscheffer/scoop-dbxignore. Run dbxignore uninstall before scoop uninstall dbxignore so the Task Scheduler entry is removed cleanly.
Install (Windows, from source)
uv tool install git+https://github.com/kiloscheffer/dbxignore
dbxignore install
dbxignore status # verify: daemon running and watching Dropbox
dbxignore install registers a Task Scheduler entry that launches the daemon (pythonw -m dbxignore daemon) at every user logon.
If install fails with "ERROR_CLOUD_FILE_INCOMPATIBLE_HARDLINKS"
Windows users whose AppData is OneDrive-synced (Files On-Demand) can hit:
error: Failed to install: psutil-...whl
Caused by: failed to hardlink file from
C:\Users\<user>\AppData\Roaming\uv\tools\... to
C:\Users\<user>\AppData\Local\uv\cache\...:
The cloud operation cannot be performed on a file with incompatible hardlinks. (os error 396)
uv hardlinks files from its cache into the tool's site-packages by default; the Cloud Files API rejects hardlinks on placeholder files (those backed by cloud storage but not yet fully materialized locally). Force uv to copy instead:
Or set it as a session-wide default before the install:
$env:UV_LINK_MODE = "copy". Either form works for uv tool upgrade too.
Install (Linux)
Requires a systemd user session (standard on Ubuntu, Fedora, Debian, Arch, and most modern distros; WSL2 requires systemd=true in /etc/wsl.conf).
uv tool install git+https://github.com/kiloscheffer/dbxignore
dbxignore install # writes systemd user unit, enables it
dbxignore status # verify: daemon running and watching Dropbox
dbxignore install writes ~/.config/systemd/user/dbxignore.service and runs systemctl --user enable --now so the daemon starts at login. For systemd-level unit state or recent log output, see systemctl --user status dbxignore.service or journalctl --user -u dbxignore.service.
For non-stock Dropbox installs, export DBXIGNORE_ROOT before running dbxignore install
— the install step will read the variable from your shell environment and write a corresponding
Environment="DBXIGNORE_ROOT=..." line into the generated unit's [Service] block. Without this, a shell-exported value won't reach the daemon when systemd launches it. If your Dropbox location ever changes, re-run dbxignore install after updating the export.
To uninstall:
dbxignore uninstall # disables unit, removes the file
dbxignore uninstall --purge # clears markers, state files, logs, systemd drop-in
Linux daemon prerequisites
The daemon uses inotify to watch the Dropbox tree recursively. The kernel caps the number of watches per user (fs.inotify.max_user_watches); on default-config kernels this is often 8192, which a typical Dropbox tree exceeds. The daemon refuses to start (exit code 75) when the limit is hit.
Raise the limit (one-time, persistent across reboots):
echo 'fs.inotify.max_user_watches=524288' | sudo tee /etc/sysctl.d/99-dbxignore.conf
sudo sysctl --system
If the daemon won't start, check journalctl --user -u dbxignore.service for the exact errno (ENOSPC = watch count, EMFILE = instance count) and the sysctl command to run.
Install (macOS)
dbxignore on macOS supports both Dropbox sync modes and auto-detects which one is active:
Legacy mode — Dropbox folder at ~/Dropbox, ignored files marked via the com.dropbox.ignored extended attribute. Synced by Dropbox's own daemon.
File Provider mode — Dropbox folder at ~/Library/CloudStorage/Dropbox/, ignored files marked via the com.apple.fileprovider.ignore#P extended attribute (per Dropbox's docs). Synced by Apple's File Provider extension; default for installs since 2023.
The macOS xattr backend auto-detects sync mode at first use. It reads the configured sync paths from
~/.dropbox/info.json (one entry per Dropbox account) and queries pluginkit for the File Provider extension's user-toggled state. The decision: any path under ~/Library/CloudStorage/ (or under /Volumes/... with the extension allowed) → File Provider mode; extension explicitly disabled → legacy; pluginkit unavailable and no path is decisive → write both attribute names; otherwise → legacy. The result is cached for the rest of the process. dbxignore status echoes the decision on macOS; the daemon also logs it at startup. The daemon registers as a launchd User Agent in either case.
Verify your sync mode manually
dbxignore status echoes a sync mode: line on macOS. To query Apple's PluginKit registry directly:
pluginkit -m -A -i com.getdropbox.dropbox.fileprovider
The prefix character indicates the user-toggled state: leading whitespace = registered, untoggled (default);
+ = explicitly enabled; - = explicitly disabled. No matching line means the extension isn't registered.
Install:
pip install dbxignore # or: uv tool install dbxignore
dbxignore install # writes ~/Library/LaunchAgents/com.kiloscheffer.dbxignore.plist
# and bootstraps it into your GUI session
dbxignore status # verify: daemon running and watching Dropbox
dbxignore install requires that you've logged into the macOS GUI at least once since the last reboot — the GUI domain that LaunchAgents bootstrap into isn't initialized until a graphical login. SSH-on-fresh-boot installs fail with Bootstrap failed: 5: Input/output error. Log into the GUI, then retry.
Pre-built binaries (arm64 only)
Pre-built Mach-O binaries are arm64 (Apple Silicon). Intel Mac users: install via PyPI — the wheel is universal Python.
The binary is unsigned — Gatekeeper refuses it on first launch with "cannot be opened because it is from an unidentified developer." Either right-click → Open in Finder (macOS remembers the override), or strip the quarantine xattr explicitly:
dbxignore uninstall # bootouts the agent, removes the plist
dbxignore uninstall --purge # also clears markers, state files, logs
Files written:
~/Library/LaunchAgents/com.kiloscheffer.dbxignore.plist # launchd unit
~/Library/Application Support/dbxignore/state.json # daemon state
~/Library/Logs/dbxignore/daemon.log # daemon log (rotated)
~/Library/Logs/dbxignore/launchd.log # launchd-captured stdout/stderr
Install (Homebrew)
brew tap kiloscheffer/dbxignore
brew install dbxignore
dbxignore install # registers launchd LaunchAgent (macOS) or systemd user unit (Linux)
dbxignore status # verify: daemon running and watching Dropbox
The tap repo is at kiloscheffer/homebrew-dbxignore. Supports macOS arm64 (Apple Silicon) and Linux x86_64. Run dbxignore uninstall before brew uninstall dbxignore so the service is removed cleanly.
Install (.exe)
Download both dbxignore.exe and dbxignorew.exe from the latest Release. They ship together — dbxignore.exe is the CLI you run from a terminal, dbxignorew.exe is the GUI helper that Task Scheduler invokes for the daemon and that the Explorer right-click verbs target. All commands you type are dbxignore; you do not invoke dbxignorew.exe directly.
Place both files in a stable directory (e.g. %LOCALAPPDATA%\dbxignore\bin\) and add that directory to your PATH.
Run dbxignore install.
Windows Explorer integration
On Windows, dbxignore install registers two right-click verbs in Explorer:
Ignore from Dropbox — shows a MessageBox confirmation dialog (yellow warning triangle, Yes/No buttons) before running dbxignore.exe ignore <path>. Marking a path ignored causes Dropbox to delete it from the cloud and propagate the deletion to every linked device, so a confirmation is required.
Restore to Dropbox — one-click; runs dbxignore.exe unignore --yes <path>. Safe direction (Dropbox re-syncs the path); no confirmation dialog.
The verbs only appear under discovered Dropbox roots — the AppliesTo filter is generated at install time from ~/.dropbox/info.json. To skip the registry write, pass --no-shell-integration to install (also accepted on Linux/macOS as a no-op for portable scripts). To preserve the verbs across a daemon reinstall, pass --no-shell-integration to uninstall. uninstall --purge always removes them.
If you move your Dropbox folder, re-run dbxignore install to refresh the AppliesTo filter.
Platform support
Platform
Marker mechanism
Daemon mechanism
Windows 10 / 11
NTFS Alternate Data Streams
Task Scheduler (user task)
Linux (Ubuntu 22.04 / 24.04 + most modern distros with systemd user session)
user.com.dropbox.ignored xattr
systemd user unit
macOS (Apple Silicon; Intel via PyPI)
com.dropbox.ignored xattr (legacy mode) or com.apple.fileprovider.ignore#P (File Provider mode — default since 2023; auto-detected)
launchd User Agent
.dropboxignore syntax
Full .gitignore syntax via pathspec. Matching is case-insensitive to accommodate NTFS. A file named .dropboxignore is never itself ignored — it needs to sync so your other machines see the same rules.
Example (put in a project root):
# everything javascripty
node_modules/
# Python
__pycache__/
.venv/
*.egg-info/
# Rust
target/
# build output
/dist/*
/build/
# except this one specific artifact we want to share
!dist/release-notes.pdf
Commands
Command
Purpose
dbxignore init [PATH]
Scaffold a starter .dropboxignore in PATH (or cwd) with a template covering Node.js / Python / Rust / JVM / .NET / frontend frameworks / build outputs / OS detritus. Walks the tree to depth 3 and annotates the header with which marker-bait dirs were detected. See First-time setup.
dbxignore install / uninstall
Register / remove the daemon with the platform's user-scoped service manager (Task Scheduler on Windows, systemd user unit on Linux, launchd LaunchAgent on macOS). uninstall --purge also clears every existing marker, removes local dbxignore state (state.json, daemon.log*, the state directory; on macOS also ~/Library/Logs/dbxignore/), and on Linux removes any systemd drop-in directory. Any stray marker on a .dropboxignore file itself is logged at WARNING before being cleared.
dbxignore daemon
Run the watcher + hourly sweep in the foreground. Usually invoked by the platform's service manager (Task Scheduler on Windows, systemd on Linux, launchd on macOS).
dbxignore apply [PATH]
One-shot reconcile of the whole Dropbox (or a subtree). Pass --from-gitignore <path> to load rules from a .gitignore instead of .dropboxignore files in the tree. Pass --dry-run to preview what would be marked/cleared without changing anything. Prompts before mutating any marker; pass --yes to skip — see Applying rules.
dbxignore generate <PATH>
Translate a .gitignore (or any nominated file) to a .dropboxignore. <PATH> is a file or a directory; default output is <dir>/.dropboxignore. Flags: -o <path>, --stdout, --force.
dbxignore status
Is the daemon running? Last sweep counts, last error. Pass --summary for a stable single-line summary suitable for status-bar widgets — see Status-bar integration.
dbxignore clear [PATH]
Clear every ignore marker under the watched roots (or under PATH). Inverse of apply. Leaves .dropboxignore files and state.json untouched — see Clearing all markers.
dbxignore ignore <path>
Append a literal-path rule to the nearest ancestor .dropboxignore and set the ignore marker on <path>.
dbxignore unignore <path>
Remove the rule and clear the marker. Refuses if <path> is also matched by a wildcard rule.
dbxignore list [PATH]
Print every path currently bearing the ignore marker.
dbxignore explain PATH
Which .dropboxignore rule (if any) matches the path?
First-time setup
dbxignore init [PATH] writes a starter .dropboxignore into PATH (or the current directory). The packaged template covers common dev artifacts across ecosystems — Node.js (node_modules, npm/yarn/pnpm caches and logs), Python (virtualenvs, bytecode, tool caches), Rust (target/), JVM (.gradle/), .NET (bin/, obj/), frontend frameworks (.next/, .nuxt/, .svelte-kit/, .turbo/, etc.), generic build/dist outputs, and OS detritus (.DS_Store, Thumbs.db, vim swap files).
The header of the generated file lists which marker-bait directories were detected in your tree at depth ≤ 3 (e.g., # Detected in this tree at depth <= 3: node_modules, __pycache__). All template patterns are emitted as active rules; the header is the cue for which ones are immediately load-bearing. Edit the file afterward to remove patterns that don't apply to your tree.
Applying rules
dbxignore apply runs one reconcile pass — the same operation the daemon performs on every .dropboxignore save and on its hourly recovery sweep. Useful for forcing a one-shot run without waiting for the daemon (or when no daemon is installed).
dbxignore apply --dry-run # preview what would be marked/cleared
dbxignore apply --yes # skip the confirmation prompt
dbxignore apply ~/Dropbox/proj # scope to a subtree
dbxignore apply --from-gitignore ~/Dropbox/proj/.gitignore --yes
A confirmation prompt fires by default and summarizes how many paths will be marked or cleared. Both directions are destructive — see Behaviour for what marker mutations do to cloud sync. Pass --yes for scripted use.
If apply finds nothing to mark and nothing to clear (the steady-state case where the daemon has already converged the tree), it exits with Nothing to apply (rules already in sync). and skips the prompt.
Unlike clear, apply does not refuse to run while the daemon is alive — the daemon performs the same operation continuously, so racing it is normal usage.
Clearing all markers
dbxignore clear walks the watched roots and clears every ignore marker, the inverse of apply. Useful for staging a manual sync change or testing that Dropbox re-syncs previously-ignored content from the cloud. Unlike uninstall --purge, it leaves .dropboxignore rule files and state.json untouched.
dbxignore clear --dry-run # preview what would be cleared
dbxignore clear --yes # skip the confirmation prompt
dbxignore clear ~/Dropbox/proj # scope to a subtree
dbxignore clear --force --yes # override daemon-alive guard
clear refuses to run when the daemon is alive — the daemon's next sweep would re-apply rule-driven markers within seconds (rule-reload events) or within the hour (recovery sweep tick). Stop the daemon first (dbxignore uninstall) or pass --force for known short-window tests where you'll restart the daemon yourself.
A confirmation prompt fires by default. After the clear, Dropbox starts syncing previously-ignored paths — for a
node_modules previously kept out of sync, that's potentially gigabytes of upload, so the prompt is a footgun guard. Pass --yes for scripted use.
Status-bar integration
dbxignore status --summary emits a stable single-line summary on stdout, suitable for status-bar widgets (polybar, tmux, i3blocks, sketchybar) and cron-friendly polling.
The format is part of the public API per SemVer: adding new fields is non-breaking, but renaming or removing existing fields bumps MINOR pre-1.0 / MAJOR post-1.0.
running — state.json present and the recorded PID corresponds to a live dbxignore daemon process.
not_running — state.json present but the recorded PID is no longer a live daemon (cleanly stopped, or stale state).
no_state — no state.json (daemon never ran). Only state and conflicts are emitted in this case.
starting — daemon is alive but the initial sweep has not yet completed.
state=starting is emitted when the daemon is alive but the initial sweep has not yet completed. During this window, the summary contains only state and pid — marked, cleared, errors, and conflicts are omitted because they would all be 0 and would falsely imply the daemon swept and found nothing. The transition to state=running happens when the initial sweep completes (a fresh install of a 27,000-directory Dropbox tree took ~50s in testing).
pid=N is omitted when no PID was recorded (rare partial-write case). The remaining fields (marked, cleared, errors) are present whenever a state.json exists, even if the daemon never finished a sweep — they default to zero.
A polybar module reading the daemon state could grep state=\S+ for the at-a-glance indicator and errors=\S+ for an error-count badge.
Command parity with git
For users coming from git, this table maps each dbxignore command to its closest git counterpart. Some align cleanly; others have a deceptively-similar git verb with materially different consequences.
dbxignore
git counterpart
Notes
apply
(none)
Reconciles markers from .dropboxignore.
check-ignore
git check-ignore -v
Alias of explain. --quiet matches git's flag.
clear
(see callout below)
NOTgit rm --cached-shaped.
daemon
(none)
dbxignore-specific watcher + hourly sweep.
explain
git check-ignore -v
Same diagnostic question; --quiet and exit codes match.
generate
(none)
Translates a .gitignore into a .dropboxignore.
ignore
(none)
Append a path-anchored rule and set the marker in one step.
init
git init (loosely)
Scaffolds a starter .dropboxignore, not a repository.
install
(none)
Registers the daemon with the platform service manager.
list
(none)
Lists every path currently bearing the Dropbox ignore marker.
status
git status (loosely)
Shows daemon state, last sweep, marker counts, conflicts.
unignore
(see callout below)
Inverse of ignore; refuses on wildcard-rule collision.
uninstall
(none)
Removes the daemon registration; --purge also clears markers.
Behaviour
What "ignored" means in Dropbox. Setting the ignore marker on a file or folder removes it from your cloud Dropbox and from every other linked device. The local copy on the device that set the marker is preserved. Removing the marker (by deleting the matching rule, or running dbxignore clear) restores the path to sync — the local copy is uploaded back to Dropbox and propagated to other devices. So .dropboxignore is not a .gitignore-style "leave this file untracked here" rule; it's an instruction to delete the path from cloud sync, with the local copy as the only surviving copy until the marker is cleared.
Source of truth..dropboxignore files declare what is ignored. Removing a rule unignores the matching paths on the next reconcile. A path marked ignored via Dropbox's right-click menu but not matching any rule will be unignored.
Hybrid trigger. The daemon reacts to filesystem events in real time and runs an hourly safety-net sweep. If the daemon is offline, an initial sweep at the next start catches any drift.
Multi-root. Personal and Business Dropbox roots are discovered automatically from %APPDATA%\Dropbox\info.json (with %LOCALAPPDATA%\Dropbox\info.json as a fallback for "install for all users") on Windows, and from ~/.dropbox/info.json on Linux and macOS.
Negations and Dropbox's ignore inheritance
Dropbox marks files and folders as ignored using xattrs. When a folder carries the ignore marker, Dropbox does not sync that folder or anything inside it — children inherit the ignored state regardless of whether they individually carry the marker. This matters for gitignore-style negation rules in your .dropboxignore.
A negation can only re-include a path if no strict ancestor directory of that path is marked ignored. The case dbxignore drops is when an earlier rule marks a directory and a later negation tries to re-include something inside that directory:
build/ # marks the directory build/ itself
!build/keep/ # ← dropped: build/ is already ignored, inheritance wins
dbxignore detects this at the moment you save the .dropboxignore, logs a WARNING naming both rules, and drops the conflicted negation from the active rule set.
The git-canonical pattern works because it marks only the children of build/, not build/ itself:
build/* # marks immediate children
!build/keep/ # except this one
!build/keep/** # re-include everything under it
If you wrote build/ only to except a child, switch the trailing / to /* — the two forms differ (build/ marks the directory itself; build/* does not), so only switch when the negation is the load-bearing reason for the rule.
Other negations that don't conflict with an ignored ancestor work normally. For example:
*.log
!important.log
Here nothing marks a parent directory as ignored (*.log matches files, not dirs), so the negation works — important.log gets synced, the other .log files don't.
Using .gitignore rules
A .gitignore and a .dropboxignore use the same pattern grammar (the same pathspec parser handles both). Two CLI verbs let you reuse .gitignore rules without hand-copying.
dbxignore generate <path> writes a .dropboxignore derived byte-for-byte from a source file. <path> may be a file or a directory; if a directory, .gitignore inside it is the source.
The destination path is <dir>/.dropboxignore by default; use -o <path> to redirect. Without --force, an existing .dropboxignore at the target is left in place and the command exits non-zero.
dbxignore apply --from-gitignore <path> runs a one-shot reconcile using rules loaded from <path> (without writing a .dropboxignore). Rules are mounted at dirname(<path>), which must be under a discovered Dropbox root. Existing .dropboxignore
files in the tree do not participate in this run.
The --yes flag skips the confirmation prompt; without it apply previews the change set and asks before mutating any marker. See Applying rules for the prompt's exact wording.
Semantic divergence between the two files
A .gitignore says "git doesn't track this file." A .dropboxignore marker tells Dropbox to stop syncing the path and remove it from cloud sync. Most rules transfer cleanly (build outputs, dependency caches, IDE state) — but transplanting a .gitignore verbatim can mark files for cloud removal that you didn't intend to remove. Review the source file before running apply --from-gitignore, or run generate --stdout to preview.
Interaction with the running daemon
If dbxignore daemon is running, writing a .dropboxignore (whether by generate, by hand, or by any other means) triggers a watchdog event. The daemon classifies it as a RULES event, debounces, and reconciles the affected root. End state: the markers are written and Dropbox starts removing matched paths from cloud sync. generate is therefore not a "preview-only" verb when the daemon is running — use --stdout to preview without committing the file.
Negations
A pattern like !build/keep/ (re-include a path under an ignored ancestor) is dropped silently; Dropbox's ignored-folder model does not support negation through ignored ancestors. Use dbxignore explain <path> to see which rule masked a dropped negation.
Configuration
Environment variables read at daemon startup:
Variable
Default
Purpose
DBXIGNORE_DEBOUNCE_RULES_MS
100
Debounce window for .dropboxignore file events.
DBXIGNORE_DEBOUNCE_DIRS_MS
0
Debounce for directory-creation events (0 = react immediately, no coalescing).
DBXIGNORE_DEBOUNCE_OTHER_MS
500
Debounce for other file events.
DBXIGNORE_LOG_LEVEL
INFO
Daemon log level. Accepts DEBUG, INFO, WARNING, ERROR, CRITICAL (case-insensitive). Unknown values fall back to INFO. Affects dbxignore daemon only — CLI commands use the top-level -v / -vv counted flag (default WARNING; -v INFO; -vv DEBUG). See Log levels below for what each level surfaces.
DBXIGNORE_ROOT
(unset)
Escape hatch for non-stock Dropbox installs: overrides info.json discovery and treats the given absolute path as the sole Dropbox root. If the path doesn't exist, a WARNING is logged and no roots are returned (so dbxignore apply exits with "No Dropbox roots found").
Log levels
The daemon and CLI have separate log-config knobs:
Daemon (dbxignore daemon) reads DBXIGNORE_LOG_LEVEL from the environment at startup. Output goes to the rotating file (and stderr on Linux for journald).
CLI commands (apply, list, status, explain, install, uninstall) use the top-level -v / -vv counted flag: default WARNING (intentional click.echo summaries only); -v INFO (also surfaces install-backend chatter and similar operator-level diagnostics); -vv DEBUG. The env var is not consulted here. Output goes to stderr.
What each level surfaces:
Level
What you see
DEBUG
Per-operation traces — individual marker reads/writes, watchdog event payloads, debouncer ticks, "xattr absent" / "path gone" race-condition skips on clear_ignored. Useful when debugging a specific reconcile decision or a marker-API edge case.
INFO (daemon default)
Daemon start/stop banners, sweep summaries (paths marked / cleared per sweep), install/uninstall confirmations, environment-forwarding diagnostics. The "what's the daemon doing right now" baseline.
WARNING (CLI default)
Recoverable conditions — filesystems that don't support markers (ENOTSUP/EOPNOTSUPP), missing info.json, dropped negations under ignored ancestors, symlink EPERM on Linux, schtasks /Run failure post-install, corrupt or shape-mismatched state.json. None of these stop the daemon.
ERROR
Conditions that prevent progress on a specific concern — "No Dropbox roots discovered; exiting", sweep-startup failures, watchdog or debouncer handler crashes (with traceback). The daemon either continues with reduced scope or shuts down cleanly.
CRITICAL
Accepted by the env var but no production code path emits at this level — the project tops out at ERROR.
Ad-hoc debugging — bump the daemon's verbosity for one run:
# Linux / macOS
systemctl --user stop dbxignore.service # Linux: stop the running daemon
launchctl bootout gui/$(id -u)/com.kiloscheffer.dbxignore # macOS: same idea
DBXIGNORE_LOG_LEVEL=DEBUG dbxignore daemon # foreground; output streams to terminal
# Windows
schtasks /End /TN dbxignore # stop the running task instance
$env:DBXIGNORE_LOG_LEVEL = "DEBUG"
dbxignore daemon # foreground in this shell
Re-enable the managed daemon (systemctl --user start dbxignore.service, launchctl bootstrap, or wait for next logon on Windows) when you're done.
CLI-side debugging — pass --verbose to any command:
Persisting a non-default level across managed-daemon restarts requires a platform-specific override and is not covered here — it's rarely the right move (DEBUG floods the daemon log fast). For one-off investigations, the foreground-run pattern above is the recommended path.
Log and state file locations
Logs (rotated, 25 MB total):
Windows — %LOCALAPPDATA%\dbxignore\daemon.log.
Linux — two sinks, same records. The rotating file at $XDG_STATE_HOME/dbxignore/daemon.log (fallback ~/.local/state/dbxignore/daemon.log) is authoritative for offline debugging and bug-report bundling; journalctl --user -u dbxignore.service surfaces the same records via systemd-journald for live tailing and cross-service filtering.
macOS — ~/Library/Logs/dbxignore/daemon.log (rotated). ~/Library/Logs/dbxignore/launchd.log captures launchd-time stdout/stderr (near-empty unless the daemon crashes during startup before its own log handler initializes).
State:
Windows — %LOCALAPPDATA%\dbxignore\state.json.
Linux — $XDG_STATE_HOME/dbxignore/state.json (fallback ~/.local/state/dbxignore/state.json).
macOS — ~/Library/Application Support/dbxignore/state.json (split from the log dir to match Apple's app-data conventions).
Backlog
Open items and planned work are tracked in BACKLOG.md.