The Problem
Teams that rely on Obsidian for knowledge capture often need a quick, self‑hosted way to share vault contents as static web pages. Existing options either require a database or a paid publish service, creating friction for small‑scale internal docs or public showcases.
What This Does
Perlite renders a full Obsidian vault (or any markdown folder) as a responsive web site without a database. The PHP entry point perlite/index.php walks the supplied directory, feeds each .md file to Parsedown (via perlite/.src/PerliteParsedown.php), and builds an interactive graph (perlite/.js/perlite.js). Styling and assets live under perlite/.styles/ and the bundled JS libraries (.js/*.min.js). No external services are required; the only runtime dependency is a PHP 7+ FPM container.
How It Is Wired
- Request →
index.php– the web server (Docker‑provided PHP‑FPM) invokesperlite/index.php. - Configuration –
perlite/settings.phpreads the vault path and feature flags (e.g., LaTeX, Mermaid). - Filesystem crawl –
perlite/helper.phprecursively scans the vault, building a tree of file metadata. - Markdown parsing – each file is passed to
PerliteParsedown.php, a thin wrapper arounderusev/parsedownwith safemode enabled. - Graph data – the crawl produces a JSON structure (
metadata.json) that the front‑end consumes. - Front‑end rendering –
perlite/.js/perlite.jsloads the JSON, draws the graph withvis-network.min.js, and enhances the page with KaTeX, Highlight.js, and Mermaid (bundled minified libs). - Output – HTML is assembled on‑the‑fly; no static files are written, so the site always reflects the current vault state.
The only external touchpoint is the Docker build (perlite/Dockerfile) which installs Composer deps (perlite/composer.json) and copies the whole perlite/ tree into the container. No database connections or network calls occur beyond optional asset fetching by the browser.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/Perlite
cd Perlite/perlite
# Build and start the container (Docker Compose files are provided)
docker compose -f ../docker-compose.yml up -d
The container listens on port 80 by default. Point a browser at http://localhost and set the vault path in perlite/settings.php (e.g., $_ENV['VAULT_PATH'] = '/var/www/html/vault';). The site will index the vault and render the interactive graph automatically.
Real‑World Use
A company can mount its internal Obsidian vault as a bind‑mount into the container:
services:
perlite:
image: perlite:latest
volumes:
- ./my-vault:/vault:ro
environment:
VAULT_PATH: /vault
Employees then access https://docs.internal to browse documentation, with full tag/graph navigation and LaTeX rendering, while the source files remain private on the host filesystem.
Code Health & Issues
- High – Deep nesting –
perlite/.src/PerliteParsedown.php,helper.php,index.phpcontain indentation depth 13. - High – Oversized files –
perlite/.js/perlite.js,PerliteParsedown.php,index.phpexceed 1 300 lines each. - Medium – High branching density –
perlite/.scripts/create_GraphLinking.pyhas 20 branches in 70 lines.
Audit findings
- High – Pin GitHub Actions – workflows use mutable tags (e.g.,
docker/setup-qemu-action@v2). Replace with SHA‑pinned refs. - High – No test suite – repository contains source files but zero tests; add unit tests for public entry points.
- Medium – Least‑privilege
GITHUB_TOKEN– workflows lack explicit permissions; declarecontents: readand narrow per‑job scopes. - Medium – Enable Dependabot – no
dependabot.ymlpresent; add to auto‑update Composer and Docker dependencies. - Medium – Pin Docker base image –
php:fpm-alpineis mutable; use a digest (php@sha256:…). - Medium – Dependency‑vulnerability gate – CI lacks a scan step; integrate
dependency-review-actionorosv-scanner. - Medium – Large binaries –
perlite/.js/mermaid.min.js.map(10 MB) should be stored via Git LFS or external storage. - Low – Job timeouts – workflows omit
timeout-minutes; set reasonable limits to avoid runaway runs.
The Bottom Line
Perlite delivers a functional, Docker‑ready markdown viewer tailored to Obsidian vaults, with a clean PHP‑only backend and rich front‑end features. The codebase is functional but suffers from very large, deeply nested files and lacks automated testing, which raises maintenance risk. It is suitable for teams comfortable managing Docker containers and willing to invest in refactoring or adding tests for long‑term stability.