Self-hosted service
asciimoo/omnom avatar
asciimoo/omnom

Omnom: self-hosted bookmarking that captures pages the way your browser rendered them

A web content preservation service

662 stars41 forksGoAGPL-3.0

At a glance

What is it?
Omnom is a Go web application that combines bookmarking, RSS aggregation and ActivityPub following with snapshot storage. It is aimed at people who want their saved pages to survive after the original URL changes or disappears, and who are willing to run a server to get that.
Who is it for?
Omnom fits engineers and small teams who already run their own services and want bookmarks that keep the rendered page, not just a URL, with feeds and ActivityPub in the same store. It does not fit anyone who wants a hosted product, password logins out of the box, or a drop-in replacement for a read-later app.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 85 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem Omnom solves: bookmarks that outlive the page

A bookmark is a pointer. When the target changes, moves behind a paywall or disappears, the pointer still resolves and the content does not. Omnom's answer is to store the page itself at the moment you save it. The README lists the core capability as bookmarking with website snapshots, and states that websites are captured as your browser renders it, so the displayed content of dynamic pages is saved as well. That distinction matters for anything built with client-side rendering, where a plain HTTP fetch of the URL returns a shell rather than the text a reader actually saw. Omnom also keeps multiple snapshots of the same URL, with a resource summary and compare and diff views, which means the unit of storage is not the URL but the URL at a point in time. The audience follows from that design: people who archive what they read, who cite pages that later change, or who want a personal corpus that does not depend on a third party staying online. The same instance also aggregates feeds and follows ActivityPub streams, so the bookmark store doubles as a reading queue.

Snapshots, feeds and ActivityPub in one Go service

The repository is a single Go binary, which keeps deployment simple but concentrates every subsystem in one process. The command list in the README shows the shape of the application: listen starts the server, update-feeds refreshes RSS and Atom sources, create-bookmark writes an entry, and diff-html and validate-html operate on saved documents from the command line. The comparison views in the web interface rest on that same HTML handling, so diffing is a first-class operation rather than an afterthought. Federated content arrives through ActivityPub support, documented on the project wiki, and the NLnet funding note names the ActivityPub work specifically, which suggests federation is an active part of the roadmap rather than a static feature. Multimedia is stored locally according to the feature list, so a snapshot is not just markup: linked assets can be kept alongside it. Filtering is described as flexible, covering date, free text search inside content, tags, users, domains and URLs. The web interface is multiuser, and the README describes a documented API plus a command to generate Markdown API documentation, so the browser is not the only way in.

Getting it running: binary, Docker, config.yml and the listen command

The README offers three paths. A single file binary release is published on the releases page, with the reminder to chmod +x after download. A Docker image is also available, with details on the wiki. For a local build, Go 1.24 or newer is required: check out the repository, run go get -u in the root directory, then go build. Once you have the binary, ./omnom help lists the available commands and ./omnom listen starts the web application. Configuration lives in config.yml, and the README notes that you must restart the webapp after updating it. To get a starting file, run ./omnom create-config config.yml, which generates a configuration file with the default values. User creation goes through ./omnom create-user, and login tokens can be generated from the command line with ./omnom create-token [username] login. The README states plainly that Omnom does not store passwords, so login depends on one time tokens, OAuth, or a remote user header. If you run Omnom behind an authenticating reverse proxy, you can pass the logged-in username in a header such as Remote-User and set the remote_user_header option in config.yml to make Omnom trust it. That mode cannot be combined with OAuth or open signups.

No password store, and what that constraint costs you

The absence of a password store is a deliberate reduction in attack surface, and it is also the first thing that will slow down a new deployment. Email delivery of login tokens requires a valid SMTP configuration in config.yml, so a fresh instance without working mail cannot log anyone in through the web interface until you either fix SMTP or fall back to ./omnom create-token from the shell. The remote user header path is convenient but has a sharp edge the README states directly: it is incompatible with OAuth and with open signups. Choose one authentication model per instance. There is a second operational constraint buried in the same section. Because tokens are the credential, the commands set-token and create-token are effectively account recovery and account issuance, and anyone with shell access to the binary can mint a login for any user. That is normal for self-hosted tools, but it means host access equals full account access, and the reverse proxy becomes part of your security boundary if you use the header mode. Nothing in the supplied material describes rate limiting, token expiry or session invalidation, so treat those as unverified until you read the source or the wiki.

Where Omnom is the wrong tool

Omnom stores rendered pages and, per the feature list, locally saved multimedia content. That is the point, and it is also the cost: disk usage grows with every snapshot, and a page with many images or a video can be far larger than its HTML. The README describes multiple snapshots of the same URL as a feature, which means the same address saved repeatedly consumes space repeatedly. There is no retention policy mentioned in the supplied material, so anyone archiving high-traffic sources should expect to manage storage themselves and should verify what the database and snapshot directory actually hold before pointing a crawler at it. The second mismatch is scope. Omnom is a server with a web interface, a database, an SMTP dependency for token login and an ActivityPub component. If you only want a read-later list with a clean mobile app, that is a lot of moving parts for the benefit. The third case is organisational: AGPL-3.0 is a strong copyleft licence, and the README names it without further commentary. Running Omnom for yourself is one thing; offering a modified version as a network service to others is where the licence terms start to matter, and that is a question for your own legal review rather than something the README settles.

How Omnom differs from a plain bookmark manager

The obvious comparison is a conventional bookmark manager or read-later service, which stores a URL, a title, maybe an extracted article body, and syncs that record across devices. The difference is what gets written to disk. A typical manager re-fetches the page when you open it, so the record is a reference; Omnom captures the rendered document and keeps it, and its diff-html command and compare views exist precisely because two captures of the same URL can differ. That changes the failure mode. A conventional manager degrades when the source goes away, and Omnom degrades when your storage fills. It also changes the client story: the README points to Firefox and Chrome/Chromium addons for the capture step, because a server-side fetch cannot reproduce what your browser rendered. So the workflow is browser-driven rather than server-driven, and the server is the archive rather than the fetcher. On the feed side, Omnom folds RSS and ActivityPub into the same store as bookmarks, which is a different arrangement from running a dedicated feed reader next to a separate bookmark service: one account, one search index, one backup target, and one process to keep alive.

Maintenance, upgrades and the AGPL-3.0 boundary

The project is not archived, the default branch is master, and the release cadence visible in the supplied material is roughly every two months across v0.7.0 in September 2025, v0.8.0 in November 2025 and v0.9.0 in January 2026, with the last push to the repository in June 2026. Those are pre-1.0 version numbers, so treat configuration and storage as things that can change between releases and read the release notes before upgrading. The upgrade procedure itself is not described in the README beyond the Docker wiki page, so the practical steps are: back up config.yml along with whatever the database and snapshot storage are, then replace the binary or image and restart. Because config.yml is generated once by ./omnom create-config and the README warns that changes require a restart, keep your edited file out of version control if it contains SMTP credentials or the remote_user_header setting. On licensing, the repository is AGPL-3.0, and the README states it in a single line. The licence is a copyleft licence with a network clause, which is the part that reaches self-hosted web services; how it applies to your specific deployment, especially if you modify the code or expose it to other users, is a question for a lawyer, not for this article. The project is funded through NGI Zero Core, a programme run by NLnet with European Commission support, and the funding note names an ActivityPub project page, which is a signal about where development effort is directed rather than a statement about stability.

Editorial conclusion

Omnom fits engineers and small teams who already run their own services and want bookmarks that keep the rendered page, not just a URL, with feeds and ActivityPub in the same store. It does not fit anyone who wants a hosted product, password logins out of the box, or a drop-in replacement for a read-later app. Before committing, verify the storage backend and retention behaviour of snapshots on your own instance, confirm whether your reverse proxy setup can supply the remote_user_header value Omnom expects, and check the AGPL-3.0 obligations if you plan to expose a modified Omnom to other users.

Official sources

  1. asciimoo/omnom on GitHub
  2. License: AGPL-3.0
  3. Project website
  4. README
  5. Releases
Community notes

Community notes