Read Firefox's local synced-tabs SQLite database and export remote-device tab lists as JSON.
Project description
ff-synctool
Small Python PoC for reading Firefox's local synced-tabs database and exporting the tab list from each remote device as JSON.
What it does
When you're signed into Firefox Sync, Firefox mirrors the list of open tabs from every other signed-in device into a local SQLite database (synced-tabs.sqlite3) inside your profile directory. The tabs table holds one row per remote device, with the actual tab metadata stored as an escaped JSON blob in the record column.
ff_synctool.export opens that database, decodes each record, and writes one tab_<guid>.json file per remote device into the current directory.
Where to find synced-tabs.sqlite3
Inside your active Firefox profile folder:
- Linux:
~/.mozilla/firefox/<profile>/synced-tabs.sqlite3 - macOS:
~/Library/Application Support/Firefox/Profiles/<profile>/synced-tabs.sqlite3 - Windows:
%APPDATA%\Mozilla\Firefox\Profiles\<profile>\synced-tabs.sqlite3
Close Firefox before reading the file, or copy it elsewhere first — SQLite may refuse to open it while Firefox holds a lock.
Usage
After installing (see Development below), use the installed console script:
ff-synctool-export path/to/synced-tabs.sqlite3
Or run the module directly from a checkout:
python -m ff_synctool.export path/to/synced-tabs.sqlite3
This writes a tab_<guid>.json file per remote device into the working directory. Each file looks roughly like:
{
"id": "DFKDGhG1_yV-",
"clientName": "mike's Firefox Developer Edition on Mike-MBP",
"tabs": [
{
"title": "The lethal trifecta for AI agents…",
"urlHistory": ["https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/"],
"icon": "https://simonwillison.net/favicon.ico",
"lastUsed": 1750500277
}
]
}
ff_synctool.commands
ff_synctool.commands is a second exploratory module that dumps the remote_tab_commands table — the queue Firefox uses for "Send Tab to Device". It currently hardcodes the database path (synced_tabs.sqlite3) and prints each pending command (open URL / close tab) to stdout.
Schema reference
sqlite> PRAGMA table_info(tabs);
0|guid|TEXT|1||1
1|record|TEXT|1||0
2|last_modified|INTEGER|1||0
Status
Proof of concept. Read-only against your Firefox profile, no warranty. Not affiliated with Mozilla.
Development
pip install -e ".[dev]"
ruff check .
pytest
CI runs ruff + pytest on Python 3.12, 3.13, and 3.14 — see .github/workflows/ci.yml.
Releases
Tag-driven publish to PyPI via Trusted Publishing (no API tokens) — see .github/workflows/release.yml.
To cut a release:
- Bump
versionin pyproject.toml. - Commit and tag matching the version, prefixed with
v(e.g.git tag v0.1.1 && git push --tags). - The workflow verifies the tag matches
pyproject.toml, builds sdist + wheel, and publishes to PyPI.
First-time setup: on pypi.org, add this repo as a trusted publisher for the ff-synctool project (workflow release.yml, environment pypi).
Roadmap / suggested improvements
Concrete next steps to turn this into something more durable, ordered roughly by impact-vs-effort:
- Tests with pytest. See tests/test_export.py.
- CI via GitHub Actions. Ruff + pytest on Ubuntu, Python 3.12/3.13/3.14.
- Project metadata + src layout. PEP 621 pyproject.toml,
src/ff_synctool/package, console scriptff-synctool-export. Stdlib-only at runtime. - Tag-driven PyPI release workflow. See .github/workflows/release.yml.
- CLI polish in
ff_synctool.export.- Use the
--jsonargument (currently parsed but ignored) or remove it. - Add
--output-dirso output doesn't pollute CWD. - Replace f-string path joins with
pathlib.Path. - Close the sqlite connection (
contextlib.closingor explicitconn.close()). - Open the DB read-only:
sqlite3.connect(f"file:{path}?mode=ro", uri=True)so a running Firefox can't be corrupted.
- Use the
- Normalise
ff_synctool.commands. Drop the hardcoded filename, take the DB path as an argument, add anif __name__ == "__main__": main()guard, and consider merging intoexportas a subcommand (export tabs/export commands). - Type hints + ruff/mypy. Small surface area, easy win.
- LICENSE file. MIT or Apache-2.0 — currently absent.
- Optional: pretty-print mode. A
--format=tableflag that prints titles + URLs to stdout for quick inspection without writing files.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ff_synctool-0.1.0.tar.gz.
File metadata
- Download URL: ff_synctool-0.1.0.tar.gz
- Upload date:
- Size: 6.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e2598b92e97ed0b69cf5f62e6d79443f5c0b4c1bf020f0b3dfb0d716180a41c
|
|
| MD5 |
d366164211e91930c0ea0b4526ea2170
|
|
| BLAKE2b-256 |
754eed13fd76ea6c70b96f0eb610dd9ba50f47929fc4f2f0254e43bb6d6e32db
|
Provenance
The following attestation bundles were made for ff_synctool-0.1.0.tar.gz:
Publisher:
release.yml on Darkflib/ff-synctool
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ff_synctool-0.1.0.tar.gz -
Subject digest:
2e2598b92e97ed0b69cf5f62e6d79443f5c0b4c1bf020f0b3dfb0d716180a41c - Sigstore transparency entry: 1650762386
- Sigstore integration time:
-
Permalink:
Darkflib/ff-synctool@4e7c617cb48069f6a2a453e8018ad3667cb0e7a4 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Darkflib
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4e7c617cb48069f6a2a453e8018ad3667cb0e7a4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ff_synctool-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ff_synctool-0.1.0-py3-none-any.whl
- Upload date:
- Size: 6.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d5cb32f49a77ed035f4ecba2b73b39bef0e31ebb8a7c79de111fbcf59d30579c
|
|
| MD5 |
e0315bdb4afd2dbbd8cdd36c68f8de96
|
|
| BLAKE2b-256 |
ed8c2bad9303e028f366c6b5b0497b09e4ef8dea5fa124ee2799ff6fb2c03775
|
Provenance
The following attestation bundles were made for ff_synctool-0.1.0-py3-none-any.whl:
Publisher:
release.yml on Darkflib/ff-synctool
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ff_synctool-0.1.0-py3-none-any.whl -
Subject digest:
d5cb32f49a77ed035f4ecba2b73b39bef0e31ebb8a7c79de111fbcf59d30579c - Sigstore transparency entry: 1650762434
- Sigstore integration time:
-
Permalink:
Darkflib/ff-synctool@4e7c617cb48069f6a2a453e8018ad3667cb0e7a4 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Darkflib
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4e7c617cb48069f6a2a453e8018ad3667cb0e7a4 -
Trigger Event:
push
-
Statement type: