Skip to content

Repository files navigation

Orthonaut

Orthonaut is a self-hosted full-stack app to check Spanish orthography on Wikipedia pages.

Stack

  • Backend: Rust (axum, zspell, rusqlite)
  • Frontend: React + Tailwind CSS + Headless UI
  • Database: SQLite

Local development

make dev

Starts the backend on http://localhost:3000 and the frontend on http://localhost:5173 (the frontend proxies API calls to the backend). On first run it downloads the dictionaries and installs frontend dependencies automatically.

Configuration

Copy orthonaut.toml.example to orthonaut.toml and fill in your details:

wikimedia_contact = "https://es.wikipedia.org/wiki/User:<your-wikipedia-username>"

[oauth]
client_id     = "..."
client_secret = "..."
redirect_uri  = "http://localhost:5173/api/auth/callback"
token         = "..."  # optional: skip OAuth flow locally

orthonaut.toml contains secrets and is not committed to git — it must be created manually wherever the app runs (see the Toolforge section below).

Word lists: local files vs. Wikipedia

Orthonaut keeps two word lists — valid words (false positives to suppress) and always-wrong words (always flagged). By default both live in local files (suppressions.txt / always_wrong.txt) and the UI lets you manage both, exporting each to its file.

Set the optional wordlist_page key to back the lists with a Wikipedia page instead (Replacer-style), recommended for production:

wordlist_page = "Usuario:Jmlarraz/Orthonaut/Palabras"

When wordlist_page is set:

  • Both lists are read from that page at startup. The "add wrong words" input is hidden — the always-wrong list is managed entirely on-wiki.
  • The "Valid word" button still works; new valid words are buffered locally until you click Export valid words to Wikipedia, which requires you to be logged in (the edit is made with your account).
  • Export re-reads the page and merges before writing, so words anyone added to the page manually are never lost. Only the valid-words block is written; the always-wrong block is left untouched.

Create the page with free text plus the two sentinel blocks below (markers must match exactly, one lowercase word per line; anything outside the markers is ignored and safe for docs):

<!-- ORTHONAUT:VALIDAS:START -->
superestrella
<!-- ORTHONAUT:VALIDAS:END -->

<!-- ORTHONAUT:INCORRECTAS:START -->
concecuencia
<!-- ORTHONAUT:INCORRECTAS:END -->

Reloading the word lists from Wikipedia

When running in Wikipedia mode (wordlist_page is set), the app reads both word lists from that page at startup. To force it to re-read the valid and always-wrong words from Wikipedia without restarting, call /api/wordlists/reload. It accepts both GET and POST, so you can trigger it from a plain link (e.g. a "reload" button on the wiki page that opens the URL in a new tab) as well as with a scripted POST. The endpoint re-fetches the page and swaps the live lists in place, treating the wiki page as the source of truth: after a reload a word is valid iff it is currently on the page. Any word added in-app but not yet exported is discarded on reload.

Open in a browser or:

curl https://orthonaut.toolforge.org/api/wordlists/reload

Locally:

curl http://localhost:3000/api/wordlists/reload

It returns the new list sizes, e.g. {"valid":1234,"wrong":56}. In local-file mode it responds with 400 (word lists are only reloadable in Wikipedia mode).

Stats endpoint

The Stats tab is hidden from the UI for now, but the backend endpoint that powers it stays available. GET /api/stats returns a per-user leaderboard of how many edits each Wikipedia account has made through Orthonaut:

curl https://orthonaut.toolforge.org/api/stats

It responds with a JSON array, e.g. [{"username":"Jmlarraz","edit_count":42}, ...].

Paths and the port can be overridden with environment variables:

Variable Default (debug / release) Description
PORT 3000 / 8000 Port the backend listens on
ORTHONAUT_CONFIG_PATH $HOME/orthonaut.toml Config file path
ORTHONAUT_DB_PATH $HOME/orthonaut.db SQLite database path
ORTHONAUT_DICT_DIR $HOME/dictionaries Dictionary / word-list directory

Deploying to Toolforge

The app runs at https://orthonaut.toolforge.org/. A single Rust binary serves both the API and the React frontend. The Hunspell dictionaries and the compiled frontend are embedded in the binary at build time, so the only thing that must exist on Toolforge is orthonaut.toml.

The frontend is embedded from frontend/dist/, which is not committed on main. A GitHub Actions workflow (.github/workflows/deploy.yml) builds it on every push to main and publishes deploy = main source + built frontend/dist to the deploy branch. Toolforge builds its image from that branch (toolforge build start <repo> --ref deploy), so you never rebuild or commit frontend/dist/ by hand. The same workflow then runs ./toolforge.sh build + ./toolforge.sh restart on the bastion, so a push to main deploys end-to-end.

First-time setup (once)

SSH into the bastion and switch to the tool account:

ssh -i ~/.ssh/<your-key> <your-username>@login.toolforge.org
become orthonaut

1. Create the config file manually. It is not in the repo, so write it directly:

cat > ~/orthonaut.toml << 'EOF'
wikimedia_contact = "https://es.wikipedia.org/wiki/User:<your-wikipedia-username>"
wordlist_page     = "Usuario:Jmlarraz/Orthonaut/Palabras"
EOF

wordlist_page makes production read/write the word lists from that Wikipedia page (see Word lists above); omit it to use local files.

To enable Wikipedia editing, add an [oauth] section with a redirect_uri of https://orthonaut.toolforge.org/api/auth/callback (register it first at the Meta-Wiki OAuth consumer registration). Without it, the app still runs — only editing is disabled.

2. Point the app at the config and data on NFS (inside the container $HOME is not the tool home, so absolute paths are required; these persist across deploys):

toolforge envvars create ORTHONAUT_CONFIG_PATH /data/project/orthonaut/orthonaut.toml
toolforge envvars create ORTHONAUT_DB_PATH /data/project/orthonaut/orthonaut.db
toolforge envvars create ORTHONAUT_DICT_DIR /data/project/orthonaut/dictionaries

3. Fetch the toolforge.sh script onto the bastion. The Toolforge build produces a container image from GitHub and never writes to the bastion, so the script has to be placed here separately (make is not installed on the bastion either). Its commands only call the toolforge CLI, so the full repo isn't needed — just the single file:

curl -sL https://raw.githubusercontent.com/JavierMonton/orthonaut/main/toolforge.sh -o ~/toolforge.sh
chmod +x ~/toolforge.sh

4. First build and start. With the script in place you can use the shortcuts:

./toolforge.sh build
./toolforge.sh start

./toolforge.sh start runs toolforge webservice buildservice start --mount all --mem 2Gi --cpu 1: --mount all keeps the tool's NFS storage mounted; --mem 2Gi is required because building the Hunspell dictionary at startup exceeds the default 512Mi limit (otherwise the container is OOMKilled into a crash loop). A healthy start logs backend listening on 0.0.0.0:8000.

Updating

Push your changes to main — that's it. GitHub Actions builds the frontend, publishes the deploy branch, then rebuilds and restarts the Toolforge webservice. The run fails if any step does, so a green check on the commit means the new version is live.

To deploy manually instead, run from the bastion home:

./toolforge.sh build     # rebuild the image from the `deploy` branch on GitHub
./toolforge.sh restart   # roll out the new image

The build command targets the deploy branch, so wait for the publish-deploy job on your push to finish before running it (otherwise you'll rebuild the previous frontend).

If you changed toolforge.sh itself, re-run the curl from step 3 first to refresh it on the bastion.

Other shortcuts: ./toolforge.sh logs, ./toolforge.sh stop, ./toolforge.sh start. Run ./toolforge.sh help for the full list.

About

Orthonaut is an assisted-editing tool that detects Spanish orthography issues on Wikipedia articles and helps editors fix them with search-and-replace.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages