Skip to main content

Turn a directory of Markdown files into a self-hosted, searchable web reader.

Project description

leafdocs

A lightweight Python library that turns a directory of Markdown files into a self-hosted, searchable web reader.

Install it, point it at a folder, get a running Flask app you can deploy anywhere.

from leafdocs import LeafDocs

ld = LeafDocs(docs_dir="./docs")
ld.run()

Installation

pip install leafdocs

Usage

Minimal setup

from leafdocs import LeafDocs

ld = LeafDocs(docs_dir="./docs")
ld.run()

Visit http://127.0.0.1:5000 — you'll see a searchable index of all .md files in your docs/ directory.

Constructor arguments

Argument Type Default Description
docs_dir str "./docs" Path to the directory containing .md files
secret_key str or None None Flask session secret key (see Auth section)

Accessing the Flask app

The underlying Flask app is exposed as ld.flask_app. Use it to add routes, middleware, or blueprints:

from leafdocs import LeafDocs

ld = LeafDocs(docs_dir="./docs")

@ld.flask_app.route("/health")
def health():
    return {"status": "ok"}

ld.run()

Frontmatter

Each .md file can optionally include YAML frontmatter. Supported fields:

---
title: My Document Title
tags: [python, tutorial]
---
Field Type Fallback
title string Filename, titlecased
tags list or comma-separated string None

Frontmatter is optional — files without it are discovered and served normally.


Authentication

By default the server runs open with no login required.

To enable pin-based auth, add pins to a .env file in your working directory:

LEAFDOCS_PINS=mypin123,anotherpin

Restart the server. All routes will now require a valid pin.

How it works:

  • Pins are hashed with bcrypt at startup — raw values are never stored in memory
  • A successful login issues an httponly session cookie
  • Multiple pins are supported — useful for sharing access without a shared secret
  • To invalidate all sessions, rotate or remove the pin and restart

Session secret key

By default a random secret key is generated at startup, which means sessions are invalidated on every restart. For persistent sessions across restarts, set a stable key:

LEAFDOCS_SECRET_KEY=your-long-random-secret-here

Or pass it directly in code:

ld = LeafDocs(docs_dir="./docs", secret_key="your-long-random-secret-here")

Generate a good key with:

python -c "import secrets; print(secrets.token_hex(32))"

Configuration reference

All pins and the secret key are configured via .env only. Copy .env.example to .env to get started:

LEAFDOCS_PINS=pin1,pin2
LEAFDOCS_SECRET_KEY=your-secret-here

Known limitations

  • No caching — Markdown is rendered on every request. Fine for personal or low-traffic use; not suitable for high-traffic production serving.
  • No per-device session revocation — rotating the pin invalidates all active sessions across all devices.
  • No plugin system — the primary extension hook is ld.flask_app. Add routes and middleware directly on the Flask app.

Deployment

Disclaimer: leafdocs is a Flask application. Running it with ld.run() uses Flask's built-in development server, which is not suitable for production. For production use, you are responsible for:

  • Running behind a production WSGI server (Gunicorn recommended)
  • Terminating HTTPS at a reverse proxy (Nginx recommended)
  • Managing the process with a supervisor (systemd recommended)

The instructions below cover a standard Nginx + Gunicorn + systemd setup.

AWS EC2 / GCP Compute Engine

The steps are identical for both — the only difference is how you provision the VM.

1. Provision a VM

  • AWS: Launch an EC2 instance (Ubuntu 24.04 LTS, t3.micro or larger). Open ports 80 and 443 in the security group.
  • GCP: Create a Compute Engine instance (Ubuntu 24.04 LTS, e2-micro or larger). Open ports 80 and 443 in the firewall rules.

SSH into the instance.

2. Install dependencies

sudo apt update && sudo apt install -y python3-pip python3-venv nginx

3. Set up the app

mkdir ~/leafdocs-app && cd ~/leafdocs-app
python3 -m venv .venv
source .venv/bin/activate
pip install leafdocs gunicorn

Create your docs/ directory and add your .md files:

mkdir docs

Create main.py:

from leafdocs import LeafDocs

ld = LeafDocs(docs_dir="./docs")
app = ld.flask_app

Create .env:

LEAFDOCS_PINS=yourpin
LEAFDOCS_SECRET_KEY=your-long-random-secret-here

Test it runs:

gunicorn --bind 127.0.0.1:8000 main:app

4. Configure systemd

Create /etc/systemd/system/leafdocs.service:

[Unit]
Description=LeafDocs
After=network.target

[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/leafdocs-app
Environment="PATH=/home/ubuntu/leafdocs-app/.venv/bin"
ExecStart=/home/ubuntu/leafdocs-app/.venv/bin/gunicorn --workers 2 --bind 127.0.0.1:8000 main:app
Restart=always

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable leafdocs
sudo systemctl start leafdocs
sudo systemctl status leafdocs

5. Configure Nginx

Create /etc/nginx/sites-available/leafdocs:

server {
    listen 80;
    server_name your-domain.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Enable and reload:

sudo ln -s /etc/nginx/sites-available/leafdocs /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

6. Enable HTTPS

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com

Certbot will automatically update your Nginx config and set up auto-renewal.


Development

git clone https://github.com/anshulraj10/leafdocs
cd leafdocs
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest tests/ -v

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

leafdocs-0.1.0.tar.gz (18.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

leafdocs-0.1.0-py3-none-any.whl (15.5 kB view details)

Uploaded Python 3

File details

Details for the file leafdocs-0.1.0.tar.gz.

File metadata

  • Download URL: leafdocs-0.1.0.tar.gz
  • Upload date:
  • Size: 18.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for leafdocs-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e68a2194db4230414bd4e9abf5e21825e9ee267ea36388a0ba5fd421710aeb10
MD5 43a766d7b8689aef5bd201650f03d9ba
BLAKE2b-256 b9168f6eb7fb04fd73dc1c24f32cef088e681b57e44b73a961297b9d0969152b

See more details on using hashes here.

Provenance

The following attestation bundles were made for leafdocs-0.1.0.tar.gz:

Publisher: publish.yml on anshulraj10/leafdocs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file leafdocs-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: leafdocs-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 15.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for leafdocs-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e3d5ad72b9a57732793e9b713c600b871e23ca93f5eeb6542c70cbe910cf7e3c
MD5 0aac26d4b6235a532e0559ceaae80319
BLAKE2b-256 f1cc8ff604c97573f1ab05410baef98bf63ca8f957f4894241ad407742c27e86

See more details on using hashes here.

Provenance

The following attestation bundles were made for leafdocs-0.1.0-py3-none-any.whl:

Publisher: publish.yml on anshulraj10/leafdocs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page