A production-ready REST API server for markdown vault management with comprehensive search, automation, and optional Obsidian compatibility. Built for CI/CD pipelines, cloud deployments, and programmatic markdown workflows.
markdown-vault is a standalone HTTP service providing a secure REST API for managing markdown files and vaults. Perfect for automation tools, CI/CD pipelines, or programmatic note access.
What it is: Independent REST API server for markdown vault operations
What it's not: Not an Obsidian plugin - runs as a standalone service with optional Obsidian vault support
- Full CRUD Operations - Create, read, update, delete markdown files via REST API
- Advanced PATCH - Targeted updates with heading/block/frontmatter selectors
- Periodic Notes - Daily, weekly, monthly, quarterly, yearly note support
- Powerful Search - Text search and JSONLogic-based complex queries
- Secure by Default - HTTPS with API key authentication
- Production Ready - Docker support, 24/7 availability, cloud-native
- Obsidian Compatible - API-compatible with Obsidian Local REST API plugin
Using uvx (Recommended - No Installation Required)
uvx markdown-vault start --reloadUsing pip
pip install markdown-vault
markdown-vault start --reloadUsing uv (Development)
git clone https://github.com/boxpositron/markdown-vault.git
cd markdown-vault
uv venv && uv sync
uv run markdown-vault start --reloadUsing Docker (from GitHub Container Registry)
docker run -d \
-p 27123:27123 \
-v /path/to/vault:/vault \
-e MARKDOWN_VAULT_VAULT__PATH=/vault \
-e MARKDOWN_VAULT_SECURITY__API_KEY=your-key \
ghcr.io/boxpositron/markdown-vault:latestMethod 1: Config File
Create config.yaml:
server:
host: "127.0.0.1"
port: 27123
https: true
vault:
path: "/path/to/your/vault"
auto_create: true
security:
api_key: "your-secure-api-key-here"
auto_generate_cert: trueMethod 2: Environment Variables
export MARKDOWN_VAULT_SERVER__PORT=27123
export MARKDOWN_VAULT_VAULT__PATH=/path/to/vault
export MARKDOWN_VAULT_SECURITY__API_KEY=your-key
markdown-vault startMethod 3: CLI Flags
markdown-vault start --config config.yaml --reload# Generate an API key
markdown-vault generate-key
# Start with auto-generated cert
markdown-vault start --reload
# Access API docs
open https://localhost:27123/docsVisit https://localhost:27123/docs for interactive Swagger documentation.
# Vault Operations
GET /vault/{filepath} # Read file
PUT /vault/{filepath} # Create/update
POST /vault/{filepath} # Append
PATCH /vault/{filepath} # Targeted update
DELETE /vault/{filepath} # Delete
GET /vault/ # List files
# Periodic Notes
GET /periodic/daily/ # Get today's note
GET /periodic/weekly/ # Get this week's note
GET /periodic/monthly/ # Get this month's note
PUT /periodic/daily/ # Update today's note
POST /periodic/daily/ # Append to today's note
# Search
POST /search/simple/ # Text search
POST /search/ # JSONLogic search
# System
GET / # Server status
GET /_/server/status # Detailed status
All requests require an API key:
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://localhost:27123/vault/note.mdHeading Targeting
# Append under specific heading
curl -X PATCH https://localhost:27123/vault/note.md \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Operation: append" \
-H "Target-Type: heading" \
-H "Target: Main Heading::Sub Heading:1" \
-d "New content"Block Reference
# Update at block reference
curl -X PATCH https://localhost:27123/vault/note.md \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Operation: replace" \
-H "Target-Type: block" \
-H "Target: ^abc123" \
-d "Updated content"Frontmatter
# Update YAML frontmatter
curl -X PATCH https://localhost:27123/vault/note.md \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Operation: replace" \
-H "Target-Type: frontmatter" \
-H "Target: tags" \
-H "Content-Type: application/json" \
-d '["new", "tags"]'Simple Text Search
curl -X POST https://localhost:27123/search/simple/ \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "search term"}'Complex JSONLogic Search
curl -X POST https://localhost:27123/search/ \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": {
"and": [
{"in": ["tag1", {"var": "frontmatter.tags"}]},
{">=": [{"var": "frontmatter.priority"}, 5]}
]
}
}'- Python 3.10+
- uv (recommended) or pip
# Clone repository
git clone https://github.com/boxpositron/markdown-vault.git
cd markdown-vault
# Install with uv (recommended)
uv venv
uv sync
source .venv/bin/activate # or `.venv\Scripts\activate` on Windows
# Or install with pip
pip install -e ".[dev]"# Run tests
make local-test # or: uv run pytest
make local-test-cov # with coverage
# Code quality
make local-lint # ruff linting
make local-format # black formatting
make local-typecheck # mypy type checking
make local-typecheck-pyright # pyright type checking
# Run all QA checks
make local-qa # lint + format + typecheck + test
# Run server
make local-run # or: uv run markdown-vault start --reloadmarkdown-vault/
├── src/markdown_vault/
│ ├── api/ # FastAPI routes and dependencies
│ ├── core/ # Business logic (vault, search, patch)
│ ├── models/ # Pydantic models
│ └── utils/ # Utilities (crypto, dates)
├── tests/ # 351 tests, 86% coverage
├── docs/ # Additional documentation
├── config/ # Example configurations
└── examples/ # Usage examples
server:
host: "127.0.0.1" # Bind address
port: 27123 # Port number
https: true # Enable HTTPS
reload: false # Auto-reload on changes (dev only)vault:
path: "/path/to/vault" # Vault directory (required)
auto_create: true # Create directory if missing
watch_files: false # Watch for file changes
respect_gitignore: true # Honor .gitignore filessecurity:
api_key: "your-key" # API authentication key
cert_path: "./certs/server.crt"
key_path: "./certs/server.key"
auto_generate_cert: true # Generate self-signed certperiodic_notes:
daily:
enabled: true
format: "YYYY-MM-DD"
folder: "daily"
template: "templates/daily.md"
weekly:
enabled: true
format: "YYYY-[W]WW"
folder: "weekly"
monthly:
enabled: true
format: "YYYY-MM"
folder: "monthly"logging:
level: "INFO" # DEBUG, INFO, WARNING, ERROR
format: "json" # json or text
file: null # Log file path (optional)version: '3.8'
services:
markdown-vault:
image: ghcr.io/boxpositron/markdown-vault:latest
ports:
- "27123:27123"
volumes:
- ./vault:/vault
- ./config.yaml:/app/config.yaml
environment:
- MARKDOWN_VAULT_VAULT__PATH=/vault
- MARKDOWN_VAULT_SECURITY__API_KEY=${API_KEY}
restart: unless-stopped[Unit]
Description=Markdown Vault REST API
After=network.target
[Service]
Type=simple
User=markdown
WorkingDirectory=/opt/markdown-vault
Environment="MARKDOWN_VAULT_VAULT__PATH=/data/vault"
Environment="MARKDOWN_VAULT_SECURITY__API_KEY=your-key"
ExecStart=/opt/markdown-vault/.venv/bin/markdown-vault start
Restart=always
[Install]
WantedBy=multi-user.targetAll configuration can be set via environment variables with the prefix MARKDOWN_VAULT_:
MARKDOWN_VAULT_SERVER__HOST=0.0.0.0
MARKDOWN_VAULT_SERVER__PORT=8080
MARKDOWN_VAULT_SERVER__HTTPS=false
MARKDOWN_VAULT_VAULT__PATH=/data/vault
MARKDOWN_VAULT_SECURITY__API_KEY=your-secure-key
MARKDOWN_VAULT_LOGGING__LEVEL=DEBUGmarkdown-vault is fully compatible with the Obsidian Local REST API plugin, allowing you to:
- Access Obsidian vaults via API without the desktop app running
- Use existing Obsidian-aware tools and integrations
- Deploy Obsidian vault access to servers
- Access multiple Obsidian vaults simultaneously
vault:
path: "/Users/you/Documents/MyObsidianVault"
obsidian:
enabled: true # Respect .obsidian/ directory
config_sync: true # Read Obsidian's periodic notes configUses the same default port (27123) and endpoints as the Obsidian plugin:
- Compatible with all Obsidian Local REST API clients
- Drop-in replacement for automation scripts
- Works whether Obsidian is running or not
- CI/CD Integration - Auto-update documentation in build pipelines
- Cloud Automation - Run as microservice for markdown workflows
- Content Management - Headless CMS for markdown content
- Knowledge Base API - REST API for documentation systems
- Script Integration - Programmatic notes from any language
- Obsidian Automation - API access without desktop dependency
- Multi-Vault Management - Access multiple vaults concurrently
# Run all tests
make local-test
# With coverage
make local-test-cov
# Single test file
uv run pytest tests/test_vault.py -v
# Single test
uv run pytest tests/test_vault.py::test_create_file -v
# Watch mode (requires pytest-watch)
pytest-watchTest Stats: 351 tests, 86% coverage
# Generate new self-signed certificate
markdown-vault start --generate-cert
# Use HTTP instead (development only)
markdown-vault start --no-https# Ensure vault directory is writable
chmod -R u+w /path/to/vault
# Or set auto_create in config
vault:
auto_create: true# Use different port
markdown-vault start --port 8080
# Or set in config
server:
port: 8080Contributions welcome! See CONTRIBUTING.md for guidelines.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes with tests
- Run QA checks (
make local-qa) - Commit (
git commit -m 'Add amazing feature') - Push (
git push origin feature/amazing-feature) - Open a Pull Request
This project uses automated publishing with OIDC Trusted Publishing for security and convenience.
One-time setup (requires PyPI account with project ownership):
-
Go to PyPI Project Settings
- Visit: https://pypi.org/manage/project/markdown-vault/settings/publishing/
- Or create pending publisher: https://pypi.org/manage/account/publishing/
-
Add GitHub as Trusted Publisher
- PyPI Project Name:
markdown-vault - Owner:
boxpositron - Repository name:
markdown-vault - Workflow name:
pypi-publish.yml - Environment name:
pypi(optional but recommended)
- PyPI Project Name:
Releases are fully automated via GitHub Actions:
# 1. Update version in pyproject.toml
# 2. Update CHANGELOG.md
# 3. Commit changes
git add pyproject.toml CHANGELOG.md
git commit -m "chore: bump version to 0.0.2"
# 4. Create and push tag
git tag v0.0.2
git push origin main --tags
# 5. Create GitHub Release (triggers publishing)
gh release create v0.0.2 \
--title "v0.0.2" \
--notes "See CHANGELOG.md for details" \
dist/*.whl dist/*.tar.gzThis automatically:
- ✅ Builds Docker images (multi-platform: amd64/arm64)
- ✅ Pushes to
ghcr.io/boxpositron/markdown-vault - ✅ Publishes to PyPI
- ✅ Generates SBOM and provenance attestations
If you need to publish manually:
# Build the package
uv build
# Publish using trusted publishing (no token needed!)
uv publishNote: Manual publishing from GitHub Actions uses OIDC automatically. Local publishing requires a PyPI API token.
After publishing, verify the release:
# Test PyPI installation
pip install markdown-vault==0.0.2
# Test Docker image
docker pull ghcr.io/boxpositron/markdown-vault:0.0.2
# Verify attestations
docker buildx imagetools inspect ghcr.io/boxpositron/markdown-vault:0.0.2See CHANGELOG.md for version history.
MIT License - See LICENSE for details.
David Ibia - pypi@boxpositron.dev
API compatibility with Obsidian Local REST API plugin by @coddingtonbear.
Obsidian is a trademark of Dynalist Inc. This project is independent and not affiliated with Obsidian or Dynalist Inc.