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
httponlysession 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.microor larger). Open ports 80 and 443 in the security group. - GCP: Create a Compute Engine instance (Ubuntu 24.04 LTS,
e2-microor 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e68a2194db4230414bd4e9abf5e21825e9ee267ea36388a0ba5fd421710aeb10
|
|
| MD5 |
43a766d7b8689aef5bd201650f03d9ba
|
|
| BLAKE2b-256 |
b9168f6eb7fb04fd73dc1c24f32cef088e681b57e44b73a961297b9d0969152b
|
Provenance
The following attestation bundles were made for leafdocs-0.1.0.tar.gz:
Publisher:
publish.yml on anshulraj10/leafdocs
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
leafdocs-0.1.0.tar.gz -
Subject digest:
e68a2194db4230414bd4e9abf5e21825e9ee267ea36388a0ba5fd421710aeb10 - Sigstore transparency entry: 1740368441
- Sigstore integration time:
-
Permalink:
anshulraj10/leafdocs@22fa0c0fe3e47e504018fe25bf3fb3fa43249b59 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/anshulraj10
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@22fa0c0fe3e47e504018fe25bf3fb3fa43249b59 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e3d5ad72b9a57732793e9b713c600b871e23ca93f5eeb6542c70cbe910cf7e3c
|
|
| MD5 |
0aac26d4b6235a532e0559ceaae80319
|
|
| BLAKE2b-256 |
f1cc8ff604c97573f1ab05410baef98bf63ca8f957f4894241ad407742c27e86
|
Provenance
The following attestation bundles were made for leafdocs-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on anshulraj10/leafdocs
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
leafdocs-0.1.0-py3-none-any.whl -
Subject digest:
e3d5ad72b9a57732793e9b713c600b871e23ca93f5eeb6542c70cbe910cf7e3c - Sigstore transparency entry: 1740368506
- Sigstore integration time:
-
Permalink:
anshulraj10/leafdocs@22fa0c0fe3e47e504018fe25bf3fb3fa43249b59 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/anshulraj10
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@22fa0c0fe3e47e504018fe25bf3fb3fa43249b59 -
Trigger Event:
push
-
Statement type: