Skip to main content

Turn a Chrome profile's browsing history into a cleaned navigation forest.

Project description

chrome-navtree

Turn a Chrome profile's browsing history into a cleaned navigation forest: the tree of pages you actually visited, with single-page-app churn collapsed, auth redirects spliced out, and junk destinations pruned.

Chrome's on-disk history already records how you got from page to page (referrer and tab-opener edges, plus a transition type per visit). This reconstructs that into a forest of trees, then cleans it so what is left is the shape of your actual journeys instead of every redirect and tab refresh.

This package is the engine. What you build on top of it (a viewer, an archiver, a note-taking integration) is yours to write. A self-contained HTML viewer ships in the box so you can see the result immediately.

Install

Run it with no install at all (needs uv):

uvx --from git+https://github.com/technicalpickles/chrome-navtree chrome-navtree profiles

Or install it as a CLI and library:

pip install chrome-navtree      # or: uv add chrome-navtree
uv tool install chrome-navtree  # isolated, on your PATH

Python 3.11+ (uses stdlib tomllib). No runtime dependencies.

Quick start

# List the Chrome profiles on this machine
chrome-navtree profiles

# Open an HTML viewer of the last 7 days for your "work" profile
chrome-navtree preview --profile work --since 7d --open

# Emit the cleaned tree as JSON for something else to consume
chrome-navtree dump --profile work --since 24h > forest.json

--since takes 7d, 24h, or an ISO date like 2026-07-01. --profile takes a profile key (from chrome-navtree profiles) or a raw directory name like Default.

The live Chrome database is never touched. It gets copied to a temp file and opened read-only, so it is safe to run while Chrome is open.

Library

Every consumer goes through the same three steps, so the viewer and the JSON output can never drift:

from datetime import timedelta
from chrome_navtree import discover_profiles, load_visits, build_forest, clean, Rules

prof   = discover_profiles()["work"]
visits = load_visits(prof.history_path, since=timedelta(days=7))
raw    = build_forest(visits)                      # faithful, unfiltered
forest = clean(raw, Rules.load("rules.toml"))      # collapsed / spliced / pruned

forest.to_dict()   # JSON-serializable: {"roots": [...], "stats": {...}}

clean is a pure function: it returns a new forest and never mutates its input.

What cleaning does

Three passes, in order:

  • collapse folds churn to one node. A single-page app that keeps rewriting its URL, or a GitHub PR you clicked through 40 tabs of, becomes one node with a visit_count. Folding is driven by rules (drop query and fragment, plus per-host path folds).
  • splice removes auth interstitials and reconnects their children to the parent, so a real page -> SSO -> real page chain stays one unbroken path instead of three.
  • prune drops junk destinations along with their whole subtree.

Rules

The engine carries no knowledge of your specific tools or workplace. What counts as junk, what counts as auth plumbing, and how paths fold all live in TOML.

The package ships default_rules.toml with generic defaults: query/fragment dropping, GitHub PR/issue folding, and common SSO providers (Google, Microsoft, Okta, WorkOS). Your own rules.toml layers on top, where list values append to the defaults so you only list your own additions:

[prune]
junk_hosts = ["calendar.example.com", "internal-dashboard.example.com"]

[splice]
auth_hosts = ["sso.mycorp.example"]

[[collapse.fold]]
host = "tickets.example.com"
pattern = "^(/browse/[A-Z]+-[0-9]+)"

See examples/rules.example.toml for a fuller template. Pass it with --rules rules.toml or Rules.load("rules.toml").

Data model

  • Visit is one row from Chrome's visits table, joined to its URL, with the timestamp and transition decoded.
  • NavNode is a node in the tree: url, title, host, transition, dwell_s, visit_count, key, children.
  • NavForest is roots plus summary stats (raw vs cleaned node counts, distinct hosts, spliced and pruned tallies).

Notes and limits

  • Chrome expires history after about 90 days, so that is the practical window. Archiving beyond it is a job for a consumer built on this engine, not the engine itself.
  • Auth detection is deliberately aggressive: anything titled "sign in" or "log in", plus common auth URL shapes and identity-provider hosts, gets spliced. For a work browsing corpus that is the right trade. If a real page ever vanishes, narrow the splice rules.

Development

uv sync --extra dev     # ruff, mypy, pytest
uv run --extra dev pytest
mise run check          # ruff + ruff format + mypy + pytest (what CI runs)

Tooling is pinned with mise and git hooks run through hk. After cloning, mise install then hk install gives you ruff + mypy on every commit. If you would rather not use mise, the checks are plain uv run --extra dev ... commands.

Test data, isolated from your real Chrome

The live Chrome database is never touched (it is copied to a temp file and opened read-only), and none of the dev tooling reads or writes a real Chrome profile. Tests build synthetic Chrome databases in a temp dir and assert an exact cleaned forest, so they are deterministic and need no real browser data.

To poke at the CLI by hand, scaffold a fake Chrome profile (--chrome-root goes after the subcommand):

python scripts/fake_chrome.py /tmp/fake-chrome
chrome-navtree profiles --chrome-root /tmp/fake-chrome
chrome-navtree dump --chrome-root /tmp/fake-chrome --profile work

For realistic data, scripts/seed_realistic.py drives a throwaway Chromium profile (its own --user-data-dir, never your real one) with Playwright and snapshots the history into tests/fixtures/realistic/:

uv sync --extra playwright
uv run playwright install chromium
uv run python scripts/seed_realistic.py --scripted   # or --manual to click around yourself

Playwright is an opt-in extra and never runs in CI.

License

MIT. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

chrome_navtree-0.2.0.tar.gz (75.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

chrome_navtree-0.2.0-py3-none-any.whl (26.8 kB view details)

Uploaded Python 3

File details

Details for the file chrome_navtree-0.2.0.tar.gz.

File metadata

  • Download URL: chrome_navtree-0.2.0.tar.gz
  • Upload date:
  • Size: 75.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for chrome_navtree-0.2.0.tar.gz
Algorithm Hash digest
SHA256 6a953ab94576397aa09e2c8981bc8d01dd3c8337781ecf73f04036efac59bfcd
MD5 d3abfa9e747484793547a9fbcd17c64d
BLAKE2b-256 cd5779f2bb3bb2d47ed0b4aa180a9151c426d66e5dc05b6073f0e66112667032

See more details on using hashes here.

Provenance

The following attestation bundles were made for chrome_navtree-0.2.0.tar.gz:

Publisher: publish.yml on technicalpickles/chrome-navtree

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file chrome_navtree-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: chrome_navtree-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 26.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for chrome_navtree-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7e8e8ff2f3ded47b0a3fc6a3fe01268e9e36c4df98d48ae20187948b97607d1f
MD5 df3e931876611c55b344b5cd31e1bb62
BLAKE2b-256 63de82e163e02f679846c4b1cc23c79df2374e4df857e044cbe97678e5910e66

See more details on using hashes here.

Provenance

The following attestation bundles were made for chrome_navtree-0.2.0-py3-none-any.whl:

Publisher: publish.yml on technicalpickles/chrome-navtree

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page