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:

  1. Reads the API secret from /run/secrets/VAULTLS_API_SECRET (backend/src/settings.rsget_secret). This is the only cryptographic entry point.
  2. Mounts the API routes defined in backend/src/api.rs (setup, login, change_password, etc.).
  3. 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:

  • Filesystemsettings.rs::save_to_file writes the initial configuration (e.g., set_password_enabled).
  • Cryptosettings.rs::get_secret reads 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/*.yml uses 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 test stage.
  • Medium – Pin base images by digestContainerfile references mutable tags (node:26, rust:1.97, nginx:stable). Use @sha256: digests.
  • Medium – No dependency‑vulnerability scan – Add dependency-review-action or osv-scanner to PR checks.
  • Medium – Run container as non‑root – Insert a low‑privilege USER directive and chown needed directories.
  • Low – Missing job timeouts – Add timeout-minutes to each job in docker-image.yml.
  • Low – Lacking repo conventions – Add .editorconfig, .gitattributes, and a formatter config (e.g., prettier for TS/Vue, rustfmt for 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.