How this website is built¶
This is the source code for the new Montreal Mesh website, currently hosted at https://lora.reseaulibre.ca and backed by Codeberg at https://codeberg.org/reseaulibre/lora-reseaulibre-ca/.
This README file documents the git repository and how to make changes
to the site. The actual site contents are in the docs/ directory of
the git repository or on lora.reseaulibre.ca.
Mirrors¶
The git repository is primarily hosted on Codeberg, but has multiple mirrors:
- Codeberg: https://codeberg.org/reseaulibre/lora-reseaulibre-ca/
- sourcehut: https://git.sr.ht/~anarcat/lora.reseaulibre.ca
- GitLab: https://gitlab.com/reseaulibre/lora.reseaulibre.ca
- GitHub: https://github.com/ReseauLibre/lora.reseaulibre.ca
Those are synchronized automatically on push in Codeberg or, if Codeberg is unavailable, manually.
Contributing¶
If you want to participate here, agree with the Code of Conduct
(Contributor Covenant 3.0 Code), and edit the files in the
docs/ directory which should bring you into a pull request
workflow.
💡 Tip
That can be done through the web interface directly, even though the linked documentation above doesn't make that obvious. The documentation seems to favor a local, git-based workflow which is more complicated, but also supported.
Once the request is approved, your changes will go live. Changes take a "few minuets" to show up, see this troubleshooting section otherwise.
The site was originally built on mkdocs-material but eventually switched to Zensical, then back to mkdocs-material because blog (fixed) and RSS support are missing.
See the authoring guide for more information on formatting the contents.
Copyright¶
The content of this repository is available under the Creative Commons Attribution-ShareAlike 4.0 International license, and so will be your contributions:
💡 License
You are free to:
- Share — copy and redistribute the material in any medium or format for any purpose, even commercially.
- Adapt — remix, transform, and build upon the material for any purpose, even commercially.
- The licensor cannot revoke these freedoms as long as you follow the license terms.
Under the following terms:
- Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
- ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
- *No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
Translations¶
Translations used to be made with the mkdocs-static-i18n plugin. That approach has been abandoned because it conflicts with the blog plugin (see upstream issues on both projects). We have therefore adopted a "two-site" approach where each language is its own site.
This is done by having the translation in separate
branches. Concretely, we only translate to "French" right now, which
lives in the fr branch. The way this works is by merging the main
branch into the fr branch whenever we update the main branch. This
is a clunky, manual process, but given we don't have many
translations, it feels like a good deal.
What this implies is that updates to existing pages will necessarily result in conflicts in the translation. That's a feature: one needs to know when a section needs an update.
Note that the translation branch has a copy of all the files here,
even though a lot of those are superfluous (like the
.woodpecker.yaml file). The mkdocs.yml is relevant, however, and
differs between the two because the site_url needs the fr suffix.
Translating pages¶
-
First pull the repository to have all branches up to date:
git pull -
Switch to the translation branch:
git switch fr -
Update it with the main branch:
git merge mainIf this is an update on an existing page, this will result in a merge conflict. Don't panic.
-
Translate a given page:
$EDITOR docs/foo.mdIf this is an existing page, you will need to resolve the merge conflict. See the Codeberg merge conflict instructions for a tutorial. When the conflict is resolved, you mark the file as resolved with:
git add docs/foo.md -
Commit the result and push:
git commit -m'translate page foo' git push
The last step will require you to setup a remote to your own fork if you don't have access to the repository.
Structure¶
The documentation is structured partly following the Diataxis method where we separate introduction and in-depth material in separate sections. We try to have at least a section (the guides, but also the "about" menu) that assume as little prior knowledge as possible, in opposition to the more in-depth and "dump everything there" references section.
In guides, instructions are clear, direct, quick and to the point.
Note
Some instructions can diverge from that path, but they marked clearly with an admonition, like here.
Links and navigation¶
The navigation menus are maintained by hand in the mkdocs.yml
file. The build will warn if a file is added to the repository without
being added to the nav dictionary there. Heed those warnings and
properly add files to the structure. An exception is the blog posts
which don't need to be individually added.
Each page should somehow be reachable from the front page, but not necessarily as a direct link. Each section should be linked there, and then each page should be listed in each section.
It's a bit cumbersome, but it makes it easier to find pages when navigation is less visible, for example on mobile or other renderings of the site. (For example, the Reticulum Micron rendering doesn't replicate the navigation menus at all and can only rely on in-page navigation.)
Link checks¶
The checklinks job uses the Lychee link checker to check the
site for dead links.
Checks sometimes fail because of transient errors like:
[ERROR] https://lora.reseaulibre.ca/fr/blog/archive/2026/ | Network error: HTTP/2 protocol error. Server may not support HTTP/2 properly (error sending request for url (https://lora.reseaulibre.ca/fr/blog/archive/2026/)): HTTP/2 protocol error. Server may not support HTTP/2 properly
Rerunning the pipeline fixes this issue. I assume this is a problem internal to Codeberg pages, but I haven't debugged the issue any further.
Links truly being mismatched by Lychee can be added to the
.lycheeignore file.
The link checker does not enforce the constraint of linking every section from the front page and section pages mentioned in the previous section, but it should.
Spell checking¶
Good old Aspell is used to check spelling in English and French.
To fix false positives found by Aspell, install the aspell package
and run the command recommended by CI, which should be something like:
aspell --mode=markdown --lang=fr --home-dir=. --personal=aspell.fr.pws --encoding=utf-8 foo.md
... where foo.md is the file with a problem. It will run a text
interface that will allow you to correct or accept the words.
Alternatively, you can add the word to the list in aspell.fr.pws
following the peculiar file format. Essentially, you need to add
the word on its own line and increment the line count on the first
line.
Finally, if you use backquotes around a word, it will get excluded
by the parser, which can be useful to bypass certain words marked as
failures. This is particularly useful for project names that won't get
reused or can't be added because they have a separator not recognized
by Aspell. For example, Meshtastic is in our dictionary so it doesn't
need to be quoted, but not grebedoc.dev, which does need back
quotes.
(Astute readers will notice that backquotes itself needs back
quotes, as it's an incorrect spelling, according to Aspell. Capitals
matter as well: aspell is a typo, but not Aspell, of course.)
CI build workflow details¶
This section explains how the site is built. You don't need to read this unless you want to debug the continuous integration (CI) process or website build.
The way this is setup is rather convoluted because we need to have a custom domain and this is still not yet well supported by the new "git pages" and "actions" in Codeberg. So, essentially, it works like this:
- on push, Codeberg somehow notifies https://ci.codeberg.org which is Woodpecker CI instance
- woodpecker pulls the git repository, builds the site, and commits the
result to the
pagesbranch - woodpecker pushes the branch back to Codeberg
- code berg fires off a webhook to publish the site to git pages
First setup¶
To set this up, I had to first follow the manual pushing guide:
- build the site on the
mainbranch - create an "orphan"
pagesbranch (git switch --orphan pages) for the site - add and commit the
sitedirectory, but to the root of the repository - setup a webhook to the legacy v2 pages on push (update: now a webhook to Grebedoc instead)
- push the
pagesbranch
At this point, anarcat.codeberg.page/lora-reseaulibre-ca is
online. Next up was to setup the custom domain:
- add a CNAME for
lora.reseaulibre.caat my registrar, pointing atlora-reseaulibre-ca.anarcat.codeberg.page.(update: now pointing atgrebedoc.dev. - add
lora.reseaulibre.cato the.domainsfile in the git repository, on themainbranch - push the main branch
At this point lora.reseaulibre.ca is online. The next step was to
use Codeberg CI to build and publish the site automatically:
- enroll into Codeberg CI, see issue 1570
- login to https://ci.codeberg.org
- add the project to Woodpecker
- create an access token for Woodpecker in Codeberg
- add the access token to a
codeberg_tokenvariable in the Woodpecker project - create a new
mailvariable with some dummy email on my domain in the woodpecker project - push a trivial change to a markdown file
At this point, changes to the repository automatically rebuild and publish the changes.
Switch to Grebedoc¶
As of 2026-05-26, we have switched to Grebedoc because we hope it
will be faster than Codeberg pages. It also provides redundancy: if
Codeberg fails, Grebedoc should survive and vice versa, which gives us
better redundancy. It also shows us how we can host this anywhere we
can run [git-pages][], which can be any virtual machine we spin up
in case of a catastrophe, see also below for Alternatives.
We setup those two DNS records:
_git-pages-repository.lora IN TXT https://codeberg.org/reseaulibre/lora-reseaulibre-ca.git
_git-pages-challenge.lora IN TXT 6697376e11b3ff01b4f4ab83956c2742f065e60239f3e0bdb78caf73f9624cab
The first tells the git-pages software which repository is an
acceptable source for the site.
The second is the result of:
printf "$DOMAIN $GIT_PAGES_PASSWORD" | sha256sum
where domain is lora.reseaulibre.ca. The password is stored in my
password manager as grebedoc.dev.
It allows for pushing arbitrary content to the site.
A first push is done with:
curl https://grebedoc.dev/ -X PUT -H "Host: lora.reseaulibre.ca" -H "Authorization: Pages $GIT_PAGES_PASSWORD" --data "https://codeberg.org/reseaulibre/lora-reseaulibre-ca.git"
The Authorization header might not be necessary since we're passing
the git repository URL here.
This will fail if the DNS has not propagated yet.
This also fails, perhaps for a different reason:
export GIT_PAGES_PASSWORD
git-pages-cli --server https://grebedoc.dev --upload-dir . http://lora.reseaulibre.ca/
Unclear.
The --password "$GIT_PAGES_PASSWORD" is implicit as it looks for the
GIT_PAGES_PASSWORD environment, see the git-pages-cli README file.
Once a first push has been made, we can switch over by changing DNS to:
lora IN CNAME grebedoc.dev.
Then a new webhook need to be added following those instructions, essentially:
Select repository > Settings > Webhooks > Add webhook > Forgejo, then configure only the following:
- Target URL:
http://lora.reseaulibre.ca- Branch filter:
pages- Authorization header:
Pages {password}(Method B only)Leave everything else at the default values and select
Add webhook.
Then this can be tested by pushing to the pages branch, which can be
done by doing a regular commit on the site, or on the pages branch
of course.
The CI configuration is actually unchanged.
Alternatives¶
We should probably hook this onto Forgejo Actions and the
git-pages action, instead. The guide for that explicitly
says it does not work for custom domains, although that might now be
inaccurate, since we've published the site on a git-pages back-end
(Grebedoc) without problems since 2026-05-26.
We're in the process of migrating to Codeberg actions. We've had trouble with the cache and artifacts action which both need node, so for now it reuses the git-based caching logic used by Woodpecker. We've also found the actions pipeline to be much slower than the Woodpecker pipelines, so much so that it would timeout and, ultimately, made migration impossible, so we're still on Woodpecker.
There was a downtime on Codeberg on 2026-03-04 that cause the site to go down almost entirely for a full 24 hours.
If this happens again, we can consider hosting the static site
somewhere else. I was recommended statichost.eu (see this
guide) or Grebedoc ("Codeberg" backwards). As of 2026-05-26,
we've switched to Grebedoc.
Updates to those sites can be posted even without Codeberg being
available, through any [git-pages][] compatible hosting provider,
see above.
We also use the cache branch to carry around the Lychee cache. This
could be fixed if Woodpecker supported caches or with a Forgejo
"cache" or artifacts action. Both actions require a Node
installation and are not compatible with many images.
Matrix commit bot¶
Moved to our Matrix guide.
Reticulum publishing¶
Efforts are under way to publish the site as a Nomadnet Micron site, which would make it accessible natively under Reticulum. The goal is to rebuild the site into Micron pages at every push, through continuous integration.
So far two separate experiments have started:
-
an incomplete Pandoc output format: this has mostly stopped because Pandoc is much stricter than
mkdocsin the Markdown format it accepts, and fundamentally renders the site differently. We also haven't implemented all the endpoints yet, and have lost focus because of... -
the
md2muconverter which reuses the Markdown parser Mark wrote forrngitand, while it is not a correct (or even complete) Markdown implementation, it currently renders better than the above
The correct way to regenerate the Micron site looks like this:
cd docs/
python ~/bin/md2mu.py . -d ~/Projects/nomadnet-pages/
Then point nomadnet at the /home/anarcat/Projects/nomadnet-pages
directory. And no, nomadnet does not support the ~ expansion, so
it really needs to be the absolute path there.
The Reticulum version assumes every page is accessible without the navigation, as the conversion doesn't currently replicate the navigation menus (and, perhaps, shouldn't).