Skip to main content

dotbrowser

CI PyPI Python

Manage your browser as a dotfile. Keep shortcuts and UI tweaks in a single TOML, apply with one command, sync across machines — no browser cloud sync required.

Status: alpha. Brave + Vivaldi + Edge + Chrome on Linux, macOS, and Windows — keyboard shortcuts (Brave / Vivaldi), general settings (vertical tabs, sidebar, NTP & toolbar declutter, …), and force-installed PWAs. Edge and Chrome support [settings] and [pwa] only — neither exposes customizable accelerators in Preferences. MAC integrity is checked across both Preferences and Secure Preferences so Chrome's split tracked-pref storage is honored. A shared _base/ module makes adding more Chromium-based browsers a ~150-line job.

Quick start

The repo ships opinionated examples under examples/brave/, examples/vivaldi/, examples/edge/, and examples/chrome/: vertical tabs collapsed to icons, decluttered new tab page, stripped-down toolbar, vim-style hjkl shortcuts. Each all.toml bundles all available namespaces; shortcuts.toml, settings.toml, and pwa.toml are single-namespace variants.

Brave with the minimal config — empty new tab page, vertical tabs collapsed to icons, decluttered toolbar

Scaffold a starter config from scratch:

dotbrowser brave init                        # write commented template to stdout
dotbrowser brave init -o brave.toml          # ...or to a file
dotbrowser vivaldi init -o vivaldi.toml
dotbrowser edge init -o edge.toml
dotbrowser chrome init -o chrome.toml

Or apply an example directly from GitHub — no clone, no install. Fetched payloads are echoed with byte size + SHA-256 so you can see exactly what's being applied:

uvx dotbrowser brave apply --dry-run \
  https://raw.githubusercontent.com/xom11/dotbrowser/main/examples/brave/all.toml

# Apply. If Brave is already running, dotbrowser uses live apply;
# first-time live setup closes Brave normally and relaunches it once.
uvx dotbrowser brave apply \
  https://raw.githubusercontent.com/xom11/dotbrowser/main/examples/brave/all.toml

Prefer to inspect / customise locally first? Download then apply:

curl -fsSL -o brave.toml https://raw.githubusercontent.com/xom11/dotbrowser/main/examples/brave/all.toml
# edit brave.toml ...
uvx dotbrowser brave apply brave.toml

The Vivaldi (vivaldi/all.toml), Edge (edge/all.toml), and Chrome (chrome/all.toml) variants follow the same shape. Anything you later remove from your config reverts to the browser's default on the next apply — no orphan entries.

Install

pipx install dotbrowser     # global, isolated venv
uvx dotbrowser <args>       # run on demand, no install step
pip install dotbrowser      # into the active environment

Pin a version: pipx install "dotbrowser>=0.3,<0.4". Run a specific tag: uvx --from 'dotbrowser==0.3.0' dotbrowser <args>. Run from a branch: uvx --from git+https://github.com/xom11/dotbrowser dotbrowser <args>. Local dev: pip install -e ".[test]".

Build your own config

A single TOML carries [shortcuts], [settings] and [pwa]. One apply writes all three in a single backup + write cycle.

# brave.toml (Vivaldi has the same shape; Edge skips [shortcuts])
[shortcuts]
toggle_sidebar = ["Control+Shift+KeyE"]
toggle_ai_chat = ["Alt+KeyA"]

# vim-style hjkl
back                = ["Alt+KeyH"]
forward             = ["Alt+KeyL"]
select_previous_tab = ["Alt+KeyJ"]
select_next_tab     = ["Alt+KeyK"]

# Same chord on all OSes — Meta+ = Cmd on macOS, Super on Linux/Windows (auto-translated)
new_tab   = ["Control+KeyT", "Meta+KeyT"]
close_tab = ["Control+KeyW", "Meta+KeyW"]

[settings]
"brave.tabs.vertical_tabs_enabled"   = true
"brave.tabs.vertical_tabs_collapsed" = true
"bookmark_bar.show_on_all_tabs"      = false

[pwa]
# Force-installed Progressive Web Apps. The browser fetches each manifest,
# downloads icons, registers the app in chrome://apps, and emits a
# launcher (.desktop on Linux, app shim on macOS, Start Menu shortcut on
# Windows). Removing a URL + re-applying = uninstall.
urls = [
  "https://squoosh.app/",
  "https://app.element.io/",
]
dotbrowser brave   apply brave.toml --dry-run    # preview the diff
dotbrowser brave   apply brave.toml              # live apply if Brave is running
dotbrowser vivaldi apply vivaldi.toml            # live apply if Vivaldi is running
dotbrowser edge    apply edge.toml               # live apply with automatic fallback
dotbrowser chrome  apply chrome.toml             # live apply with automatic fallback
  • Shortcut keys (Brave): Chromium KeyEvent codes joined by + — Control+Shift+KeyP, Alt+Digit1, F11. Meta+ is auto-translated to Command+ on macOS.
  • Shortcut keys (Vivaldi): lowercase, tokenized with + — meta+t, ctrl+shift+e. Vivaldi uses the same meta+ spelling on every platform (cmd on macOS, win/super on Linux/Windows), so no platform translation is needed.
  • Edge / Chrome have no [shortcuts]: neither browser exposes a Preferences key for accelerators. Use [settings] and [pwa] instead.
  • Setting keys: dotted paths into the profile Preferences JSON. MAC-protected keys (homepage, default search engine, pinned_tabs, …) are refused with a clear error.
  • PWA URLs: every entry installs with default_launch_container = "window" (standalone PWA window) and create_desktop_shortcut = true. [pwa] is the only namespace that needs elevated privileges: it writes a managed-policy file (sudo on Linux/macOS) or the Windows Registry (administrator). If your config has no [pwa] table or no diff to apply, no elevation prompt happens.
  • Empty [settings] header (no entries) wipes everything dotbrowser previously managed in that namespace. Missing header = skip the namespace entirely. Same rule applies to [shortcuts] and [pwa].

CLI reference

Shape: dotbrowser <browser> [browser-flags] <action> [action-flags] [args]. The same actions work for brave, vivaldi, edge, and chrome (Edge and Chrome omit the shortcuts action). export is the inverse of apply: produce a round-trippable TOML from the current profile state.

Browser-level flags

These apply to every action under a browser and go before the action name.

Flag Default What it does
-r, --profile-root PATH see table below Browser's root profile directory. Auto-detected on Linux / macOS / Windows.
-p, --profile NAME Default Profile directory name inside the root — e.g. "Profile 1", Default.
--channel {stable,beta,nightly} stable Brave only. Selects the release channel; auto-detects the corresponding Brave-Browser-Beta / Brave-Browser-Nightly profile path.
Browser Linux macOS Windows
brave ~/.config/BraveSoftware/Brave-Browser ~/Library/Application Support/BraveSoftware/Brave-Browser %LOCALAPPDATA%\BraveSoftware\Brave-Browser\User Data
vivaldi ~/.config/vivaldi ~/Library/Application Support/Vivaldi %LOCALAPPDATA%\Vivaldi\User Data
edge ~/.config/microsoft-edge ~/Library/Application Support/Microsoft Edge %LOCALAPPDATA%\Microsoft\Edge\User Data
chrome ~/.config/google-chrome ~/Library/Application Support/Google/Chrome %LOCALAPPDATA%\Google\Chrome\User Data

For Brave Beta / Nightly the same paths apply with a -Beta / -Nightly suffix on the Brave-Browser directory (Snap and Flatpak only ship stable, so non-stable channels skip those probes).

# Apply on a non-default profile, with an alternate root.
dotbrowser brave -r /custom/path -p "Profile 1" apply brave.toml

# Apply against Brave Beta (auto-detects ~/.config/BraveSoftware/Brave-Browser-Beta).
dotbrowser brave --channel beta apply brave.toml

init — scaffold a starter TOML

Writes a commented template with example shortcuts (Brave / Vivaldi only), settings, and a commented-out [pwa] block. Refuses to overwrite an existing file.

Flag What it does
-o, --output FILE Write to FILE instead of stdout.
dotbrowser brave   init -o brave.toml
dotbrowser vivaldi init -o vivaldi.toml
dotbrowser edge    init -o edge.toml
dotbrowser chrome  init -o chrome.toml

apply <config> — write [shortcuts] + [settings] + [pwa]

<config> is a local TOML file path or an http:///https:// URL. URLs are fetched in-memory; the URL, byte size, and SHA-256 are printed before the diff so you can verify exactly what's about to be applied. If the config contains a non-empty [pwa] table, dotbrowser runs a privilege preflight (sudo on Linux/macOS, admin check on Windows) before closing or relaunching the browser, so an auth failure cannot interrupt the current session.

Flag What it does
-n, --dry-run Compute + print the diff. Do not back up, write, or touch state files.
dotbrowser brave   apply brave.toml --dry-run
dotbrowser brave   apply brave.toml
dotbrowser vivaldi apply vivaldi.toml
dotbrowser edge    apply edge.toml
dotbrowser chrome  apply chrome.toml

For every browser, plain apply automatically attempts a live update when the browser is running. dotbrowser privately creates or reuses a loopback DevTools endpoint when needed. Settings exposed by the browser UI apply in the existing process; unsupported settings and resets trigger a normal close, verified offline apply, and relaunch. There is no force-kill workflow or manual endpoint option.

shortcuts dump — emit current shortcuts as TOML (Brave + Vivaldi)

By default, only user-customised bindings are emitted (a useful starting point for your own config).

Flag What it does
-a, --all Dump every binding, including the browser's compiled-in defaults.
-o, --output FILE Write to FILE instead of stdout.
dotbrowser brave   shortcuts dump
dotbrowser vivaldi shortcuts dump -a -o all-binds.toml

shortcuts list [filter] — search known command names (Brave + Vivaldi)

Lists every command id you can bind to. The optional positional filter is a substring match.

dotbrowser brave   shortcuts list toggle    # everything containing "toggle"
dotbrowser vivaldi shortcuts list           # full list

For a fresh Vivaldi profile, dotbrowser reads the platform-specific vivaldi.actions defaults from Vivaldi's installed prefs_definitions.json, so shortcuts list and apply work before you have changed a keyboard shortcut in the UI. If that installed schema cannot be found, apply prints the manual Keyboard Settings seeding fallback.

settings dump [keys ...] — inspect setting values

  • No keys → dumps every setting dotbrowser is currently managing on this profile.
  • Explicit keys → dumps those dotted paths. Missing keys appear as commented-out lines so you know dotbrowser looked.
Flag What it does
-o, --output FILE Write to FILE instead of stdout.
dotbrowser brave  settings dump
dotbrowser edge   settings dump browser.show_home_button bookmark_bar.show_on_all_tabs
dotbrowser chrome settings dump bookmark_bar.show_on_all_tabs

settings blocked — list MAC-protected keys apply will refuse

Walks protection.macs in your profile and prints every tracked pref path as commented TOML, with the current value when present. Use this to learn upfront which keys (e.g. homepage, default_search_provider_data.template_url_data, session.startup_urls) you need to set via the browser UI instead of the config.

Flag What it does
-o, --output FILE Write to FILE instead of stdout.
dotbrowser brave   settings blocked
dotbrowser vivaldi settings blocked
dotbrowser edge    settings blocked
dotbrowser chrome  settings blocked

pwa dump — emit currently-managed PWA URLs as TOML

Reads the managed-policy source for that browser (Linux: /etc/<browser>/policies/managed/dotbrowser-pwa.json; macOS: /Library/Managed Preferences/<bundle-id>.plist; Windows: HKLM\Software\Policies\<vendor>\<browser>\WebAppInstallForceList) and prints a [pwa] table you can paste straight into your config. All sources are readable without elevation.

Flag What it does
-o, --output FILE Write to FILE instead of stdout.
dotbrowser brave   pwa dump
dotbrowser vivaldi pwa dump
dotbrowser edge    pwa dump
dotbrowser chrome  pwa dump

export — capture every user-customised namespace into one round-trippable TOML

Bootstrap your dotfile from a profile you've already tweaked by hand: export reads the live profile + managed-policy file and emits [shortcuts] (Brave/Vivaldi only) plus [pwa] in a single TOML document. Pipe it back into apply to migrate to another machine.

  • Brave [shortcuts] lists only bindings that differ from the browser's compiled-in defaults (brave.default_accelerators). Pass --all-shortcuts to lift the diff filter and emit every binding.
  • Vivaldi has no defaults mirror, so its [shortcuts] lists every command with a non-empty binding (still includes Vivaldi's compiled-in defaults). The export header documents this limitation inline.
  • Edge / Chrome skip [shortcuts] entirely (neither browser has the namespace).
  • [pwa] always reflects the current managed-policy source for that browser.
  • [settings] is intentionally absent: Chromium does not expose a defaults table for arbitrary prefs, so diff vs default is not computable. Use settings dump <key>... for specific keys you already know about, or settings blocked to learn which MAC-protected keys apply would refuse.
Flag What it does
-o, --output FILE Write to FILE instead of stdout.
-a, --all-shortcuts (Brave/Vivaldi) include every shortcut binding, not just user-customised ones.
dotbrowser brave   export -o brave.toml         # full snapshot (no settings)
dotbrowser vivaldi export                       # to stdout
dotbrowser edge    export -o edge.toml          # [pwa] only
dotbrowser chrome  export -o chrome.toml        # [pwa] only

How it works

When the target browser is closed, dotbrowser patches the profile Preferences JSON directly. Each offline apply takes one timestamped backup, writes atomically (temp file + rename), and verifies the result by reloading. Edge and Chrome also use this path whenever a requested setting is not exposed through their running Settings UI.

Plain apply uses each browser's privileged UI APIs whenever possible: ordinary Brave, Chrome, and Edge settings go through chrome.settingsPrivate; Brave New Tab settings such as ntp.shortcust_visible go through live New Tab UI actions; Brave shortcuts go through its Settings CommandsService; and Vivaldi settings/shortcuts go through vivaldi.prefs. Vivaldi shortcut changes reload the internal Vivaldi UI page so new accelerators are registered. Supported changes take effect with the current process open. A browser not yet carrying dotbrowser's internal endpoint closes normally and relaunches once; a setting without a live route uses the same normal-close fallback for a verified offline write. Live apply writes .dotbrowser.live.json so later apply runs can find the endpoint, and it still writes namespace sidecar files so the next apply knows what it manages. Removing/resetting [settings] keys is handled through automatic normal-close offline fallback.

[shortcuts] and [settings] track managed entries per namespace in sidecar files (Preferences.dotbrowser.{shortcuts,settings}.json), so removing a key from your config restores the browser's default on the next offline apply. [pwa] is different: its state lives in Chromium's managed-policy storage (Linux JSON file, macOS plist, Windows Registry) — the policy is the state, no sidecar. Chromium marks the PWA force-install policy as dynamically refreshable, but Windows still requires Administrator to write HKLM. Removing a URL from [pwa] and re-applying triggers an uninstall through the same policy mechanism.

On macOS, /Library/Managed Preferences/ is a system-managed cache that gets reclaimed at boot on non-MDM machines, which would otherwise uninstall your PWAs. To keep them installed, apply installs a small root-owned self-healing helper:

  • /Library/LaunchDaemons/org.dotbrowser.<bundle>.pwa.plist — watches the managed-preferences directory and rewrites the policy if macOS removes it.
  • /Library/Application Support/dotbrowser/<bundle>.managed.plist and <bundle>.heal.sh — the policy source of truth and the rewrite script.

Applying an empty [pwa] table (urls = []) removes all of the above. To remove it manually:

sudo launchctl bootout system /Library/LaunchDaemons/org.dotbrowser.<bundle>.pwa.plist
sudo rm /Library/LaunchDaemons/org.dotbrowser.<bundle>.pwa.plist
sudo rm "/Library/Application Support/dotbrowser/<bundle>.managed.plist" \
        "/Library/Application Support/dotbrowser/<bundle>.heal.sh"

where <bundle> is e.g. com.vivaldi.Vivaldi.

Architecture

All Chromium-based browsers share src/dotbrowser/_base/: orchestrator, settings logic (with MAC-protected refusal), PWA logic (sudo / registry write paths), and the platform-aware BrowserProcess class. Each browser module (brave/, vivaldi/, edge/, chrome/) is a thin wrapper that configures _base/ with its profile path, process names, policy file location, and any browser-specific quirks (Brave's numeric command IDs and Meta+↔Command+ rewrite; Vivaldi's vivaldi.actions[0] storage shape). Adding a new Chromium browser is roughly 150 lines.

Brave install methods

Install Auto-detected Automatic apply [pwa] works Notes
.deb (Debian / Ubuntu apt repo) yes yes yes Reference install; full support.
.rpm (Fedora / RHEL dnf repo) yes yes yes Same paths as .deb (Chromium upstream convention).
Arch / pacman yes yes yes Same.
NixOS (pkgs.brave) yes yes yes Same.
Snap (sudo snap install brave) yes (probes ~/snap/brave/current/.config/...) yes refused with clear error — sandbox doesn't read /etc/brave/policies/managed/ Use .deb for [pwa] support.
Flatpak (flathub com.brave.Browser) yes (probes ~/.var/app/com.brave.Browser/config/...) yes; relaunch goes back through flatpak run com.brave.Browser refused with clear error — same sandbox limitation as Snap Use .deb for [pwa] support.
macOS .dmg yes yes yes Includes the cfprefsd cache invalidation needed for [pwa].
Windows installer yes (auto-detects %LOCALAPPDATA%) yes; relaunches via the standard brave.exe path yes — writes to Windows Registry (HKLM\...\WebAppInstallForceList); requires running as Administrator

Dual-install machines (e.g. .deb + Snap installed side-by-side) prefer the direct-install profile, matching what which brave-browser resolves to.

Caveats

  • Cloud sync (Brave Sync, Edge Sync, Vivaldi Sync) can overwrite [settings] entries on its next pulse if they fall in a synced category. UI-layout keys like brave.tabs.vertical_tabs_* are local-only and immune. apply prints a non-fatal warning when sync.has_setup_completed=true so you know to verify the change sticks.
  • Chrome — [pwa] works fully, [settings] works for non-MAC keys. Chrome hardcodes keyboard shortcuts in C++ (there is no [shortcuts]), and many useful settings (homepage, session.startup_urls, browser.show_home_button, pinned_tabs, …) live in a separate Secure Preferences file with their own HMAC integrity. dotbrowser walks protection.macs from BOTH files when refusing MAC-protected keys, so those keys are correctly rejected up front rather than silently reset by Chrome on next launch — run dotbrowser chrome settings blocked to see exactly what Chrome will refuse on your profile. Use [settings] for non-MAC keys (bookmark_bar.show_on_all_tabs, ntp.shortcust_visible, …) and [pwa], which uses Chromium's enterprise-policy mechanism and works fully. Brave and Vivaldi expose dedicated, non-MAC-protected prefs (brave.accelerators, vivaldi.actions) specifically for user customisation; Edge has enough non-protected keys to make [settings] useful too. Brave and Vivaldi support [shortcuts]; Edge and Chrome do not.
  • A handful of settings (homepage, default search engine, pinned_tabs, …) are integrity-protected and can't be patched yet — dotbrowser refuses them with a clear error rather than letting the change silently disappear on next launch. Set those via the browser UI for now.
  • [pwa] is force-install (Chromium's enterprise WebAppInstallForceList). Apps installed this way appear in chrome://apps (or edge://apps) with an "Installed by your administrator" label and the right-click "Remove" option is hidden — to uninstall, delete the URL from [pwa] and re-apply, then dotbrowser does the rest. This is the right semantics for dotfile-style management (the TOML is the source of truth) but worth knowing if you also install PWAs by hand via the address-bar Install button.

Roadmap

Open work is tracked on the issues page: settings catalog generator, Brave / Edge Beta / Nightly auto-detection, macOS live CI, more Chromium browsers, and more.

License

MIT

Metadata

Release files for dotbrowser 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dotbrowser 0.7.0
File Size Uploaded
dotbrowser-0.7.0.tar.gz 1.4 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for dotbrowser 0.7.0
File Interpreter ABI Platform
dotbrowser-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.5 MB

Release files / dotbrowser-0.7.0.tar.gz

Download URL dotbrowser-0.7.0.tar.gz
Size 1.4 MB
Tags Source
SHA-256 checksum
How to use checksums
e619e7a304e7d240d1805ff2e3ce9f4ca87613da0373ed95e6f223778e640515
BLAKE2b-256 checksum
How to use checksums
5e9f5dfda5ccfc10cea1d0cfe21a3be77b407b7406de257328782ef426e98355
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release files / dotbrowser-0.7.0-py3-none-any.whl

Download URL dotbrowser-0.7.0-py3-none-any.whl
Size 88.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e9b2585b508717d909207799226196da451f998cf6eb7b521b686294bbad2e50
BLAKE2b-256 checksum
How to use checksums
1da0504355168d3185226f77892e402f50ef955670ffc4daa8117a4b1b40b391
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release history Release notifications | RSS feed

This release

0.7.0 This release

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page