Skip to main content

avallon

A place where notes are kept, and found again.

A personal note site: every page is a Markdown file in a directory tree that encodes both its URL and its taxonomy. Pages cite each other with [[wikilinks]] and show their backlinks, group into dossiers, and are found through faceted browsing or full-text search. Notes live in their own git repository, apart from this tool, and sync across devices on their own.

The content model, and the reasoning behind it, lives in docs/model.md. This README is the user guide.

Status. v0.1.0. What comes next, and why, is in ROADMAP.md.

How it works in one picture

your machine                              git remote (e.g. GitHub)
------------                              ------------------------
avallon serve  --> reads/writes the notes repo
                   |
                   +--> commit (instant, batched)
                        |
                        +--> [detached] pull + push  ------------> origin
                                                                     ^
another device: the running server polls  <-- pull ----------------- +
  • One directory per page, holding index.md and its images.
  • Instant local commit on every save, network sync in the background.
  • The tool and the notes are two repositories. Upgrading one never touches the other.

Requirements

  • Python 3.11+
  • git
  • pandoc for the Word/PDF export
  • fzf for the interactive pickers
  • ripgrep (optional): full-text search falls back to a pure-Python scan without it

pandoc and fzf are not Python packages. The supported install is through pixi, which brings them along.

Install

pixi global install avallon      # brings pandoc and fzf
pip install avallon              # Python parts only, see Requirements

Setup a notes repository

The notes live in their own git repository, separate from this tool. Point avallon at it once:

avallon init ~/notes                          # local path
avallon init git@github.com:you/notes.git     # or a clone URL

init creates the layout, makes the repository active, and remembers its path in ~/.config/avallon/config.toml. The created repository looks like:

notes/
├── taxonomy.toml          # the declared domains and types
└── <domain>/<type>/<slug>/
    ├── index.md
    └── figure.png         # assets sit next to the page that uses them

init and repo are create-or-validate:

Target What happens
Path does not exist mkdir + git init + full scaffold
Existing directory, not a git repository git init + scaffold what is missing
Existing repository, already conformant adopted as is
Existing repository with unrelated content asks for confirmation first
A clone URL cloned into ~/<repo-name>, then validated

Switch repositories

avallon repo                 # print the active notes repository
avallon repo ~/other-notes   # switch to another one (same rules)

Pages

A page is a directory holding an index.md. Its path is both its URL and its taxonomy:

content/<domain>/<type>/<slug>/index.md   ->   /<domain>/<type>/<slug>/
  • domain: what the page is about (informatique, administratif, ...). A subject, never a context: "work" or "personal" is a tag, because a page can be both.
  • type: what the page is (fiche, cr, tutoriel, recueil).

Both are declared in taxonomy.toml, and a page cannot be born under an undeclared pair. Extend the vocabulary with avallon add-domain / avallon add-type, check the tree with avallon check.

Frontmatter

---
title: Page title
date: 2026-07-01        # creation, set once
updated: 2026-07-08     # last change, stamped on commit
tags: [smartg, flux]
summary: One sentence, shown on the home page.
project: recours-batterie   # slug of the page indexing the dossier (optional)
status: en cours            # en cours | terminé | abandonné (optional)
visibility: private         # local only, never served elsewhere (optional)
---

Dossiers

A dossier is not a new kind of object: it is an ordinary page, the one that introduces it, and its pages name it in their project:. The site derives the rest, so nothing has to be kept up to date by hand:

  • the dossier's page lists its own pages, grouped by type;
  • the sidebar browses the dossier instead of the whole tree while you are in it;
  • each page names the dossier it belongs to, and search shows it too.

Titles can therefore stay short ("Mail retour"): the dossier places them.

Belonging is not citing. A page enters a dossier when it will be archived with it; a durable note the dossier merely cites stays outside and is linked with [[…]], so it outlives the dossier it was written during.

Writing

Standard Markdown, plus:

Syntax What it does
[[slug]], [[slug|label]] link another page, by slug, path or title
[[figure.pdf]] link a file sitting next to the page
```python syntax-highlighted code (Pygments)
!!! note / !!! warning admonition cards
$…$, $$…$$ LaTeX math (MathJax)
{rouge}(texte) inline coloured span
```gallery, ```plot, ```csv, ```query content blocks
- [ ] task lists

Every page shows what cites it, so a note is never a dead end.

Commands

Command What it does
avallon serve Run the site (see Server).
avallon init <path|url> Initialize or adopt a notes repository, make it active.
avallon repo [path] Print or switch the active repository.
avallon new Scaffold a page (domain and type picked from the taxonomy).
avallon move <page> Re-file a page under another domain/type.
avallon sync Pull, commit, push. --local commits without the network.
avallon export <page> Export a page to .docx or .pdf (needs pandoc).
avallon add-domain <name> Extend the taxonomy.
avallon add-type <name> Extend the taxonomy.
avallon check Verify every page sits under a declared domain/type.
avallon stamp Fill missing date: / updated: in the frontmatter.
avallon setup Write the deployment env file (address, port, token).
avallon install Install and start the systemd user unit.

Server

Quick start (local)

avallon serve                 # 127.0.0.1:8000
avallon serve --port 8800

The browser is also the editor: Ctrl+E opens the raw Markdown of the page, saving writes the file and commits it. Ctrl+K opens the command palette, Ctrl+N creates a page. What you type is re-rendered live.

Access token

Binding anywhere other than loopback requires a token:

AVALLON_TOKEN=$(openssl rand -hex 32) avallon serve --host 10.8.0.2

The guard is a no-op when no token is set, which is only safe on loopback, so the command refuses any other bind without one. The token is the second layer, behind the network boundary: it is what stands between your notes and any other device that can reach the port.

Deploy (systemd + wireguard)

avallon setup      # write the env file, generate a token
avallon install    # install and start a systemd user unit

The unit binds the address given at setup, and the notes repository is polled on a timer so pages written on another device show up without a restart.

Configuration

Variable Default What it does
AVALLON_CONTENT_DIR the configured repository Override the notes location.
AVALLON_TOKEN (none) Bearer token required for every request.
AVALLON_HOST / AVALLON_PORT 127.0.0.1 / 8000 Bind address.
AVALLON_SHOW_PRIVATE on in debug Serve pages marked visibility: private.
AVALLON_SYNC_WINDOW 900 Seconds during which consecutive edits fold into one commit.
AVALLON_POLL_INTERVAL 120 Seconds between two pulls; 0 disables the poller.
AVALLON_ALLOWED_HOSTS loopback Comma separated names the site answers to.
AVALLON_SECRET_KEY (generated per process) Django secret key. avallon setup writes a fixed one.
AVALLON_DEBUG 0 Debug mode. Never on when exposed.

Sync model

Every write commits immediately and locally, then a detached process pulls and pushes. A save never waits on the network, and being offline only costs a warning.

Commit batching. Consecutive edits fold into a single commit for AVALLON_SYNC_WINDOW seconds, so a page written in ten passes does not leave ten commits behind. Only commits the tool made itself are amended, recognized by their trailer.

Polling. A long-running server is one more git writer among the devices, so it pulls on a timer to reflect what was written elsewhere.

Development

pixi run serve        # the site, with live reload
pixi run fmt          # ruff format
pixi run lint         # ruff check
pixi run type-check   # mypy
pixi run test         # pytest + coverage
pixi run all          # the four above

Releasing

Version numbers live in pyproject.toml. Tagging vX.Y.Z and pushing the tag triggers the release workflow, which builds the sdist and the wheel and publishes them to PyPI through Trusted Publishing (OIDC), so no token is stored anywhere.

git tag v0.1.0 && git push origin v0.1.0

License

Apache License 2.0, see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

avallon-0.1.0.tar.gz (1.5 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

avallon-0.1.0-py3-none-any.whl (1.5 MB view details)

Uploaded Python 3

File details

Details for the file avallon-0.1.0.tar.gz.

File metadata

  • Download URL: avallon-0.1.0.tar.gz
  • Upload date:
  • Size: 1.5 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for avallon-0.1.0.tar.gz
Algorithm Hash digest
SHA256 3fc6fdc7d84a6705bb35613a4ac6b935ce67f772aac3df81346fa50f9bf83d14
MD5 e5260ba36751599474ef34fe3a0283ad
BLAKE2b-256 98b6a074c175f724cff9b19edac71ba29614f86ddf57774117dfc241f4307363

See more details on using hashes here.

Provenance

The following attestation bundles were made for avallon-0.1.0.tar.gz:

Publisher: release.yml on walcark/avallon

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file avallon-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: avallon-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 1.5 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for avallon-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 495050fde752698e008654ae8d6ff7d514961092c7dec41989f51520ef210345
MD5 94fa2e7e935d32dbf9176698ce4bb517
BLAKE2b-256 7fdc958ea1e8df1ea44b643592d9d1fc5990bfb928da8a7ee8fe143857c9382e

See more details on using hashes here.

Provenance

The following attestation bundles were made for avallon-0.1.0-py3-none-any.whl:

Publisher: release.yml on walcark/avallon

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page