dotbrave 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dotbrave-0.1.0/.github/workflows/ci.yml +32 -0
- dotbrave-0.1.0/.github/workflows/release.yml +50 -0
- dotbrave-0.1.0/.gitignore +22 -0
- dotbrave-0.1.0/CLAUDE.md +121 -0
- dotbrave-0.1.0/LICENSE +21 -0
- dotbrave-0.1.0/PKG-INFO +277 -0
- dotbrave-0.1.0/README.md +252 -0
- dotbrave-0.1.0/docs/img/minimal-brave.png +0 -0
- dotbrave-0.1.0/examples/all.toml +79 -0
- dotbrave-0.1.0/examples/pwa.toml +21 -0
- dotbrave-0.1.0/examples/settings.toml +56 -0
- dotbrave-0.1.0/examples/shortcuts.toml +22 -0
- dotbrave-0.1.0/flake.nix +57 -0
- dotbrave-0.1.0/pyproject.toml +41 -0
- dotbrave-0.1.0/scripts/generate_brave_command_ids.py +79 -0
- dotbrave-0.1.0/src/dotbrave/__init__.py +1 -0
- dotbrave-0.1.0/src/dotbrave/__main__.py +4 -0
- dotbrave-0.1.0/src/dotbrave/_base/__init__.py +0 -0
- dotbrave-0.1.0/src/dotbrave/_base/cdp.py +286 -0
- dotbrave-0.1.0/src/dotbrave/_base/live_apply.py +98 -0
- dotbrave-0.1.0/src/dotbrave/_base/orchestrator.py +700 -0
- dotbrave-0.1.0/src/dotbrave/_base/process.py +344 -0
- dotbrave-0.1.0/src/dotbrave/_base/pwa.py +530 -0
- dotbrave-0.1.0/src/dotbrave/_base/settings.py +431 -0
- dotbrave-0.1.0/src/dotbrave/_base/utils.py +67 -0
- dotbrave-0.1.0/src/dotbrave/browser.py +355 -0
- dotbrave-0.1.0/src/dotbrave/cli.py +65 -0
- dotbrave-0.1.0/src/dotbrave/command_ids.py +549 -0
- dotbrave-0.1.0/src/dotbrave/live.py +273 -0
- dotbrave-0.1.0/src/dotbrave/pwa.py +131 -0
- dotbrave-0.1.0/src/dotbrave/settings.py +42 -0
- dotbrave-0.1.0/src/dotbrave/shortcuts.py +289 -0
- dotbrave-0.1.0/src/dotbrave/utils.py +103 -0
- dotbrave-0.1.0/tests/__init__.py +0 -0
- dotbrave-0.1.0/tests/conftest.py +79 -0
- dotbrave-0.1.0/tests/test_apply_live.py +186 -0
- dotbrave-0.1.0/tests/test_brave_channel.py +94 -0
- dotbrave-0.1.0/tests/test_brave_live.py +173 -0
- dotbrave-0.1.0/tests/test_cdp.py +47 -0
- dotbrave-0.1.0/tests/test_error_messages.py +146 -0
- dotbrave-0.1.0/tests/test_export.py +215 -0
- dotbrave-0.1.0/tests/test_help.py +84 -0
- dotbrave-0.1.0/tests/test_init.py +75 -0
- dotbrave-0.1.0/tests/test_live_apply.py +356 -0
- dotbrave-0.1.0/tests/test_logic.py +220 -0
- dotbrave-0.1.0/tests/test_macos_pwa_daemon.py +178 -0
- dotbrave-0.1.0/tests/test_platform.py +657 -0
- dotbrave-0.1.0/tests/test_pwa_apply.py +469 -0
- dotbrave-0.1.0/tests/test_restore.py +222 -0
- dotbrave-0.1.0/tests/test_settings_apply.py +380 -0
- dotbrave-0.1.0/tests/test_smoke.py +79 -0
- dotbrave-0.1.0/tests/test_unified_apply.py +427 -0
- dotbrave-0.1.0/tests/test_url_config.py +139 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: pytest (${{ matrix.os }} / py${{ matrix.python-version }})
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
16
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
cache: pip
|
|
25
|
+
|
|
26
|
+
- name: Install package + test extras
|
|
27
|
+
run: |
|
|
28
|
+
python -m pip install --upgrade pip
|
|
29
|
+
pip install -e ".[test]"
|
|
30
|
+
|
|
31
|
+
- name: Run pytest
|
|
32
|
+
run: pytest -q
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Triggered when you push a tag like `v0.2.0`. Workflow:
|
|
4
|
+
# 1. tag locally → git tag v0.2.0
|
|
5
|
+
# 2. push the tag → git push origin v0.2.0
|
|
6
|
+
# 3. this workflow runs → builds sdist + wheel, validates, uploads
|
|
7
|
+
#
|
|
8
|
+
# Setup needed once before the first release:
|
|
9
|
+
# - Generate an API token at https://pypi.org/manage/account/token/
|
|
10
|
+
# (scope it to the dotbrave project after the first upload).
|
|
11
|
+
# - Add it as a GitHub Actions secret named `PYPI_API_TOKEN`
|
|
12
|
+
# (Settings → Secrets and variables → Actions → New repository secret).
|
|
13
|
+
#
|
|
14
|
+
# After dotbrave is published once, prefer migrating to PyPI Trusted
|
|
15
|
+
# Publishing (OIDC, no shared secret) — see https://docs.pypi.org/trusted-publishers/
|
|
16
|
+
|
|
17
|
+
on:
|
|
18
|
+
push:
|
|
19
|
+
tags: ["v*"]
|
|
20
|
+
workflow_dispatch:
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
build-and-publish:
|
|
24
|
+
name: build + upload
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
|
|
29
|
+
- name: Set up Python
|
|
30
|
+
uses: actions/setup-python@v5
|
|
31
|
+
with:
|
|
32
|
+
python-version: "3.12"
|
|
33
|
+
cache: pip
|
|
34
|
+
|
|
35
|
+
- name: Install build tooling
|
|
36
|
+
run: |
|
|
37
|
+
python -m pip install --upgrade pip
|
|
38
|
+
pip install build twine
|
|
39
|
+
|
|
40
|
+
- name: Build sdist + wheel
|
|
41
|
+
run: python -m build
|
|
42
|
+
|
|
43
|
+
- name: Validate metadata
|
|
44
|
+
run: twine check dist/*
|
|
45
|
+
|
|
46
|
+
- name: Upload to PyPI
|
|
47
|
+
env:
|
|
48
|
+
TWINE_USERNAME: __token__
|
|
49
|
+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
50
|
+
run: twine upload dist/*
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
.env
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
.ruff_cache/
|
|
14
|
+
.tox/
|
|
15
|
+
htmlcov/
|
|
16
|
+
.coverage
|
|
17
|
+
*.bak.*
|
|
18
|
+
.worktrees/
|
|
19
|
+
|
|
20
|
+
# nix
|
|
21
|
+
result
|
|
22
|
+
result-*
|
dotbrave-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Guidance for agents changing this repository. User-facing setup, examples,
|
|
4
|
+
and CLI reference belong in `README.md` and runtime `--help`; keep this file
|
|
5
|
+
focused on implementation constraints.
|
|
6
|
+
|
|
7
|
+
## Project
|
|
8
|
+
|
|
9
|
+
`dotbrave` manages Brave browser customizations from TOML files: managed
|
|
10
|
+
tables `[shortcuts]`, `[settings]`, `[pwa]`, applied live when possible with
|
|
11
|
+
an automatic offline fallback, on Linux/macOS/Windows across the stable,
|
|
12
|
+
beta, and nightly channels. It is a Python 3.11+ stdlib-only CLI package,
|
|
13
|
+
extracted from the multi-browser `xom11/dotbrowser` project. The CLI is
|
|
14
|
+
single-browser: `dotbrave apply`, not `dotbrave brave apply`.
|
|
15
|
+
|
|
16
|
+
## Commands
|
|
17
|
+
|
|
18
|
+
Run from the repository root:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install -e ".[test]"
|
|
22
|
+
PYTHONPATH=src python -m dotbrave --help
|
|
23
|
+
PYTHONPATH=src python -m dotbrave apply --help
|
|
24
|
+
pytest -q
|
|
25
|
+
|
|
26
|
+
# Regenerate the command-name mapping from upstream brave-core headers.
|
|
27
|
+
# Requires an authenticated `gh` CLI.
|
|
28
|
+
python scripts/generate_brave_command_ids.py
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Useful targeted suites:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pytest tests/test_help.py tests/test_smoke.py
|
|
35
|
+
pytest tests/test_unified_apply.py tests/test_settings_apply.py tests/test_pwa_apply.py
|
|
36
|
+
pytest tests/test_live_apply.py tests/test_brave_live.py tests/test_apply_live.py
|
|
37
|
+
pytest tests/test_export.py tests/test_restore.py tests/test_brave_channel.py
|
|
38
|
+
pytest tests/test_platform.py tests/test_macos_pwa_daemon.py
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Code Map
|
|
42
|
+
|
|
43
|
+
- `src/dotbrave/cli.py`: root parser; mounts actions via
|
|
44
|
+
`browser.register(parser)`.
|
|
45
|
+
- `src/dotbrave/browser.py`: channel/profile-root resolution, plan
|
|
46
|
+
assembly, init template, `cmd_*` handlers, registration.
|
|
47
|
+
- `src/dotbrave/shortcuts.py`, `settings.py`, `pwa.py`: namespace wrappers
|
|
48
|
+
(module-level state kept patchable for tests).
|
|
49
|
+
- `src/dotbrave/live.py`: Brave live-apply routes (settingsPrivate, New
|
|
50
|
+
Tab actions, CommandsService).
|
|
51
|
+
- `src/dotbrave/utils.py`: Brave `BrowserProcess` config per channel.
|
|
52
|
+
- `src/dotbrave/command_ids.py`: generated name<->id mapping.
|
|
53
|
+
- `src/dotbrave/_base/`: engine shared with upstream dotbrowser:
|
|
54
|
+
`orchestrator.py` (config loading, unified apply/init/export/restore,
|
|
55
|
+
argparse wiring), `utils.py` (`Plan`, atomic write), `settings.py`
|
|
56
|
+
(dotted keys + MAC refusal), `pwa.py` (policy storage + macOS daemon),
|
|
57
|
+
`process.py`, `cdp.py`, `live_apply.py`.
|
|
58
|
+
- `examples/*.toml`: valid user-facing config samples.
|
|
59
|
+
- `tests/`: behavior contracts. Add or update tests alongside behavior or
|
|
60
|
+
CLI-help changes.
|
|
61
|
+
|
|
62
|
+
## Invariants
|
|
63
|
+
|
|
64
|
+
Preserve these contracts unless a change explicitly redesigns them:
|
|
65
|
+
|
|
66
|
+
1. `apply` uses module `Plan` objects and one orchestrated cycle. Validate
|
|
67
|
+
all selected namespaces before committing profile changes; create at most
|
|
68
|
+
one Preferences backup per offline apply.
|
|
69
|
+
2. Missing TOML table means "skip this namespace"; an empty table means
|
|
70
|
+
"remove/reset entries previously managed by dotbrave".
|
|
71
|
+
3. `[settings]` must refuse MAC-protected keys found in either `Preferences`
|
|
72
|
+
or sibling `Secure Preferences`. Never make a write that Brave will
|
|
73
|
+
silently reset on launch.
|
|
74
|
+
4. `[pwa]` is external managed policy storage. It requires sudo on
|
|
75
|
+
Linux/macOS or Administrator on Windows when changed, writes before the
|
|
76
|
+
Preferences commit, and has no Preferences sidecar.
|
|
77
|
+
On macOS the policy file is kept alive by a root-owned self-healing
|
|
78
|
+
LaunchDaemon (`org.dotbrave.<bundle>.pwa`) installed during the same
|
|
79
|
+
privileged write; an empty `[pwa]` table removes the daemon and its
|
|
80
|
+
support files. Keep `build_heal_script`/`build_launchd_plist` pure and
|
|
81
|
+
`install_self_healing_daemon`/`remove_self_healing_daemon` patchable.
|
|
82
|
+
5. Plain `apply` manages live apply. Endpoints bind to `127.0.0.1` and
|
|
83
|
+
remain internal; no public endpoint or force-kill switch is exposed.
|
|
84
|
+
Unsupported live settings and removals fall back to a normal close,
|
|
85
|
+
verified offline apply, and relaunch. A diff whose only changes are
|
|
86
|
+
`[pwa]` never touches the running browser: the policy is written
|
|
87
|
+
directly (no endpoint bootstrap) and Brave loads it at next launch.
|
|
88
|
+
6. `export` intentionally omits `[settings]` (Chromium has no defaults
|
|
89
|
+
table for arbitrary prefs). It emits `[shortcuts]` diffs against
|
|
90
|
+
`brave.default_accelerators` plus `[pwa]`.
|
|
91
|
+
7. `restore` restores Preferences backups and clears shortcut/settings
|
|
92
|
+
sidecars. If Brave is running, it closes normally and restarts; it does
|
|
93
|
+
not roll back external `[pwa]` policy.
|
|
94
|
+
8. Profile flags (`--channel`, `-r`, `-p`) are accepted both before and
|
|
95
|
+
after the action name: real defaults live on the root parser; action
|
|
96
|
+
parsers re-declare them with `argparse.SUPPRESS` so the after-action
|
|
97
|
+
form overrides. Profile-reading leaves set `_needs_profile` so
|
|
98
|
+
`_normalize_brave_args` skips profile-root resolution for `init` and
|
|
99
|
+
`shortcuts list`. Keep new action parsers consistent with this scheme.
|
|
100
|
+
9. Runtime help is part of the capability contract. Do not reintroduce
|
|
101
|
+
manual endpoint-selection or force-kill controls.
|
|
102
|
+
|
|
103
|
+
## Browser Notes
|
|
104
|
+
|
|
105
|
+
- Shortcut values use Chromium KeyEvent-style bindings. `Meta+` and
|
|
106
|
+
`Command+` are normalized per platform before persistence.
|
|
107
|
+
- `--channel` changes both profile discovery and process handling.
|
|
108
|
+
Non-stable Linux channels require PID filtering so applying Beta/Nightly
|
|
109
|
+
does not close another Brave channel.
|
|
110
|
+
- Keep shared engine logic in `_base/` (it mirrors upstream dotbrowser's
|
|
111
|
+
`_base/`, which eases porting fixes across the two repos); Brave-specific
|
|
112
|
+
behavior belongs in the top-level modules.
|
|
113
|
+
- Preserve testability: policy paths, privilege writers, and process
|
|
114
|
+
callbacks are intentionally patchable in tests.
|
|
115
|
+
|
|
116
|
+
## Release
|
|
117
|
+
|
|
118
|
+
`src/dotbrave/__init__.py::__version__` is the version source of truth;
|
|
119
|
+
`pyproject.toml` reads it through Hatch. Releases are tag-driven through
|
|
120
|
+
`.github/workflows/release.yml` after tests pass (needs the
|
|
121
|
+
`PYPI_API_TOKEN` repo secret).
|
dotbrave-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xom11
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
dotbrave-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dotbrave
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Manage Brave as a dotfile: keyboard shortcuts, general settings (non-MAC keys), and force-installed PWAs from one TOML.
|
|
5
|
+
Project-URL: Homepage, https://github.com/xom11/dotbrave
|
|
6
|
+
Project-URL: Issues, https://github.com/xom11/dotbrave/issues
|
|
7
|
+
Author: xom11
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: brave,browser,config,dotfiles,keybindings,pwa,settings,shortcuts
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Provides-Extra: test
|
|
23
|
+
Requires-Dist: pytest>=7; extra == 'test'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# dotbrave
|
|
27
|
+
|
|
28
|
+
[](https://github.com/xom11/dotbrave/actions/workflows/ci.yml)
|
|
29
|
+
[](https://pypi.org/project/dotbrave/)
|
|
30
|
+
[](https://pypi.org/project/dotbrave/)
|
|
31
|
+
|
|
32
|
+
Manage Brave as a dotfile. Keep keyboard shortcuts, UI tweaks, and
|
|
33
|
+
force-installed web apps in a single TOML, apply with one command, sync
|
|
34
|
+
across machines — no Brave Sync required.
|
|
35
|
+
|
|
36
|
+
> **Status: alpha.** Works on Linux, macOS, and Windows against Brave
|
|
37
|
+
> stable, beta, and nightly. Extracted from the multi-browser
|
|
38
|
+
> [dotbrowser](https://github.com/xom11/dotbrowser) project as a focused,
|
|
39
|
+
> Brave-only tool with a flatter CLI (`dotbrave apply` instead of
|
|
40
|
+
> `dotbrowser brave apply`). Python 3.11+, standard library only.
|
|
41
|
+
|
|
42
|
+
## Quick start
|
|
43
|
+
|
|
44
|
+
The repo ships an opinionated example config: vertical tabs collapsed to
|
|
45
|
+
icons, decluttered new tab page, stripped-down toolbar, vim-style hjkl
|
|
46
|
+
shortcuts. [`examples/all.toml`](examples/all.toml) bundles all three
|
|
47
|
+
namespaces; [`shortcuts.toml`](examples/shortcuts.toml),
|
|
48
|
+
[`settings.toml`](examples/settings.toml), and
|
|
49
|
+
[`pwa.toml`](examples/pwa.toml) are single-namespace variants.
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
**Scaffold a starter config from scratch:**
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
dotbrave init # write commented template to stdout
|
|
57
|
+
dotbrave init -o brave.toml # ...or to a file
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Or apply the example directly from GitHub** — no clone, no install.
|
|
61
|
+
Fetched payloads are echoed with byte size + SHA-256 so you can see exactly
|
|
62
|
+
what's being applied:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
uvx dotbrave apply --dry-run \
|
|
66
|
+
https://raw.githubusercontent.com/xom11/dotbrave/main/examples/all.toml
|
|
67
|
+
|
|
68
|
+
# Apply. If Brave is already running, dotbrave uses live apply;
|
|
69
|
+
# first-time live setup closes Brave normally and relaunches it once.
|
|
70
|
+
uvx dotbrave apply \
|
|
71
|
+
https://raw.githubusercontent.com/xom11/dotbrave/main/examples/all.toml
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Prefer to inspect / customise locally first? Download then apply:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
curl -fsSL -o brave.toml https://raw.githubusercontent.com/xom11/dotbrave/main/examples/all.toml
|
|
78
|
+
# edit brave.toml ...
|
|
79
|
+
uvx dotbrave apply brave.toml
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Anything you later remove from your config reverts to Brave's default on
|
|
83
|
+
the next `apply` — no orphan entries.
|
|
84
|
+
|
|
85
|
+
## Install
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pipx install dotbrave # global, isolated venv
|
|
89
|
+
uvx dotbrave <args> # run on demand, no install step
|
|
90
|
+
pip install dotbrave # into the active environment
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Run from a branch: `uvx --from git+https://github.com/xom11/dotbrave dotbrave <args>`.
|
|
94
|
+
Local dev: `pip install -e ".[test]"`. Nix users: the repo ships a flake
|
|
95
|
+
(`nix run github:xom11/dotbrave`).
|
|
96
|
+
|
|
97
|
+
## Build your own config
|
|
98
|
+
|
|
99
|
+
A single TOML carries `[shortcuts]`, `[settings]` and `[pwa]`. One `apply`
|
|
100
|
+
writes all three in a single backup + write cycle.
|
|
101
|
+
|
|
102
|
+
```toml
|
|
103
|
+
# brave.toml
|
|
104
|
+
[shortcuts]
|
|
105
|
+
toggle_sidebar = ["Control+Shift+KeyE"]
|
|
106
|
+
toggle_ai_chat = ["Alt+KeyA"]
|
|
107
|
+
|
|
108
|
+
# vim-style hjkl
|
|
109
|
+
back = ["Alt+KeyH"]
|
|
110
|
+
forward = ["Alt+KeyL"]
|
|
111
|
+
select_previous_tab = ["Alt+KeyJ"]
|
|
112
|
+
select_next_tab = ["Alt+KeyK"]
|
|
113
|
+
|
|
114
|
+
# Same chord on all OSes — Meta+ = Cmd on macOS, Super on Linux/Windows (auto-translated)
|
|
115
|
+
new_tab = ["Control+KeyT", "Meta+KeyT"]
|
|
116
|
+
close_tab = ["Control+KeyW", "Meta+KeyW"]
|
|
117
|
+
|
|
118
|
+
[settings]
|
|
119
|
+
"brave.tabs.vertical_tabs_enabled" = true
|
|
120
|
+
"brave.tabs.vertical_tabs_collapsed" = true
|
|
121
|
+
"bookmark_bar.show_on_all_tabs" = false
|
|
122
|
+
|
|
123
|
+
[pwa]
|
|
124
|
+
# Force-installed Progressive Web Apps. Brave fetches each manifest,
|
|
125
|
+
# downloads icons, registers the app in chrome://apps, and emits a
|
|
126
|
+
# launcher (.desktop on Linux, app shim on macOS, Start Menu shortcut on
|
|
127
|
+
# Windows). Removing a URL + re-applying = uninstall.
|
|
128
|
+
urls = [
|
|
129
|
+
"https://squoosh.app/",
|
|
130
|
+
"https://app.element.io/",
|
|
131
|
+
]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
dotbrave apply brave.toml --dry-run # preview the diff
|
|
136
|
+
dotbrave apply brave.toml # live apply if Brave is running
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
- **Shortcut keys**: Chromium [KeyEvent codes](https://www.w3.org/TR/uievents-code/)
|
|
140
|
+
joined by `+` — `Control+Shift+KeyP`, `Alt+Digit1`, `F11`. `Meta+` is
|
|
141
|
+
auto-translated to `Command+` on macOS, so one config works everywhere.
|
|
142
|
+
- **Setting keys**: dotted paths into the profile `Preferences` JSON.
|
|
143
|
+
MAC-protected keys (`homepage`, default search engine, `pinned_tabs`, …)
|
|
144
|
+
are refused with a clear error — Brave would silently reset them on
|
|
145
|
+
launch. Run `dotbrave settings blocked` to see what's protected.
|
|
146
|
+
- **PWA URLs**: every entry installs with
|
|
147
|
+
`default_launch_container = "window"` and `create_desktop_shortcut = true`.
|
|
148
|
+
`[pwa]` is the only namespace that needs elevated privileges: it writes a
|
|
149
|
+
managed-policy file (sudo on Linux/macOS) or the Windows Registry
|
|
150
|
+
(Administrator). No `[pwa]` diff → no elevation prompt.
|
|
151
|
+
- **Empty header** (e.g. `[settings]` with no entries) wipes everything
|
|
152
|
+
dotbrave previously managed in that namespace. **Missing header** = skip
|
|
153
|
+
the namespace entirely.
|
|
154
|
+
|
|
155
|
+
## CLI reference
|
|
156
|
+
|
|
157
|
+
Shape: `dotbrave [profile-flags] <action> [action-flags] [args]`.
|
|
158
|
+
Profile flags may be given **before or after** the action name — the
|
|
159
|
+
after-action form wins when both are present. `export` is the inverse of
|
|
160
|
+
`apply`: produce a round-trippable TOML from the current profile state.
|
|
161
|
+
|
|
162
|
+
### Profile flags
|
|
163
|
+
|
|
164
|
+
| Flag | Default | What it does |
|
|
165
|
+
|---|---|---|
|
|
166
|
+
| `-r, --profile-root PATH` | auto-detected | Brave's root profile directory. |
|
|
167
|
+
| `-p, --profile NAME` | `Default` | Profile directory name inside the root — e.g. `"Profile 1"`. |
|
|
168
|
+
| `--channel {stable,beta,nightly}` | `stable` | Release channel; auto-detects the `Brave-Browser-Beta` / `-Nightly` profile path and targets the matching process. |
|
|
169
|
+
|
|
170
|
+
| Channel | Linux | macOS | Windows |
|
|
171
|
+
|---|---|---|---|
|
|
172
|
+
| stable | `~/.config/BraveSoftware/Brave-Browser` | `~/Library/Application Support/BraveSoftware/Brave-Browser` | `%LOCALAPPDATA%\BraveSoftware\Brave-Browser\User Data` |
|
|
173
|
+
| beta / nightly | same, with `-Beta` / `-Nightly` suffix | same, with suffix | same, with suffix |
|
|
174
|
+
|
|
175
|
+
Snap and Flatpak installs (stable only) are probed automatically on Linux.
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
dotbrave apply -r /custom/path -p "Profile 1" brave.toml
|
|
179
|
+
dotbrave apply --channel beta brave.toml
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Actions
|
|
183
|
+
|
|
184
|
+
| Action | What it does |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `init [-o FILE]` | Scaffold a commented starter TOML. Refuses to overwrite. |
|
|
187
|
+
| `apply [-n] CONFIG` | Apply `[shortcuts]` + `[settings]` + `[pwa]` from a file or HTTPS URL. `-n/--dry-run` previews the diff. URL fetches print size + SHA-256; pin with `--expect-sha256 HEX`; plain HTTP refused unless `--allow-http`. |
|
|
188
|
+
| `export [-o FILE] [-a]` | Emit `[shortcuts]` (only bindings that differ from Brave defaults; `-a/--all-shortcuts` lifts the filter) plus `[pwa]` as round-trippable TOML. `[settings]` is intentionally omitted — Chromium exposes no defaults table, so diff-vs-default is not computable. |
|
|
189
|
+
| `restore [--list] [--from FILE] [-n]` | Restore Preferences from a backup created by `apply` (most recent by default) and clear dotbrave sidecars. Does not touch `[pwa]` policy. |
|
|
190
|
+
| `shortcuts dump [-a] [-o FILE]` | Emit current bindings as TOML (default: only user-customised ones). |
|
|
191
|
+
| `shortcuts list [FILTER]` | List every bindable command name (substring filter). |
|
|
192
|
+
| `settings dump [KEYS...] [-o FILE]` | Dump managed keys, or explicit dotted paths. |
|
|
193
|
+
| `settings blocked [-o FILE]` | List MAC-protected keys `apply` will refuse, with current values. |
|
|
194
|
+
| `pwa dump [-o FILE]` | Emit currently force-installed PWA URLs as a `[pwa]` table. |
|
|
195
|
+
|
|
196
|
+
Every action has detailed `--help` with safety notes and examples.
|
|
197
|
+
|
|
198
|
+
## How it works
|
|
199
|
+
|
|
200
|
+
When Brave is closed, `dotbrave` patches the profile `Preferences` JSON
|
|
201
|
+
directly. Each offline apply takes one timestamped backup, writes
|
|
202
|
+
atomically (temp file + rename), and verifies the result by reloading.
|
|
203
|
+
|
|
204
|
+
When Brave is running, plain `apply` uses Brave's privileged UI APIs over
|
|
205
|
+
a private loopback DevTools endpoint: ordinary settings go through
|
|
206
|
+
`chrome.settingsPrivate`, New Tab settings through live New Tab UI
|
|
207
|
+
actions, and shortcuts through the Settings `CommandsService`. Supported
|
|
208
|
+
changes take effect without restarting. A Brave not yet carrying the
|
|
209
|
+
endpoint closes normally and relaunches once; a setting without a live
|
|
210
|
+
route falls back to the same normal-close + verified offline write. A
|
|
211
|
+
config whose only diff is `[pwa]` skips the endpoint entirely — the
|
|
212
|
+
managed policy is written while Brave keeps running, and Brave loads it
|
|
213
|
+
at its next launch. The endpoint binds to `127.0.0.1` only, and there is
|
|
214
|
+
no force-kill switch.
|
|
215
|
+
|
|
216
|
+
`[shortcuts]` and `[settings]` track managed entries in sidecar files
|
|
217
|
+
(`Preferences.dotbrave.{shortcuts,settings}.json`), so removing a key from
|
|
218
|
+
your config restores Brave's default on the next apply. `[pwa]` state
|
|
219
|
+
lives in Chromium's managed-policy storage (Linux JSON file, macOS plist,
|
|
220
|
+
Windows Registry) — the policy *is* the state.
|
|
221
|
+
|
|
222
|
+
On macOS, `/Library/Managed Preferences/` is a system-managed cache that
|
|
223
|
+
gets reclaimed at boot on non-MDM machines, which would otherwise
|
|
224
|
+
uninstall your PWAs. To keep them installed, `apply` installs a small
|
|
225
|
+
root-owned self-healing helper:
|
|
226
|
+
|
|
227
|
+
- `/Library/LaunchDaemons/org.dotbrave.com.brave.Browser.pwa.plist` —
|
|
228
|
+
watches the managed-preferences directory and rewrites the policy if
|
|
229
|
+
macOS removes it.
|
|
230
|
+
- `/Library/Application Support/dotbrave/com.brave.Browser.managed.plist`
|
|
231
|
+
and `com.brave.Browser.heal.sh` — the policy source of truth and the
|
|
232
|
+
rewrite script.
|
|
233
|
+
|
|
234
|
+
Applying an empty `[pwa]` table (`urls = []`) removes all of the above.
|
|
235
|
+
|
|
236
|
+
### Brave install methods
|
|
237
|
+
|
|
238
|
+
| Install | Auto-detected | `[pwa]` works | Notes |
|
|
239
|
+
|---|---|---|---|
|
|
240
|
+
| `.deb` / `.rpm` / Arch / NixOS | yes | yes | Reference install; full support. |
|
|
241
|
+
| **Snap** | yes | **refused with clear error** — sandbox doesn't read `/etc/brave/policies/managed/` | Use `.deb` for `[pwa]`. |
|
|
242
|
+
| **Flatpak** | yes | **refused with clear error** — same sandbox limitation | Relaunch goes back through `flatpak run`. |
|
|
243
|
+
| **macOS** `.dmg` | yes | yes | Includes cfprefsd cache invalidation + self-healing daemon. |
|
|
244
|
+
| **Windows** installer | yes | yes — writes `HKLM\Software\Policies\BraveSoftware\Brave`; requires Administrator | |
|
|
245
|
+
|
|
246
|
+
## Coming from dotbrowser?
|
|
247
|
+
|
|
248
|
+
`dotbrave` is a standalone extraction of dotbrowser's Brave support with
|
|
249
|
+
its own state names: sidecars are `Preferences.dotbrave.*.json` (was
|
|
250
|
+
`Preferences.dotbrowser.*.json`) and the macOS daemon is
|
|
251
|
+
`org.dotbrave.<bundle>.pwa` (was `org.dotbrowser.<bundle>.pwa`). Your
|
|
252
|
+
Brave profile itself is untouched by the switch, but entries previously
|
|
253
|
+
managed by dotbrowser are not tracked by dotbrave until you `apply` your
|
|
254
|
+
config once with dotbrave. On macOS, remove dotbrowser's daemon
|
|
255
|
+
(`dotbrowser brave apply` with an empty `[pwa]` table, or the manual
|
|
256
|
+
`launchctl bootout` steps in its README) before letting dotbrave manage
|
|
257
|
+
`[pwa]`, so two daemons don't fight over the same plist.
|
|
258
|
+
|
|
259
|
+
## Caveats
|
|
260
|
+
|
|
261
|
+
- **Brave Sync** can overwrite `[settings]` entries on its next pulse if
|
|
262
|
+
they fall in a synced category. UI-layout keys like
|
|
263
|
+
`brave.tabs.vertical_tabs_*` are local-only and immune. `apply` prints a
|
|
264
|
+
non-fatal warning when `sync.has_setup_completed=true`.
|
|
265
|
+
- A handful of settings (`homepage`, default search engine, `pinned_tabs`,
|
|
266
|
+
…) are integrity-protected and refused rather than silently reset by
|
|
267
|
+
Brave on next launch. Set those in the Brave UI.
|
|
268
|
+
- **`[pwa]` is force-install** (Chromium's enterprise
|
|
269
|
+
`WebAppInstallForceList`). Apps appear in `chrome://apps` with an
|
|
270
|
+
"Installed by your administrator" label and hidden right-click Remove —
|
|
271
|
+
uninstall by deleting the URL from `[pwa]` and re-applying. That is the
|
|
272
|
+
right semantics for dotfile-style management (the TOML is the source of
|
|
273
|
+
truth), but worth knowing if you also install PWAs by hand.
|
|
274
|
+
|
|
275
|
+
## License
|
|
276
|
+
|
|
277
|
+
MIT
|