Skip to content
GitHub stars

Web Server

Overview

msgvault serve starts an HTTP server that exposes your local email archive over a REST API. It optionally runs a background sync scheduler to keep accounts up to date on a cron-based schedule.

All data stays local. The API queries the same SQLite database and attachment store as the CLI and TUI. The server is designed for local integrations, dashboards, and automation scripts.

Quick Start

Add a [server] section to your config.toml:

[server]
api_port = 8080
api_key = "your-secret-key"

Start the server:

Terminal window
msgvault serve

Test connectivity:

Terminal window
# Health check (no auth required)
curl http://localhost:8080/health
# Archive stats (auth required)
curl -H "Authorization: Bearer your-secret-key" http://localhost:8080/api/v1/stats

Authentication

All endpoints except /health require authentication when api_key is set in your config. Three authentication methods are supported:

MethodHeaderExample
Bearer tokenAuthorization: Bearer <key>Authorization: Bearer my-secret
API key headerX-API-Key: <key>X-API-Key: my-secret
Plain auth headerAuthorization: <key>Authorization: my-secret

If no api_key is configured and the server is bound to a loopback address (127.0.0.1 or ::1), authentication is not required.

API Endpoints

GET /health

Health check endpoint. Does not require authentication.

Response:

{"status": "ok"}

GET /api/v1/stats

Archive statistics.

Response:

{
"total_messages": 142857,
"total_threads": 48293,
"total_accounts": 2,
"total_labels": 47,
"total_attachments": 31204,
"database_size_bytes": 8589934592
}

GET /api/v1/messages

Paginated message list.

ParameterTypeDefaultDescription
pageint1Page number
page_sizeint50Results per page

Response:

{
"total": 142857,
"page": 1,
"page_size": 50,
"messages": [
{
"id": 12345,
"subject": "Q4 Planning",
"from": "[email protected]",
"sent_at": "2024-10-15T09:30:00Z",
"snippet": "Here's the draft for Q4...",
"labels": ["INBOX", "IMPORTANT"],
"has_attachments": true,
"size_bytes": 52480
}
]
}

GET /api/v1/messages/{id}

Full message details including body and attachment metadata.

Response:

{
"id": 12345,
"subject": "Q4 Planning",
"from": "[email protected]",
"sent_at": "2024-10-15T09:30:00Z",
"snippet": "Here's the draft for Q4...",
"labels": ["INBOX", "IMPORTANT"],
"has_attachments": true,
"size_bytes": 52480,
"body": "<full message body>",
"attachments": [
{
"filename": "q4-plan.pdf",
"mime_type": "application/pdf",
"size_bytes": 204800
}
]
}

GET /api/v1/search

Full-text search using the same query syntax as msgvault search.

ParameterTypeDefaultDescription
qstring(required)Search query
pageint1Page number
page_sizeint50Results per page

Response:

{
"query": "quarterly report",
"total": 23,
"page": 1,
"page_size": 50,
"messages": [
{
"id": 12345,
"subject": "Q4 Planning",
"from": "[email protected]",
"sent_at": "2024-10-15T09:30:00Z",
"snippet": "Here's the draft for Q4...",
"labels": ["INBOX", "IMPORTANT"],
"has_attachments": true,
"size_bytes": 52480
}
]
}

See Searching for the full query syntax reference.


GET /api/v1/accounts

List configured accounts with sync status.

Response:

{
"accounts": [
{
"email": "[email protected]",
"display_name": "Your Name",
"last_sync_at": "2024-10-15T08:00:00Z",
"next_sync_at": "2024-10-15T09:00:00Z",
"schedule": "0 * * * *",
"enabled": true
}
]
}

POST /api/v1/sync/{account}

Trigger a manual sync for an account. Returns immediately with a 202 status while the sync runs in the background.

Response (202 Accepted):

{
"status": "accepted",
"message": "sync started for [email protected]"
}

GET /api/v1/scheduler/status

Scheduler state and per-account schedule details.

Response:

{
"running": true,
"accounts": [
{
"email": "[email protected]",
"running": false,
"last_run": "2024-10-15T08:00:00Z",
"next_run": "2024-10-15T09:00:00Z",
"schedule": "0 * * * *",
"last_error": null
}
]
}

Rate Limiting

The API enforces rate limiting of 10 requests per second per client IP, with a burst allowance of 20 requests. When the limit is exceeded, the server responds with HTTP 429 and includes a Retry-After header indicating how long to wait before retrying.

CORS

Cross-Origin Resource Sharing is disabled by default. To allow browser-based clients, configure allowed origins in your config.toml:

[server]
cors_origins = ["http://localhost:3000", "https://my-dashboard.example.com"]
cors_credentials = true
cors_max_age = 3600

Scheduled Sync

The server can automatically sync accounts on a cron-based schedule. Add [[accounts]] sections to your config:

[[accounts]]
schedule = "0 * * * *" # every hour
enabled = true
[[accounts]]
schedule = "*/15 * * * *" # every 15 minutes
enabled = true

The scheduler starts automatically with msgvault serve when account schedules are configured. Use the /api/v1/scheduler/status endpoint to monitor schedule state, and /api/v1/sync/{account} to trigger a manual sync outside the schedule.

Security Model

The server is designed for local use:

  • Loopback-only by default. The default bind address is 127.0.0.1, restricting access to the local machine.
  • API key required for non-loopback. If you bind to a non-loopback address (e.g., 0.0.0.0), the server requires api_key to be set and will refuse to start without it.
  • Opt-in for insecure binding. To bind to a non-loopback address without an API key (not recommended), set allow_insecure = true.

Configuration Reference

All server settings go in the [server] section of config.toml. Account schedules use [[accounts]] sections.

[server]

KeyDefaultDescription
api_port8080Port the server listens on
bind_addr127.0.0.1Bind address
api_keyAPI key for authentication
allow_insecurefalseAllow non-loopback binding without api_key
cors_origins[]Allowed CORS origins
cors_credentialsfalseAllow credentials in CORS requests
cors_max_age0CORS preflight cache duration in seconds

[[accounts]]

KeyDefaultDescription
email(required)Gmail account email address
scheduleCron expression for sync schedule
enabledtrueWhether scheduled sync is active

See the Configuration page for the full config file reference.