Skip to main content

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.

Python 3.10+ License: MIT

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

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes with tests
  4. Run QA checks (make local-qa)
  5. Commit (git commit -m 'Add amazing feature')
  6. Push (git push origin feature/amazing-feature)
  7. 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):

  1. Go to PyPI Project Settings

  2. 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)
  3. Repeat for TestPyPI (for testing)

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.

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)

Source distribution for markdown-vault 0.0.1
File Size Uploaded
markdown_vault-0.0.1.tar.gz 95.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for markdown-vault 0.0.1
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.0.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page