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

  1. Request → index.php – the web server (Docker‑provided PHP‑FPM) invokes perlite/index.php.
  2. Configuration – perlite/settings.php reads the vault path and feature flags (e.g., LaTeX, Mermaid).
  3. Filesystem crawl – perlite/helper.php recursively scans the vault, building a tree of file metadata.
  4. Markdown parsing – each file is passed to PerliteParsedown.php, a thin wrapper around erusev/parsedown with safemode enabled.
  5. Graph data – the crawl produces a JSON structure (metadata.json) that the front‑end consumes.
  6. Front‑end rendering – perlite/.js/perlite.js loads the JSON, draws the graph with vis-network.min.js, and enhances the page with KaTeX, Highlight.js, and Mermaid (bundled minified libs).
  7. 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.php contain indentation depth 13.
  • High – Oversized files – perlite/.js/perlite.js, PerliteParsedown.php, index.php exceed 1 300 lines each.
  • Medium – High branching density – perlite/.scripts/create_GraphLinking.py has 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; declare contents: read and narrow per‑job scopes.
  • Medium – Enable Dependabot – no dependabot.yml present; add to auto‑update Composer and Docker dependencies.
  • Medium – Pin Docker base image – php:fpm-alpine is mutable; use a digest (php@sha256:…).
  • Medium – Dependency‑vulnerability gate – CI lacks a scan step; integrate dependency-review-action or osv-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.