The Problem
Managing mTLS certificates for a home‑lab or small‑scale environment quickly becomes a manual, error‑prone process: rotating keys, tracking expirations, and keeping a consistent CA across services all require repetitive shell work and OpenSSL commands. Operators need a single, self‑hosted UI that stores secrets safely, automates ACME issuance, and integrates with existing identity providers.
What This Does
VaulTLS bundles a Rust backend (backend/src/*.rs) and a Vue/TypeScript frontend (frontend/src/*). The backend implements a REST API (backend/src/api.rs) for CRUD on CAs, TLS and SSH certificates, user accounts, and OIDC login. The frontend talks to that API through frontend/src/api/ApiClient.ts and related modules (certificates.ts, auth.ts, etc.). Database schema migrations live in backend/migrations/, and the encrypted SQLite store is initialized in backend/src/db.rs. Containerisation is provided by the top‑level Containerfile and a Docker‑Compose file under tests/docker-compose.yml.
How It Is Wired
Execution begins in backend/src/main.rs at the rocket function (line 7). rocket calls create_rocket (see backend/src/api.rs) which:
- Reads the API secret from
/run/secrets/VAULTLS_API_SECRET(backend/src/settings.rs→get_secret). This is the only cryptographic entry point. - Mounts the API routes defined in
backend/src/api.rs(setup,login,change_password, etc.). - Instantiates the database via
backend/src/db.rs::new, which opens the encrypted SQLite file and runs migrations (backend/migrations/*).
Each API handler ultimately calls shared helpers such as new_authenticated (used in 27 places) and as_bytes (15 places). The most widely referenced module on the frontend is frontend/src/api/ApiClient (imported by three other API modules).
Outbound effects are limited to:
- Filesystem –
settings.rs::save_to_filewrites the initial configuration (e.g.,set_password_enabled). - Crypto –
settings.rs::get_secretreads the base64‑encoded API secret. - Network – ACME client code (
backend/src/acme/*) makes DNS and HTTP calls; email notifications (backend/src/notification/*) issue SMTP traffic.
No circular import cycles were detected; the call graph contains 500 internal edges, with the highest‑impact functions being new_authenticated, as_bytes, and malformed. Changing any of those will affect dozens of callers.
How To Use It
# Clone the repository
git clone https://github.com/moses-y/VaulTLS
cd VaulTLS
# Build the container image (Docker or Podman)
docker build -t vaultls:local -f Containerfile .
# Run the container (example from README)
docker run -d \
--name vaultls \
-p 5173:80 \
-v vaultls-data:/app/data \
-e VAULTLS_API_SECRET="$(openssl rand -base64 32)" \
-e VAULTLS_URL="https://vaultls.example.com/" \
vaultls:local
Required environment variables are listed in the README and parsed in backend/src/settings.rs: VAULTLS_API_SECRET (mandatory), VAULTLS_URL, optional VAULTLS_DB_SECRET (enables DB encryption), VAULTLS_INSECURE, and OIDC variables (VAULTLS_OIDC_*).
The UI is served at http://localhost:5173 (or through a reverse proxy). The API can be exercised directly with tools like curl against http://localhost:5173/api/....
Real‑World Use
A small office deploys VaulTLS behind an Nginx reverse proxy. When a new internal service is provisioned, an operator clicks Create Certificate in the UI, selects the internal CA, and downloads the PEM bundle. The service’s startup script loads the bundle from a shared volume mounted at /app/data/certs. Renewal is automated via the built‑in ACME client, which contacts Let’s Encrypt using the stored API secret.
Code Health & Issues
- High – Pin GitHub Actions –
.github/workflows/*.ymluses tag references (@v3,@v5). Replace with commit SHA to prevent supply‑chain drift. - High – CI never runs tests – Workflow files contain no test step despite 19 test files. Add a
run: cargo test && npm teststage. - Medium – Pin base images by digest –
Containerfilereferences mutable tags (node:26,rust:1.97,nginx:stable). Use@sha256:digests. - Medium – No dependency‑vulnerability scan – Add
dependency-review-actionorosv-scannerto PR checks. - Medium – Run container as non‑root – Insert a low‑privilege
USERdirective andchownneeded directories. - Low – Missing job timeouts – Add
timeout-minutesto each job indocker-image.yml. - Low – Lacking repo conventions – Add
.editorconfig,.gitattributes, and a formatter config (e.g.,prettierfor TS/Vue,rustfmtfor Rust).
No critical findings were reported; the repository includes tests, a CI pipeline, a license, and lockfiles.
The Bottom Line
VaulTLS delivers a functional, container‑ready solution for managing mTLS and SSH certificates, with a clean separation between a performant Rust API and a Vue front‑end. The codebase is usable but suffers from duplicated logic, deep nesting, and several CI/ops hygiene gaps that should be addressed before adopting it in production. Engineers comfortable with Rust, Docker, and Vue can extend it, but the current health issues merit a modest remediation effort.