Skip to main content

Dashtro

CI Docker image npm @dashtro/client npm @dashtro/mcp License: MIT

A self-hosted CMS with a project → workspace → collection → document model, a FastAPI backend, and a React/TypeScript frontend. Ships as a single Docker image, published to ghcr.io/1atharvad/dashtro. This repo only builds and publishes that image — running it in production (nginx, tunnel/domain routing, etc.) is owned by the consuming project (e.g. the portfolio site that embeds Dashtro as its admin/CMS backend).

Contents

Structure

Path What it is
cms_backend/ FastAPI backend — API, auth, schema engine, data clients. See its README.
cms-frontend/ React + TypeScript + Vite frontend. See its README.
cms_mcp/ MCP server exposing Dashtro operations to MCP-compatible clients over /api/sdk/* (Python, console script dashtro-mcp, API-key auth).
sdk/js/ @dashtro/client — JS/TS client SDK for /api/sdk/*.
sdk/python/ dashtro-client — Python client SDK for /api/sdk/*, released in lockstep with sdk/js/.
sdk/mcp/ @dashtro/mcp — npx-runnable MCP server (Node/TS port of cms_mcp/) for consuming projects; talks to /api/sdk/* over HTTP with an API key, no Python required.
nginx/ Reverse proxy config for local dev only.

Packages

Everything this repo publishes, and what it's for:

Package Registry Install What it's for
ghcr.io/1atharvad/dashtro GHCR docker pull ghcr.io/1atharvad/dashtro The CMS itself — backend + built frontend in one image. See Running the image elsewhere.
@dashtro/client npm npm install @dashtro/client JS/TS client SDK for /api/sdk/* — read/write a project's documents and RTDB from an external app.
dashtro-client PyPI pip install dashtro-client Python equivalent of @dashtro/client, released in lockstep with it.
@dashtro/mcp npm npx @dashtro/mcp init npx-runnable MCP server — lets Claude/other MCP clients read and write your CMS content. Node port of cms_mcp/, no Python needed.
dashtro PyPI pip install dashtro The dashtro backup/restore CLI (cms_backend/scripts/cms_schema.py, see Backup / restore CLI). Also installs dashtro-mcp (direct-database-access MCP server) — use @dashtro/mcp instead unless you specifically need that.

Architecture

flowchart LR
    Browser["Browser SPA\n(cms-frontend)"] -->|"/api/*"| Backend["FastAPI backend\n(cms_backend)"]
    Backend --> DB[("sqlite / postgres")]
    ExternalApp["External app"] -->|"/api/sdk/*"| Backend
    ExternalApp -.->|uses| ClientSDK["@dashtro/client\ndashtro-client"]
    MCPClient["MCP client\n(Claude, etc.)"] -->|stdio, npx| MCPNode["@dashtro/mcp\n(Node)"]
    MCPNode -->|"/api/sdk/*"| Backend
    MCPClient -.->|stdio, local install| MCPPy["dashtro-mcp\n(cms_mcp, Python)"]
    MCPPy -->|"/api/sdk/*"| Backend

Everything ultimately talks to the same FastAPI backend — the frontend over its own JWT-authenticated API, and everything else (external apps via either client SDK, and MCP clients via either MCP server) over the API-key-scoped /api/sdk/* surface only. MCP clients never see a user's JWT — what an MCP client can do is exactly what its configured API key is scoped to (project/collections, read vs. write). Pick @dashtro/mcp for a zero-install npx setup, or cms_mcp/dashtro-mcp if you're already in a Python environment.

Data backends

DB_TYPE selects the storage backend, defaulting to sqlite:

  • sqlite — single-file DB, zero external dependencies. Default.
  • postgres — set DB_HOST/DB_PORT/DB_NAME/DB_USER/DB_PASSWORD. An optional bundled postgres compose service is available (see below) if you don't want to point at an external instance.

Both backends implement the same interface (get_data_client() / get_auth_client() / get_audit_client() in cms_backend/api/utils/__init__.py), so routers don't change depending on which one is active.

Running locally (dev)

Hot-reloading, separate frontend/backend containers, Cloudflare tunnel support:

cp .env.example .env   # fill in real values
npm run dev             # docker compose -f docker-compose.dev.yml up --build
npm run dev:down

Optional local Postgres instead of an external one:

docker compose -f docker-compose.dev.yml --profile postgres up

Running the image elsewhere

The published image serves both the API and the built SPA on port 7312, and reads its config from env vars (see below) — no other dependency beyond whichever DB_TYPE backend you point it at:

dashtro:
  image: ghcr.io/1atharvad/dashtro:latest
  environment:
    JWT_SECRET_KEY: ...
    CORS_ORIGINS: ...
    CMS_PUBLIC_URL: ...
    DB_TYPE: sqlite
    # ...see .env.example for the full list
  volumes:
    - uploads_data:/app/uploads

Put it behind whatever reverse proxy/tunnel the consuming project already uses to route a subdomain (e.g. admin.example.com) to it.

Environment variables

See .env.example for the full list (DB_TYPE, JWT_SECRET_KEY, CORS_ORIGINS, CMS_PUBLIC_URL, etc.).

Backup / restore CLI

cms_backend/scripts/cms_schema.py (installed as the dashtro console script) exports/imports schemas, documents, and media to/from a backup/ directory.

Local (direct database access)

# Export
dashtro export schema --project-id <id>
dashtro export documents --project-id <id> --workspace <name>
dashtro export media

# Import
dashtro import schema --project-id <id>
dashtro import documents --project-id <id> --workspace <name>
dashtro import media

Run against a running container:

docker exec <container> dashtro export schema --project-id <id> --backup-dir /app/backup
docker exec <container> dashtro import schema --project-id <id> --backup-dir /app/backup

Remote (HTTP API with authentication)

Use --base-url to export/import from/to a remote Dashtro instance. Requires an API key generated in the CMS settings.

Via command line:

# Export from remote
dashtro export schema --project-id <id> --base-url https://your-cms.com --api-key <api-key>
dashtro export documents --project-id <id> --workspace <name> --base-url https://your-cms.com --api-key <api-key>
dashtro export media --base-url https://your-cms.com --api-key <api-key>

# Import to remote
dashtro import schema --project-id <id> --base-url https://your-cms.com --api-key <api-key>
dashtro import documents --project-id <id> --workspace <name> --base-url https://your-cms.com --api-key <api-key>
dashtro import media --base-url https://your-cms.com --api-key <api-key>

Via environment variable (recommended):

export CMS_API_KEY=<api-key>
dashtro export schema --project-id <id> --base-url https://your-cms.com
dashtro import documents --project-id <id> --workspace <name> --base-url https://your-cms.com

Full backup/restore workflow

Export must run in order: schema → documents → media. Restore uses the same order:

# Backup from source instance
dashtro export schema --project-id abc123 --base-url https://source.com --api-key <key>
dashtro export documents --project-id abc123 --workspace production --base-url https://source.com --api-key <key>
dashtro export media --base-url https://source.com --api-key <key>

# Restore to destination instance
dashtro import schema --project-id abc123 --base-url https://dest.com --api-key <key>
dashtro import documents --project-id abc123 --workspace production --base-url https://dest.com --api-key <key>
dashtro import media --base-url https://dest.com --api-key <key>

Options:

Flag Meaning
--backup-dir Backup directory location (default: ./backup/)
--base-url Remote API URL (if omitted, uses direct database access)
--api-key API key for authentication (or set CMS_API_KEY env var)
--merge Merge documents instead of replacing (local mode only)

CI/CD

.github/workflows/build-image.yml runs on every push to main (or manually via workflow_dispatch):

  1. lint — frontend npm run lint + backend isort/black/ruff --check. Must pass before anything builds.
  2. build-and-push — builds Dockerfile.dashtro, pushes to ghcr.io/1atharvad/dashtro tagged latest and the commit SHA.

That's it — this repo doesn't deploy anywhere itself. Whatever consumes the image (e.g. the portfolio project) is responsible for pulling and running it.

Tests

npm test                              # everything: frontend, backend + cms_mcp, sdk/mcp

npm run test:frontend                 # cms-frontend (vitest)
npm run test:backend                  # cms_backend + cms_mcp (pytest, SQLite by default)
TEST_DB_TYPE=postgres pytest          # cms_backend, against a reachable Postgres
npm run test:mcp                      # sdk/mcp — @dashtro/mcp (vitest)

License

MIT

Release files for dashtro 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dashtro 0.2.0
File Size Uploaded
dashtro-0.2.0.tar.gz 45.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dashtro 0.2.0
File Interpreter ABI Platform
dashtro-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 95.7 kB

Release files / dashtro-0.2.0.tar.gz

Download URL dashtro-0.2.0.tar.gz
Size 45.2 kB
Tags Source
SHA-256 checksum
How to use checksums
71739a85b8b05158fc3b06c9954a6e599fc736c7b61384a305db2391f95d9ae2
BLAKE2b-256 checksum
How to use checksums
df4ec9fe8ffc0ec3ba5f7f38a1bdd982b945d5b0c37ecb9cb821d4e45f8b6412
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Aug 15, 2026.

Transparency log

Release files / dashtro-0.2.0-py3-none-any.whl

Download URL dashtro-0.2.0-py3-none-any.whl
Size 50.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
484c09c0f8ba28db88cea8894e7a36f38c9d76da5be0488d1bf0faa700611117
BLAKE2b-256 checksum
How to use checksums
f7270957f13adea778dfa4598e0bbf53d7222b02421191d31ef14ab48d0ea21d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Aug 15, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

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