markdown-vault
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.
Overview
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
Key Features
- 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
Quick Start
Installation
Using uvx (Recommended - No Installation Required)
uvx markdown-vault start --reload
Using pip
pip install markdown-vault
markdown-vault start --reload
Using uv (Development)
git clone https://github.com/boxpositron/markdown-vault.git
cd markdown-vault
uv venv && uv sync
uv run markdown-vault start --reload
Using 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:latest
Configuration
Method 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: true
Method 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 start
Method 3: CLI Flags
markdown-vault start --config config.yaml --reload
First Run
# Generate an API key
markdown-vault generate-key
# Start with auto-generated cert
markdown-vault start --reload
# Access API docs
open https://localhost:27123/docs
API Documentation
Visit https://localhost:27123/docs for interactive Swagger documentation.
Core Endpoints
# 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
Authentication
All requests require an API key:
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://localhost:27123/vault/note.md
Advanced Features
PATCH Operations
Heading 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"]'
Search Examples
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]}
]
}
}'
Development
Prerequisites
- Python 3.10+
- uv (recommended) or pip
Setup
# 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]"
Development Commands
# 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 --reload
Project Structure
markdown-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
Configuration Reference
Server Configuration
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 Configuration
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 files
Security Configuration
security:
api_key: "your-key" # API authentication key
cert_path: "./certs/server.crt"
key_path: "./certs/server.key"
auto_generate_cert: true # Generate self-signed cert
Periodic Notes
periodic_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
logging:
level: "INFO" # DEBUG, INFO, WARNING, ERROR
format: "json" # json or text
file: null # Log file path (optional)
Deployment
Docker Compose
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
Systemd Service
[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.target
Environment Variables
All 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=DEBUG
Obsidian Compatibility
markdown-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
Configuration for Obsidian
vault:
path: "/Users/you/Documents/MyObsidianVault"
obsidian:
enabled: true # Respect .obsidian/ directory
config_sync: true # Read Obsidian's periodic notes config
API Endpoints
Uses 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
Use Cases
- 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
Testing
# 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-watch
Test Stats: 351 tests, 86% coverage
Troubleshooting
SSL Certificate Issues
# Generate new self-signed certificate
markdown-vault start --generate-cert
# Use HTTP instead (development only)
markdown-vault start --no-https
Permission Errors
# Ensure vault directory is writable
chmod -R u+w /path/to/vault
# Or set auto_create in config
vault:
auto_create: true
Port Already in Use
# Use different port
markdown-vault start --port 8080
# Or set in config
server:
port: 8080
Contributing
Contributions welcome! See CONTRIBUTING.md for guidelines.
Quick Contribution Guide
- 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
Publishing & Releases
This project uses automated publishing with OIDC Trusted Publishing for security and convenience.
For Maintainers: Setting Up PyPI Trusted Publishing
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:
-
Repeat for TestPyPI (for testing)
- Visit: https://test.pypi.org/manage/account/publishing/
- Use same settings but with environment name:
testpypi
Creating a Release
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.gz
This automatically:
- ✅ Builds Docker images (multi-platform: amd64/arm64)
- ✅ Pushes to
ghcr.io/boxpositron/markdown-vault - ✅ Publishes to TestPyPI (for verification)
- ✅ Publishes to PyPI (production)
- ✅ Generates SBOM and provenance attestations
Manual Publishing (Alternative)
If you need to publish manually:
# Build the package
uv build
# Publish using trusted publishing (no token needed!)
uv publish
# Or publish to TestPyPI first
uv publish --publish-url https://test.pypi.org/legacy/
Note: Manual publishing still uses OIDC when run from GitHub Actions, or requires API token when run locally.
Verification
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.2
Changelog
See CHANGELOG.md for version history.
License
MIT License - See LICENSE for details.
Author
David Ibia - pypi@boxpositron.dev
Credits
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.
Links
- Repository: https://github.com/boxpositron/markdown-vault
- Issues: https://github.com/boxpositron/markdown-vault/issues
- PyPI: https://pypi.org/project/markdown-vault/ (when published)
Metadata
Release files for markdown-vault 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| markdown_vault-0.0.1.tar.gz | 95.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| markdown_vault-0.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 154.1 kB
Release files / markdown_vault-0.0.1.tar.gz
| Download URL | markdown_vault-0.0.1.tar.gz |
|---|---|
| Size | 95.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
81f8a5e8ee2df31c694cc5b80516de6c617af5fe9271500b020a511fe848995d
|
|
BLAKE2b-256 checksum How to use checksums |
d22de5247629ffa65286beb3530d22011f344b06eeeac2e2c7e04921511fb808
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 30, 2025.
Transparency logRelease files / markdown_vault-0.0.1-py3-none-any.whl
| Download URL | markdown_vault-0.0.1-py3-none-any.whl |
|---|---|
| Size | 58.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9c2a5b1fbbc5de7933abe0a59640473aec082c8374708c80a2bc10dd06b00bed
|
|
BLAKE2b-256 checksum How to use checksums |
bcbc0be8d3f2e6c70adf06a377b8806a87f0a9fa406765f8521773503c8b130d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 30, 2025.
Transparency log