Quota is a single-tenant quotation system for self-hosted small businesses. It runs on Astro SSR with Cloudflare Workers, stores business data in D1, stores generated files and brand assets in R2, and exposes the same HTTP API to the web app and the MCP server.
The repository ships blank by default. Demo brand assets and the original reference workbook live under examples/ only:
examples/demo-brand/examples/demo-reference/
Do not deploy the demo assets unless you intentionally want to test with them.
- Web quote list, client management, company settings, quote editing, preview, and XLSX download.
- D1-backed company, client, quote, and quote item data.
- R2-backed quote files and brand assets.
- Token-gated HTTP API for automation and MCP.
seed-brandCLI for pushing logo, stamp, and bank images into any deployed Quota API.
pnpm install
pnpm test
pnpm buildFor local development, create the local D1 schema before opening pages that read the database:
pnpm wrangler d1 migrations apply quota --local
pnpm devOpen http://127.0.0.1:4321/settings and fill your own company profile.
For the production deployment checklist, resource creation commands, Cloudflare Access setup, canary, and smoke checks, use DEPLOY.md.
Create your own Cloudflare resources:
pnpm wrangler d1 create quota
pnpm wrangler r2 bucket create quota-filesUpdate the D1 database_id, D1 database_name, and R2 bucket_name in the Wrangler deploy config used by your environment. Keep the binding names as:
- D1:
DB - R2:
FILES
Set a machine API token as a Cloudflare secret:
pnpm wrangler secret put QUOTA_API_TOKENUse a long random value. Store the source value in your password manager or platform secret store, not in Git.
Apply remote migrations and deploy:
pnpm wrangler d1 migrations apply quota --remote
pnpm build
pnpm wrangler deployAfter deploy, configure your own company data in one of two ways.
Open /settings and fill:
- company name, address, and phone
- bank transfer text
- default tax rate and notes
- logo, stamp, and bank image files
When company identity and brand assets are blank, /settings shows a first-run setup guide.
Use seed-brand when you want to push local image files to a deployed Quota instance:
QUOTA_API_URL="https://your-quota.example.workers.dev" \
QUOTA_API_TOKEN="$(op read op://your-vault/quota-api-token)" \
pnpm seed-brand -- \
--logo ./brand/logo.png \
--stamp ./brand/stamp.png \
--bank ./brand/bank.jpgYou can also pass --url and --token flags:
pnpm seed-brand -- \
--url "https://your-quota.example.workers.dev" \
--token "$QUOTA_API_TOKEN" \
--logo ./brand/logo.pngAccepted image types are PNG and JPEG. The CLI uploads to:
/api/company/brand/logo/api/company/brand/stamp/api/company/brand/bank
The API writes stable R2 keys such as brand/logo.png and updates the company profile keys in D1.
The MCP server lives in packages/mcp and calls the Quota HTTP API with the same bearer token.
pnpm --dir packages/mcp install
pnpm --dir packages/mcp build
pnpm --dir packages/mcp testConfigure the MCP runtime environment:
export QUOTA_API_URL="https://your-quota.example.workers.dev"
export QUOTA_API_TOKEN="$(op read op://your-vault/quota-api-token)"Add it to Claude after publishing or linking your MCP package:
claude mcp add quota -- npx -y quota-mcpBefore deploying changes:
pnpm test
pnpm build
pnpm --dir packages/mcp testQUOTA_API_TOKENmust be provided by environment/secret store only.- Never commit real tokens, bank data, customer data, or production exports.
- The brand upload API only accepts
logo,stamp, andbankassets. - The app is designed for one company per deployment, not multi-tenant SaaS.