* feat(translate): offline translation for the Information Library Implements #1272. Ships as a Supply Depot app so users who do not need translation never see it. A path-preserving reverse proxy in front of kiwix-serve. /viewer, /skin/*, /search, /random and /catalog/* are forwarded byte for byte; only text/html under /content/ is rewritten. Preserving the path is the whole design: viewer.js keeps the content iframe in sync with location.hash by comparing contentWindow.location.pathname, so moving the path starts the ping-pong its own comment warns about and forces us to patch Kiwix's JavaScript on every Kiwix bump. Because the path never moves, no Kiwix JS is modified, and Kiwix's header, search, library and random keep working because they are Kiwix. Language is a cookie, so links inside the ZIM need no rewriting either. Bergamot rather than the AI Assistant: measured on the same box and the same passage, 12,818 words/s on CPU against llama3.1:8b at 8 words/s on an RTX 5060. It is also more careful with names, where the LLM translated the product name and a smaller model hallucinated a clause. No GPU, so it works on CPU-only boxes where the LLM path is unusable. Models come from Firefox Remote Settings, the endpoint Firefox itself uses, not the mozilla/firefox-translations-models repo, which was archived on 2026-08-21. MPL-2.0, about 37 MB per direction. Failure handling is deliberate throughout: a failed model fetch is non-fatal, a translation error returns the original article with a comment rather than an error page, and a box with no models still starts and still forwards the library. Translation being unavailable must never make the Information Library unreachable. amd64 only, and pinned as such in the build workflow. Bergamot's intgemm backend is x86 specific and there is no aarch64 wheel, so a multi-arch build would produce an ARM image that pulls cleanly and dies at start. Also adds .gitattributes pinning *.sh to LF. The repo stores LF but Windows checks out CRLF, and a CRLF after a shebang makes the interpreter unresolvable inside the image. CI builds on Linux so this never bit, but it breaks a local image build and would have bitten the existing sidecar script too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F8Mgg1vd2eKSs8StZiuyMV * fix(translate): vary the cache on the language cookie Found by clicking the language buttons in a browser rather than trusting curl. Kiwix sends ETag and Cache-Control: max-age=3600, and the proxy forwarded both unchanged on a response whose content now varies by cookie. The browser cached the English article for an hour and served it straight back when the reader picked a language, so the feature looked like it simply did not work. A shared cache would have been worse, serving one reader's language to another. Translatable responses now send Vary: Cookie, and their ETag is namespaced by language so revalidation still works per language rather than being turned off. Forwarded paths are untouched, so /viewer, /skin/* and /catalog/* stay byte for byte identical to Kiwix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F8Mgg1vd2eKSs8StZiuyMV * fix(translate): move off port 8480, which Vaultwarden already uses Caught by seeing the two cards side by side in Supply Depot with the same port. My port survey grepped ui_location for a bare number, and Vaultwarden's is 'https:8480' because it needs an https Open link, so it did not match and 8480 looked free. Moved to 8460, which fits the existing spacing between Meshtastic Web on 8450 and Homebox on 8470. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F8Mgg1vd2eKSs8StZiuyMV * fix(translate): pin the base image to python 3.10 The build fails outright on 3.12: bergamot publishes no wheel for it and pip resolves no further than 0.1.2. The spike used 3.10 for this reason and I had modernised it without checking. Caught by building the image rather than reading it. Moving off this pin means moving off the 2022 PyPI wheel to a build from mozilla/translations, which is the follow-up already noted in the Dockerfile. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F8Mgg1vd2eKSs8StZiuyMV * fix(translate): translate text that lives in a bare div Sotoki-generated StackExchange ZIMs put every question excerpt in a <div class="...excerpt">, so on a questions list the titles translated and the descriptions underneath them did not. Reported against the ham radio ZIM. div could not simply be added to BLOCK_TAGS: the outermost-block rule would claim a top-level layout wrapper and send the whole page as one job. It is now claimed speculatively and abandoned the moment anything block-level opens inside it, so a wrapper yields to the blocks it contains and only a genuine text leaf is translated. Also moves the test harness's reporting block back to the end of the file. New checks had been appended after it, so they executed but their failures were never reported: verified that removing div from the set now fails 3 checks, where before it reported success. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F8Mgg1vd2eKSs8StZiuyMV * fix(translate): decode numeric character references before translating Bergamot's HTML mode decodes named entities (`&` arrives as "&") but not numeric ones. It treats `'` as literal text and escapes the ampersand on the way out, so "If I'm in the wilderness" came back as "Si I&#x27;m dans la nature" and rendered the escape sequence visibly to the user. Confirmed by round-tripping fragments through the model directly. Numeric refs are now decoded before the round trip. `&`, `<` and `>` are deliberately left encoded: decoding those would inject real markup into the fragment and fail the balance check that gates HTML mode, dropping the block to plain text and losing its inline links. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F8Mgg1vd2eKSs8StZiuyMV --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .github | ||
| admin | ||
| collections | ||
| install | ||
| .dockerignore | ||
| .gitattributes | ||
| .gitignore | ||
| .releaserc.json | ||
| CODE_OF_CONDUCT.md | ||
| CONTRIBUTING.md | ||
| Dockerfile | ||
| FAQ.md | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| SECURITY.md | ||
Project NOMAD is a self-contained, offline-first knowledge and education server packed with critical tools, knowledge, and AI to keep you informed and empowered — anytime, anywhere.
Installation & Quickstart
Project NOMAD can be installed on any Debian-based operating system (we recommend Ubuntu 26.04 LTS; 24.04 LTS and Debian 12 are also supported). Installation is completely terminal-based, and all tools and resources are designed to be accessed through the browser, so there's no need for a desktop environment if you'd rather setup NOMAD as a "server" and access it through other clients.
Before you start: NOMAD itself needs about 5 GB of disk and under 1 GB of RAM. The offline content you add afterwards is what actually fills a drive, and the AI Assistant is what drives the memory requirement up. See Device Requirements for both, and the FAQ for individual content sizes.
Note: sudo/root privileges are required to run the install script
Quick Install (Debian-based OS Only)
sudo apt-get update && \
sudo apt-get install -y curl && \
curl -fsSL https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/main/install/install_nomad.sh \
-o install_nomad.sh && \
sudo bash install_nomad.sh
Project NOMAD is now installed on your device! Open a browser and navigate to http://localhost:8080 (or http://DEVICE_IP:8080) to start exploring!
For a complete step-by-step walkthrough (including Ubuntu installation), see the Installation Guide. For Windows users, see the WSL2 install guide — community-supported path covering native Docker and Docker Desktop install routes.
Advanced Installation
For more control over the installation process, copy and paste the Docker Compose template into a docker-compose.yml file and customize it to your liking (be sure to replace any placeholders with your actual values). Then, run docker compose up -d to start the Command Center and its dependencies. Note: this method is recommended for advanced users only, as it requires familiarity with Docker and manual configuration before starting.
How It Works
NOMAD is a management UI ("Command Center") and API that orchestrates a collection of containerized tools and resources via Docker. It handles installation, configuration, and updates for everything — so you don't have to.
Built-in capabilities include:
- AI Chat with Knowledge Base — local AI chat powered by Ollama or you can use OpenAI API compatible software such as LM Studio or llama.cpp, with document upload and semantic search (RAG via Qdrant)
- Information Library — offline Wikipedia, medical references, ebooks, and more via Kiwix
- Education Platform — Khan Academy courses with progress tracking via Kolibri
- Offline Maps — downloadable regional maps via ProtoMaps
- Data Tools — encryption, encoding, and analysis via CyberChef
- Notes — local note-taking via FlatNotes
- System Benchmark — hardware scoring with a community leaderboard
- Supply Depot — a one-click app catalog (PDF tools, file browser, e-book library, password manager, and more) plus the ability to run your own custom Docker containers
- Automatic Updates — opt-in, hands-off updates for the core software, installed apps, and offline content, on a schedule you control
- Easy Setup Wizard — guided first-time configuration with curated content collections
NOMAD also includes built-in tools like a Wikipedia content selector, ZIM library manager, and content explorer.
What's Included
| Capability | Powered By | What You Get |
|---|---|---|
| Information Library | Kiwix | Offline Wikipedia, medical references, survival guides, ebooks |
| AI Assistant | Ollama + Qdrant | Built-in chat with document upload and semantic search |
| Education Platform | Kolibri | Khan Academy courses, progress tracking, multi-user support |
| Offline Maps | ProtoMaps | Downloadable regional maps for offline viewing and search |
| Data Tools | CyberChef | Encryption, encoding, hashing, and data analysis |
| Notes | FlatNotes | Local note-taking with markdown support |
| System Benchmark | Built-in | Hardware scoring, Builder Tags, and community leaderboard |
| Supply Depot | Built-in | One-click app catalog + bring-your-own custom Docker containers |
Device Requirements
While many similar offline survival computers are designed to be run on bare-minimum, lightweight hardware, Project NOMAD is quite the opposite. To install and run the available AI tools, we highly encourage the use of a beefy, GPU-backed device to make the most of your install.
At its core, however, NOMAD is still very lightweight. For a barebones installation of the management application itself, the following minimal specs are required:
Note: Project NOMAD is not sponsored by any hardware manufacturer and is designed to be as hardware-agnostic as possible. The hardware listed below is for example/comparison use only
Minimum Specs
- Processor: 2 GHz dual-core processor or better
- RAM: 4GB system memory (the whole stack without AI runs in under 1 GB)
- Storage: At least 5 GB free disk space (plan for 10 GB to install comfortably)
- OS: Debian-based (Ubuntu 26.04 LTS recommended)
- Stable internet connection (required during install only)
Adding the AI Assistant brings the install to roughly 25 GB, because Ollama and a general-purpose model are both large, and a model needs about its download size in memory while it answers. That memory comes from VRAM if you have a discrete GPU and from system RAM if you do not, which is the single biggest reason the figures below are so much higher.
To run LLMs and other included AI tools:
Optimal Specs
- Processor: AMD Ryzen 7 or Intel Core i7 or better
- RAM: 32 GB system memory
- Graphics: NVIDIA RTX 3060 or AMD equivalent or better (more VRAM = run larger models)
- Storage: At least 250 GB free disk space (preferably on SSD)
- OS: Debian-based (Ubuntu 26.04 LTS recommended)
- Stable internet connection (required during install only)
For detailed build recommendations at three price points ($150–$1,000+), see the Hardware Guide.
Again, Project NOMAD itself is quite lightweight — it's the tools and resources you choose to install with NOMAD that will determine the specs required for your unique deployment
Running AI models on a different host
By default, NOMAD's installer will attempt to setup Ollama on the host when the AI Assistant is installed. However, if you would like to run the AI model on a different host, you can go to the settings of the AI assistant and input a URL for either an ollama or OpenAI-compatible API server (such as LM Studio).
Note that if you use Ollama on a different host, you must start the server with this option: OLLAMA_HOST=0.0.0.0.
Ollama is the preferred way to use the AI assistant, as it has features such as model download that OpenAI API does not support. So when using LM Studio, for example, you will have to use LM Studio to download models.
You are responsible for the setup of Ollama/OpenAI server on the other host.
Frequently Asked Questions (FAQ)
For answers to common questions about Project NOMAD, please see our FAQ page.
About Internet Usage & Privacy
Project NOMAD is designed for offline usage. An internet connection is only required during the initial installation (to download dependencies) and if you (the user) decide to download additional tools and resources at a later time. Otherwise, NOMAD does not require an internet connection and has ZERO built-in telemetry.
To test internet connectivity, NOMAD first attempts to make a request to Cloudflare's utility endpoint, https://1.1.1.1/cdn-cgi/trace. If that endpoint is unreachable (for example, because your network blocks 1.1.1.1), it falls back to other endpoints the application already contacts (the GitHub API and the Project NOMAD API) and considers the connection online if any of them respond.
You can override the endpoint used for this check in two ways. The connectivity test URL can be configured from the UI under Settings → Advanced (stored locally on your instance), or you can set the INTERNET_STATUS_TEST_URL environment variable. When set, the environment variable always takes precedence over the UI-configured value. If neither is set, the built-in defaults above are used.
About Security
By design, Project NOMAD is intended to be open and available without hurdles — it includes no authentication. If you decide to connect your device to a local network after install (e.g. for allowing other devices to access its resources), you can block/open ports to control which services are exposed.
Will authentication be added in the future? Maybe. It's not currently a priority, but if there's enough demand for it, we may consider building in an optional authentication layer in a future release to support use cases where multiple users need access to the same instance but with different permission levels (e.g. family use with parental controls, classroom use with teacher/admin accounts, etc.). We have a suggestion for this on our public roadmap, so if this is something you'd like to see, please upvote it here: https://roadmap.projectnomad.us/posts/1/user-authentication-please-build-in-user-auth-with-admin-user-roles
For now, we recommend using network-level controls to manage access if you're planning to expose your NOMAD instance to other devices on a local network. NOMAD is not designed to be exposed directly to the internet, and we strongly advise against doing so unless you really know what you're doing, have taken appropriate security measures, and understand the risks involved.
Contributing
Contributions are welcome and appreciated! Please see CONTRIBUTING.md for guidelines on how to contribute to the project.
Testing Auto-Updates (Dry Run)
The Command Center can automatically install minor/patch updates of itself during a configurable window, after a cool-off period, and only when pre-flight checks pass (sufficient disk for the new image, no downloads or app installs in progress). Major versions always require a manual update.
Because exercising this logic with real version bumps is impractical, an Ace command runs the entire decision pipeline without ever triggering an update. Run it from the admin/ directory:
# 1) Deterministic scenario suite — no network, DB, or Docker required.
# Proves every branch (major-only, cool-off, prerelease/draft, window wrap, …)
# and exits non-zero on failure, so it's safe to wire into CI.
node ace auto-update:dry-run --scenarios
# 2) Simulate "what would happen if I were running 1.32.0 right now?"
# against the LIVE GitHub releases feed and real pre-flight checks:
node ace auto-update:dry-run --current=1.32.0 --force-enabled
# 3) Fully offline simulation with a canned release list and a fixed clock:
node ace auto-update:dry-run --current=1.32.0 --force-enabled \
--releases-file=./fixtures/releases.json --now=2026-06-04T21:00:00Z \
--window-start=20:00 --window-end=23:00 --cooloff=72 --skip-preflight
It prints the resolved decision — current version, whether the clock is inside the window, the eligible target (if any), and pre-flight blockers — ending in a clear verdict such as WOULD UPDATE → v1.33.2 or WOULD NOT UPDATE (outside-window): …. No real update is ever requested.
| Flag | Description |
|---|---|
--scenarios |
Run the built-in deterministic scenario suite and exit |
--current=<version> |
Simulate this currently-running version (e.g. 1.32.0) |
--force-enabled |
Treat auto-update as enabled, ignoring the saved setting |
--cooloff=<hours> |
Override the cool-off period |
--window-start=<HH:MM> / --window-end=<HH:MM> |
Override the update window |
--now=<ISO timestamp> |
Simulate the clock at a specific time |
--releases-file=<path> |
Use a local JSON releases array instead of fetching GitHub (offline) |
--skip-preflight |
Bypass the Docker/disk/queue pre-flight checks |
Community & Resources
- Website: www.projectnomad.us - Learn more about the project
- Discord: Join the Community - Get help, share your builds, and connect with other NOMAD users
- Benchmark Leaderboard: benchmark.projectnomad.us - See how your hardware stacks up against other NOMAD builds
- FAQ: FAQ.md - Find answers to frequently asked questions
- Community Add-Ons: admin/docs/community-add-ons.md - Third-party content packs built by the community
License
Project NOMAD is licensed under the Apache License 2.0.
Helper Scripts
Once installed, Project NOMAD has a few helper scripts should you ever need to troubleshoot issues or perform maintenance that can't be done through the Command Center. All of these scripts are found in Project NOMAD's install directory, /opt/project-nomad
Start Script - Starts all installed project containers
sudo bash /opt/project-nomad/start_nomad.sh
Stop Script - Stops all installed project containers
sudo bash /opt/project-nomad/stop_nomad.sh
Update Script - Attempts to pull the latest images for the Command Center and its dependencies (i.e. mysql) and recreate the containers. Note: this only updates the Command Center containers. It does not update the installable application containers - that should be done through the Command Center UI
sudo bash /opt/project-nomad/update_nomad.sh
Uninstall Script - Need to start fresh? Use the uninstall script to make your life easy. Note: this cannot be undone!
curl -fsSL https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/main/install/uninstall_nomad.sh -o uninstall_nomad.sh && sudo bash uninstall_nomad.sh