Skip to main content

Standalone REST API service for markdown vault management with Obsidian compatibility

Project description

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.

Links

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

markdown_vault-0.0.1.tar.gz (95.7 kB view details)

Uploaded Source

Built Distribution

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

markdown_vault-0.0.1-py3-none-any.whl (58.4 kB view details)

Uploaded Python 3

File details

Details for the file markdown_vault-0.0.1.tar.gz.

File metadata

  • Download URL: markdown_vault-0.0.1.tar.gz
  • Upload date:
  • Size: 95.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for markdown_vault-0.0.1.tar.gz
Algorithm Hash digest
SHA256 81f8a5e8ee2df31c694cc5b80516de6c617af5fe9271500b020a511fe848995d
MD5 bbe910f1ca3c6ba9452898987af661b1
BLAKE2b-256 d22de5247629ffa65286beb3530d22011f344b06eeeac2e2c7e04921511fb808

See more details on using hashes here.

Provenance

The following attestation bundles were made for markdown_vault-0.0.1.tar.gz:

Publisher: pypi-publish.yml on boxpositron/markdown-vault

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

File details

Details for the file markdown_vault-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: markdown_vault-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 58.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for markdown_vault-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9c2a5b1fbbc5de7933abe0a59640473aec082c8374708c80a2bc10dd06b00bed
MD5 502928c56125b9d7747d9b84ea6e6ddb
BLAKE2b-256 bcbc0be8d3f2e6c70adf06a377b8806a87f0a9fa406765f8521773503c8b130d

See more details on using hashes here.

Provenance

The following attestation bundles were made for markdown_vault-0.0.1-py3-none-any.whl:

Publisher: pypi-publish.yml on boxpositron/markdown-vault

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