Development
Setup
make install runs uv sync for Python and npm ci for the frontend toolchain.
Common tasks
make on its own lists every target:
| Target | Purpose |
|---|---|
make dev |
Serve with auto reload and debug logging. |
make assets |
Collect the Tabler stylesheet and vendored scripts. |
make test |
Run the tests with coverage. |
make test-all |
Run the tests on every supported Python version. |
make lint |
Ruff lint and format check. |
make format |
Apply autofixes and format. |
make check |
Everything CI runs. |
make docs |
Serve this documentation locally. |
make docs-build |
Build the documentation as CI does, with --strict. |
make image |
Build the container image. |
Layout
vaultr/
├── app.py # application factory, middleware, lifespan
├── config.py # config.yml schema and passphrase resolution
├── vault.py # encrypt / re-encrypt service, the security critical core
├── mcp_server.py # MCP tools, mounted at /mcp
├── settings.py # environment driven settings
├── security.py # bearer token authentication
├── dependencies.py # shared FastAPI dependencies
├── schemas.py # API request and response models
├── routes/ # api, web and health routers
├── templates/ # Jinja2 templates
└── static/ # generated: built by `make assets`
assets/ # source: custom.css and app.js
tests/
docs/
vaultr/static/ is generated and not in version control. Run make assets after a
fresh clone, or the UI comes up unstyled.
Frontend
The UI is built with Tabler, a Bootstrap 5 component framework.
Tabler ships compiled CSS, so there is no build step: make assets copies
tabler.min.css, Tabler's theme script and htmx out of node_modules into
vaultr/static/, alongside assets/custom.css and assets/app.js.
Because nothing compiles, there is no class scanning and no purge: every Tabler class is available in the templates without registering it anywhere.
Theme switching
Tabler themes on a data-bs-theme attribute on <html> and ships
tabler-theme.min.js to manage it. That script reads the tabler-theme localStorage
key, falls back to the system preference, and also honours a ?theme= query
parameter. It is loaded synchronously right after <body>, which is what Tabler
requires; deferring it makes the page flash the light theme before switching.
The navbar shows one of two icons, using Tabler's hide-theme-dark and
hide-theme-light helpers so only the switch for the inactive theme is visible. The
links carry ?theme= so they still work if app.js fails to load; app.js
intercepts the click, sets the attribute and stores the choice, which avoids a
navigation and keeps an encryption result on screen.
Icons are Tabler outline SVGs inlined as Jinja partials under
vaultr/templates/partials/icons/. Nothing is fetched from a CDN, which the Content
Security Policy also enforces.
Tests
The suite covers configuration parsing, encryption, both route layers, settings and
token comparison. Encryption tests decrypt their output with a plain Ansible
VaultLib rather than asserting on fixed ciphertext, so they verify real
compatibility instead of an implementation detail.
Releasing
Version lives in vaultr/version.py.
- Bump it and update the changelog.
- Tag the commit
vX.Y.Zand push the tag. - CI builds and pushes
ghcr.io/ngine-io/vaultr:X.Y.Zand:X.Y. make releasepublishes to PyPI.
Documentation
make docs serves this site locally on port 8001. Pull requests build it with
--strict, so a broken link or an unknown setting fails the check before merge.
Pushing to main publishes it to https://ngine-io.github.io/vaultr/ through the
Docs workflow, which uploads the built site as a Pages artifact and deploys it with
GitHub's own Pages actions. There is no gh-pages branch, and no local publish step:
merging is what publishes.