The Problem
ArchiveBox solves the pain of web content disappearing by providing a selfâhosted archive that captures URLs, browser history, bookmarks, Pocket/Pinboard feeds, and more in durable formats (HTML, PDF, WARC, SQLite, etc.). Organisations and individuals use it to preserve evidence, backup socialâmedia media, and keep research papers accessible long term.
What This Does
ArchiveBox is a Djangoâbacked Python application that accepts input via CLI, browser extension, or API, then persists the crawled content to the filesystem and a SQLite database. Core logic lives in archivebox/core/models.py (196 functions, 13 types) which defines the data model (get_group_by_cols, slug, api_url, to_json) and is called from 120 other files. Input handling starts at archivebox/cli/__init__.py:191 (main) and archivebox/core/management/commands/archivebox.py:15 (handle), both of which delegate to run_archivebox_cmd (called 281 times) and dispatch (called 125 times). Crawling and media extraction are orchestrated by archivebox/crawls/models.py (68 functions, 5 classes) and archivebox/machine/models.py (95 functions, 17 classes), each reading/writing the database and filesystem. Dockerisation is provided by Dockerfile and docker-compose.yml, while configuration is stored in archivebox/config/common.py (62 functions, 8 classes) and read via get_config (123 callers). The project ships with a browserâextension exporter, a REST API (archivebox/api/v1_core.py), and a web interface served through the Django ORM.
How It Is Wired
Execution flows from the entry points through a small set of hub modules:
| Entry point | Reaches | Primary callee(s) |
|---|---|---|
dispatch (archivebox/core/views.py:1503) | 125 functions | filter, get_config, use_archivebox_db |
handle (archivebox/core/management/commands/archivebox.py:15) | 133 functions | current (DB cls.objects.get) |
cli (archivebox/cli/__init__.py:146) | 116 functions | setup_django (subprocess) |
init (archivebox/cli/archivebox_init.py:25) | 114 functions | set (DB Binary.objects.filter) |
main (archivebox/cli/__init__.py:191) | 399 functions | run_runner (DB Crawl.objects.filter) |
Key paths to external sideâeffects:
dispatch â _latest_snapshot_for_url â find_snapshots_for_urlâ reads Snapshot.objects.filter (database).handle â currentâ reads cls.objects.get (database).cli â setup_djangoâ spawns a subprocess (subprocess.run).init â setâ reads Binary.objects.filter(id=binary_id).afirst (database).main â run_runnerâ filters Crawl.objects.filter(id=crawl_id, status__in=Crawl.RUNNABLE_âŚ) (database) then runs external commands viaarchivebox/services/runner.py.
The import graph contains 52 modules in circular dependencies (e.g., archivebox/core/models, archivebox/crawls/models, archivebox/machine/models, archivebox/config/common, archivebox/config/__init__, archivebox/services/runner). These cycles increase blast radius: a change to archivebox/core/models.py ripples through 97 importers, and archivebox/tests/conftest.py is imported by 81 other modules.
How To Use It
Setup
- pip:
pip install archivebox(pyproject.toml indicates pip/uv). - Docker: build from
Dockerfileor run the published imagearchivebox/archivebox.docker-compose.ymldefines the full stack (Postgres, Nginx, the ArchiveBox container).
Configuration
- Environment variables and settings live in
archivebox/config/common.pyand are read byget_config. No secret is committed; the repo states âcommitted secrets: none foundâ.
Running it
- CLI:
archivebox add https://example.com(invokesmainârun_archivebox_cmd). - Python API:
from archivebox.archive import add_url; add_url("https://example.com"). - Docker quickstart (from README):
curl -fsSL 'https://get.archivebox.io' | bashordocker run -p 8000:8000 archivebox/archivebox.
Stopping / migrating
- Management command:
archivebox manage migrate(uses Django migrations underarchivebox/core/migrations/).
RealâWorld Use
A research team needs to preserve a set of openâaccess papers and their referenced media. They run archivebox add https://arxiv.org/abs/2305.01234; the CLI enqueues the URL, archivebox/core/models.py creates a Snapshot record, archivebox/crawls/models.py launches a headless browser, extracts HTML, PDF, and embedded images, and stores them under data/snapshots/<id>/. The web UI (archivebox/core/views.py) then serves the archived copy, while the REST endpoint archivebox/api/v1_core.py allows an internal CI job to query the archive status. Because the data lives on disk and in SQLite, the team can migrate the archive to a new host by copying the data/ directory and reâregistering the database.
Code Health & Issues
Measured analysis (static, deterministic):
- 232 total findings: 86âŻhigh, 145âŻmedium, 1âŻlow.
- 329 internal modules, 990 import edges, 52 modules in circular dependencies.
- Hub modules:
archivebox/core/models.py(97 dependents),archivebox/tests/conftest.py(81 dependents),archivebox/crawls/models.py(71 dependents). - Deep nesting (indentation depthâŻ8) in
archivebox/core/models.py,archivebox/crawls/models.py,archivebox/machine/models.py. - Oversized files (>âŻ3âŻk lines) same as above.
- Broad exception handling (
except Exception) inarchivebox/core/models.py,archivebox/crawls/models.py,archivebox/config/common.py. - Importâcycle members in the same three files plus
archivebox/config/common.py. - File opened without context manager in
archivebox/tests/conftest.py,archivebox/services/runner.py,archivebox/workers/supervisord_util.py.
CODE HEALTH AUDIT (6 findings, each with a prescribed fix):
- [HIGH] Pin thirdâparty GitHub Actions to a commit SHA â
.github/workflowsuses@v3,@v6etc.; replace with 40âchar SHA and let Dependabot bump them. - [HIGH] Commit a lockfile beside the manifest â
etc/package.jsonhas no lockfile; run the package manager once and commit the generated file. - [MEDIUM] Pin the container base image by digest â
Dockerfilereferencesarchivebox/sonic:1.4.9,ubuntu:24.04; pin toimage:tag@sha256:<digest>and enable Dependabot Docker scanning. - [MEDIUM] Gate pull requests on a dependency vulnerability scan â no dependencyâreview action in CI; add
dependency-review-actiononpull_requestorosv-scanneron push. - [MEDIUM] Add a nonâroot USER to the image â Dockerfile ends with
CMD/ENTRYPOINTwithout aUSERdirective; create an unprivileged user, chown needed paths, and end the Dockerfile withUSER. - [LOW] Set
timeout-minuteson workflow jobs â four jobs in.github/workflows/deploy-publicsite.ymlhave no timeout; add a realistic bound to each job.
The Bottom Line
ArchiveBox is a capable, wellâdocumented selfâhosted archiving platform that captures web content in durable formats and offers CLI, API, and web UI entry points. Its strengths lie in extensive format support, Docker support, and an active community. Maintainability suffers from deep import cycles, oversized core modules, and several hygiene gaps (missing lockfile, mutable base images, unpinned GitHub Action SHAs). Teams that need a flexible, openâsource way to preserve web evidence will find it valuable, but should budget time to refactor the hub modules, add a lockfile, and lock down container base images before deploying to production.