https://github.com/franckferman/bmctl
bmctl Firefox bookmark toolkit - audit duplicates, compare exports, merge collections, and generate an interactive dashboard from the command line.
https://github.com/franckferman/bmctl
bookmark bookmark-manager bookmark-search bookmark-web bookmarker bookmarking bookmarklet bookmarklets bookmarks bookmarks-app bookmarks-manager bookmarks-menu bookmarks-search
Last synced: about 2 months ago
JSON representation
bmctl Firefox bookmark toolkit - audit duplicates, compare exports, merge collections, and generate an interactive dashboard from the command line.
- Host: GitHub
- URL: https://github.com/franckferman/bmctl
- Owner: franckferman
- License: agpl-3.0
- Created: 2026-03-09T17:11:46.000Z (3 months ago)
- Default Branch: stable
- Last Pushed: 2026-03-27T00:45:06.000Z (3 months ago)
- Last Synced: 2026-03-27T06:29:48.936Z (3 months ago)
- Topics: bookmark, bookmark-manager, bookmark-search, bookmark-web, bookmarker, bookmarking, bookmarklet, bookmarklets, bookmarks, bookmarks-app, bookmarks-manager, bookmarks-menu, bookmarks-search
- Language: Python
- Homepage: https://franckferman.github.io/bmctl/
- Size: 38.1 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# bmctl
**Firefox bookmark toolkit** - audit duplicates, compare exports, merge collections, and generate an interactive self-hosted dashboard from the command line.
[](https://franckferman.github.io/bmctl/demo/)
[](https://www.python.org)
[](LICENSE)
---
## Table of Contents
- [Overview](#overview)
- [Features](#features)
- [Quick Start](#quick-start)
- [Project Structure](#project-structure)
- [Installation](#installation)
- [Getting Started](#getting-started)
- [Commands](#commands)
- [audit](#audit)
- [compare](#compare)
- [merge](#merge)
- [export](#export)
- [dashboard](#dashboard)
- [URL Deduplication](#url-deduplication)
- [Firefox JSON Compatibility](#firefox-json-compatibility)
- [License](#license)
---
## Overview
bmctl is a single-file Python CLI for managing Firefox bookmark exports (`.json`). It parses the full folder hierarchy, preserves the original Firefox tree order, and provides five commands covering the full lifecycle of a bookmark collection.
No installation required beyond Python itself. No database, no daemon, no config file. One file, one command, one output.
---
## Features
| Command | What it does |
|---|---|
| `audit` | Duplicate detection, stats, folder tree visualization |
| `compare` | Diff two exports - added, removed, net delta |
| `merge` | Merge two collections into a Netscape HTML file with conflict resolution |
| `export` | Flat export to CSV, Excel, or Markdown |
| `dashboard` | Self-contained interactive HTML dashboard, no server required |
URL normalization across all commands: scheme, `www.`, trailing slash, and `utm_*` parameters are stripped before comparison. `http://www.github.com/` and `https://github.com` are treated as the same URL.
---
## Quick Start
```bash
# 1. Get the tool
git clone https://github.com/franckferman/bmctl.git
cd bmctl
# 2. Export your bookmarks from Firefox
# Bookmarks > Show All Bookmarks > Import and Backup > Backup...
# Save as bookmarks.json
# 3. Run your first audit
python3 bmctl.py audit -i bookmarks.json
# Generate an interactive dashboard
python3 bmctl.py dashboard -i bookmarks.json -o dashboard.html
# Open dashboard.html in your browser
```
**Comparing two exports:**
`compare` works on two separate Firefox exports taken at different points in time. Export once, wait (add/remove bookmarks), export again, then diff:
```bash
# First export saved as bookmarks-jan.json
# Second export saved as bookmarks-feb.json
python3 bmctl.py compare -o bookmarks-jan.json -n bookmarks-feb.json
```
**Merging two collections:**
Useful if you have bookmarks spread across two Firefox profiles or two machines. The merge takes the union of both collections. For each URL:
- If it appears in only one collection, it is kept as-is.
- If it appears in both collections in the **same folder**, the most recent version is kept and tags are merged.
- If it appears in both collections in **different folders** (conflict), you are prompted to choose which folder to keep - or use `--no-confirm` to automatically keep the most recent.
No entries are silently deleted. Every URL from both files ends up in the output.
```bash
python3 bmctl.py merge -b profile-a.json -n profile-b.json -o merged.html
# merged.html is a Netscape HTML file - import it in any browser via:
# Bookmarks > Show All Bookmarks > Import and Backup > Restore... (or Import HTML)
```
No install step required. `bmctl.py` is a single file - you can also just download it directly without cloning the full repo.
---
## Project Structure
```
bmctl.py
BookmarkNode Data model for a single bookmark
UrlNormalizer URL normalization / deduplication
BookmarkDatabase JSON parser + in-memory index
BookmarkAuditor Duplicate detection + reporting
BookmarkComparator Two-database diff
BookmarkMerger Merge + conflict resolution + HTML export
BookmarkDashboardGen Interactive HTML dashboard generator
BookmarkExporter CSV / Excel / Markdown export
```
`BookmarkDatabase` is the core - it walks the Firefox JSON tree recursively, assigns each bookmark its full folder path, and builds a URL index for deduplication. All five commands consume it.
---
## Installation
**Prerequisites:** Python 3
No pip install required for core functionality. `pandas` and `openpyxl` are only needed for `export --format xlsx`:
```bash
pip install -r requirements.txt
```
No other dependencies.
---
## Getting Started
Export your bookmarks from Firefox:
```
Bookmarks > Show All Bookmarks > Import and Backup > Backup...
```
Save as `.json`. This file is the input for all commands.
---
## Commands
### `audit`
Inspect a single export: duplicate detection, stats, and optional folder tree.
```bash
python bmctl.py audit -i bookmarks.json
```
```
======================================================================
GLOBAL AUDIT REPORT
======================================================================
Total bookmarks found : 3110
Folders scanned : 244
Unique links : 3059
Duplicates detected : 51 (1.6%)
======================================================================
[!] Top 10 most duplicated links:
- GitHub
URL : https://github.com
Found 3 times:
* Folder: Dev
* Folder: CTI, OSINT & SocMint > Code Search Engines
```
Use `--show-tree` to verify that your folder structure was parsed correctly before generating a dashboard:
```bash
python bmctl.py audit -i bookmarks.json --show-tree
```
```
| [ 0] Cybersecurity & CTI
+-- [ 5] C2 Frameworks
+-- [ 7] OSINT & Recon
+-- [ 5] Exploitation
| [ 0] Development
+-- [ 4] Python
+-- [ 4] Go
| [ 6] Tools
| [ 6] Blogs & News
```
**Flags:**
| Flag | Default | Description |
|---|---|---|
| `-i / --input` | required | Firefox JSON export |
| `--top N` | `10` | Show top N most duplicated URLs |
| `--show-short` | off | Stats only, skip duplicate list |
| `--show-tree` | off | Print full folder hierarchy with bookmark counts |
---
### `compare`
Diff two exports. Shows what was added and removed between two snapshots of the same collection.
```bash
python bmctl.py compare -o bookmarks-old.json -n bookmarks-new.json
```
```
======================================================================
COMPARISON REPORT
======================================================================
Unique bookmarks V1 (old) : 2980
Unique bookmarks V2 (new) : 3059
Net delta : +79
======================================================================
[+] New bookmarks added : 102
[-] Old bookmarks removed : 23
======================================================================
[+] PREVIEW OF NEW ENTRIES (Max 15):
+ HackTricks (Folder: Cybersecurity > Documentation & Articles)
https://book.hacktricks.xyz
```
**Flags:**
| Flag | Default | Description |
|---|---|---|
| `-o / --old` | required | Old JSON export |
| `-n / --new` | required | New JSON export |
| `--show-full` | off | Show complete added/removed lists (no limit) |
| `--show-short` | off | Stats only, skip item lists |
---
### `merge`
Merge two bookmark collections into a single Netscape HTML file (importable by any browser). Detects URL conflicts (same URL in different folders) and resolves them.
```bash
python bmctl.py merge -b bookmarks-base.json -n bookmarks-new.json -o merged.html
```
Without `--no-confirm`, conflicts trigger an interactive prompt:
```
[?] FOLDER CONFLICT DETECTED FOR:
- URL : https://example.com
- Title: Example Site
In which folder(s) do you want to keep it?
1) [Keep] -> Dev > Tools
2) [Keep] -> Misc.
3) Skip / Keep most recent version
Your choice (1, 2...) :
```
Tags from all instances are merged onto the surviving node.
With automatic conflict resolution (keeps most recent):
```bash
python bmctl.py merge -b base.json -n new.json -o merged.html --no-confirm
```
**Flags:**
| Flag | Default | Description |
|---|---|---|
| `-b / --base` | required | Base JSON export |
| `-n / --new` | required | JSON to merge in |
| `-o / --output` | required | Output HTML file |
| `--no-confirm` | off | Auto-resolve conflicts silently (keeps most recent) |
---
### `export`
Export to a flat format. All formats include: Title, URL, Folder path, Tags, Date added.
```bash
# CSV
python bmctl.py export -i bookmarks.json --format csv -o bookmarks.csv
# Excel
python bmctl.py export -i bookmarks.json --format xlsx -o bookmarks.xlsx
# Markdown (organized by folder)
python bmctl.py export -i bookmarks.json --format md -o bookmarks.md
```
**Flags:**
| Flag | Default | Description |
|---|---|---|
| `-i / --input` | required | Firefox JSON export |
| `--format` | required | `csv`, `xlsx`, or `md` |
| `-o / --output` | required | Output file path |
---
### `dashboard`
Generate a fully self-contained single-file HTML dashboard. No server required - open directly in a browser.
```bash
python bmctl.py dashboard -i bookmarks.json -o dashboard.html
```
**Dashboard features:**
- Sidebar with full collapsible folder tree (Firefox order preserved)
- Three views: Dashboard (widget grid), Cards, Table
- Global search across title, URL and tags
- "Recent additions" quick view (last 50)
- Folder-aware widget titles (relative path in folder view, full path in global view)
- Pure black enterprise theme
**Flags:**
| Flag | Default | Description |
|---|---|---|
| `-i / --input` | required | Firefox JSON export |
| `-o / --output` | `dashboard.html` | Output HTML file |
> **WSL users:** use Linux paths to avoid backslash stripping.
> ```bash
> # Correct
> python bmctl.py dashboard -i /mnt/c/Users/you/Desktop/bookmarks.json \
> -o /mnt/c/Users/you/Desktop/dashboard.html
> # Wrong - bash strips backslashes without quotes
> python bmctl.py dashboard -i ... -o C:\Users\you\Desktop\dashboard.html
> ```
---
## URL Deduplication
bmctl normalizes URLs before comparing them to find true duplicates:
| Rule | Example |
|---|---|
| Scheme normalization | `http://` = `https://` |
| Strip `www.` prefix | `www.github.com` = `github.com` |
| Strip trailing slash | `github.com/` = `github.com` |
| Strip `utm_*` params | `?utm_source=...` removed |
| Query params preserved | `?q=foo` != `?q=bar` |
`http://www.github.com/` and `https://github.com` resolve to the same canonical URL.
---
## Firefox JSON Compatibility
bmctl handles both Firefox export formats:
| Format | Bookmark | Folder |
|---|---|---|
| Legacy | `typeCode: 1` | `typeCode: 2` |
| Modern | `type: "text/x-moz-place"` | `type: "text/x-moz-place-container"` |
| Fallback | presence of `uri` field | presence of `children` field |
---
## License
This project is licensed under the [GNU Affero General Public License v3.0](LICENSE) (AGPL-3.0).
Any use, modification, or distribution - including over a network - requires the full source code to remain open under the same license.