"I'll be back... with a quote."
A self-hosted quote REST API with MCP support, built in C# / ASP.NET Core, deployable as a Docker container. Designed for homelab and self-hosted environments β serves real, verified quotes from films, books, and famous people over a clean REST API, with a Blazor management frontend and MCP tool support for AI assistants.
- Serve real, accurately attributed quotes via a clean REST API
- Source types: films, television, books, and famous people
- Quotes stored in their original language with optional curated translations
- Support the Model Context Protocol (MCP) so AI assistants can fetch quotes as a tool
- Ship as a Docker image (amd64 + arm64)
- Include a Blazor Server web frontend for managing quotes, users, and settings
- Stay maintainable by a single developer with standard .NET skills
Quotinator/
βββ src/
β βββ Quotinator.Api/ # ASP.NET Core β REST endpoints + Blazor Server UI (combined)
β βββ Quotinator.Changelog/ # Changelog library β models, schema validation, formatters
β βββ Quotinator.Constants/ # Route strings, tag names, error message keys (no dependencies)
β βββ Quotinator.Core/ # Domain models, interfaces, and the SQLite-backed service implementation
β βββ Quotinator.Data/ # Generic, reusable SQLite/Dapper infrastructure (domain-agnostic)
β βββ Quotinator.Data.Testing/ # Test helper library β stubs, fakes, and disposable SQLite DB
βββ tests/
β βββ Quotinator.Api.Tests/ # Endpoint integration tests (WebApplicationFactory)
β βββ Quotinator.Changelog.Tests/ # Changelog schema and generation tests
β βββ Quotinator.Constants.Tests/ # Tests for route and constant definitions
β βββ Quotinator.Core.Tests/ # Unit tests for domain logic, integration tests for the SQLite-backed implementation
β βββ Quotinator.Data.Example/ # Concrete example implementations of Data patterns (not a test runner)
β βββ Quotinator.Data.Testing.Tests/ # Tests for the Data.Testing helper library
β βββ Quotinator.Data.Tests/ # Integration tests for Data infrastructure (real SQLite, no fakes)
βββ addon/ # Home Assistant add-on manifest, config, and translations β stable channel
βββ addon-beta/ # Same as addon/, for the beta channel (same image, different slug/version)
βββ data/
β βββ sources/ # Bundled source files (one JSON per dataset) + manifest
βββ docker/
β βββ Dockerfile
β βββ docker-compose.yml
βββ docs/ # Architecture decisions, workflow, security, and reference docs
βββ schemas/ # JSON Schema files for source file validation and editor IntelliSense
βββ scripts/
β βββ SOURCES.md # Workflow for adding a new quote source via a converter plugin
β βββ changelog.csx # Changelog markdown generator (keepachangelog + HA add-on formats)
β βββ changelog-import.csx # Import tool for adding new changelog entries
β βββ changelog-upgrade.csx # Schema upgrade tool for changelog format migrations
βββ SOURCES.md # Attribution for seed data sources
βββ CLAUDE.md # AI assistant context (read this first)
βββ README.md
| Layer | Technology |
|---|---|
| Language | C# (.NET 10) |
| API | ASP.NET Core Minimal API |
| Frontend | Blazor Server |
| Data | SQLite (Dapper β no EF Core) |
| Logging | Serilog (programmatic configuration β HA container compatible) |
| Protocol | REST (MCP planned) |
| Container | Docker (linux/amd64 + linux/arm64) |
| Auth | API key required for admin endpoints; quote endpoints are public |
Quotinator's quote data lives in data/sources/ β one JSON file per dataset, normalised to the canonical schema. The bundled sources are:
quotinator-curated.jsonβ manually verified entries with enriched metadata (character names, genres, conversations)quotinator-series-universe.jsonβ curated Series/Universe groupings for Sources already present in the other bundled files (e.g. linking Star Wars films into a "Star Wars" Series/Universe); carries no quotes of its own- vilaboim/movie-quotes β AFI Top 100 movie quotes (~99 entries)
- NikhilNamal17/popular-movie-quotes β popular movie, TV, and anime quotes (~732 entries)
All external sources are MIT licensed. See SOURCES.md for full attribution and JSON Schema documentation.
The canonical quote schema is:
{
"id": "uuid-v4",
"quote": "Here's looking at you, kid.",
"originalLanguage": "en",
"source": "Casablanca",
"date": "1942",
"character": "Rick Blaine",
"author": null,
"type": "movie",
"genres": ["drama", "romance"],
"translations": {
"nl": { "quote": "Hier kijk ik naar je, kind.", "source": "Casablanca" }
}
}originalLanguageβ ISO 639-1 code; most entries are"en"(American English)sourceβ film/show title, book title, or speech occasiondateβ ISO 8601, as precise as the source has it:"1942","1940-06", or"1940-06-04"characterβ fictional character (movie/tv/anime/book fiction)authorβ book's author, or the real person forpersontype quotestypeβmovie,tv,anime,book, orpersongenresβ filter tags; standard values:action,adventure,animation,comedy,drama,fantasy,fiction,horror,mystery,non-fiction,romance,sci-fi,thrillertranslationsβ manually curated only; never auto-generated
API responses include language, originalLanguage, and isTranslated so consumers always know whether they received a translation or the original.
All endpoints accept an optional lang query parameter (ISO 639-1) to request a specific language. Responses always include language, originalLanguage, and isTranslated so consumers know whether they received a translation or the original. See docs/localisation.md for details.
See docs/api-endpoints.md for the full endpoint reference β every route, its query parameters, and a description of its behavior. For an interactive, always-current view of the same API on a running instance, use the Scalar reference at /scalar/v1 or the raw OpenAPI spec at /openapi/v1.json.
The web UI includes a language selector in the navbar. It overrides the browser's automatic language detection (English, Deutsch, Nederlands) and persists the choice as a cookie for one year. Selecting "Auto-detect" clears the override and returns to browser language detection.
Quotinator can be installed directly as a Home Assistant add-on. Click the button below to open your Home Assistant instance's app store with this repository pre-filled:
Then find Quotinator (stable) or Quotinator (BETA) in the store and click Install β both are independently installable, published from the same repository and image. See addon/DOCS.md for configuration options once installed (identical for both channels).
docker run -d \
-p 8080:8080 \
-v ./data:/data \
-e Quotinator__DataDir=/data \
ghcr.io/dutchjafo/quotinator:latestA docker-compose.yml example is included in the docker/ directory.
Always mount the persistent volume at /data and set Quotinator__DataDir=/data β never mount it at /app/data. Bundled quote sources are baked into the image at /app/data/sources/, and standalone Docker's data-directory default (when Quotinator__DataDir is unset) is that same /app/data path. Mounting a volume there hides the bundled sources under whatever is on the host (usually nothing on a first run), so the app starts with no quotes at all. See docs/docker.md for details.
The volume at /data contains everything Quotinator persists across restarts:
| Path | Purpose | Safe to delete? |
|---|---|---|
quotinatordata.db |
SQLite database β the live data store | No β this is your data |
backups/ |
Pre-migration database snapshots, named quotinatordata_v{N}_{timestamp}Z.db |
Yes β old backups can be pruned freely |
keys/ |
ASP.NET Core Data Protection keys β used to sign antiforgery tokens and Blazor session descriptors | No β deleting this invalidates all active browser sessions; the app recovers on restart but users will need to reload |
Note: Authentication is not yet implemented. The API is read-only and requires no credentials.
SSL is disabled by default. To enable HTTPS on port 8080, mount a certificate and key and pass the paths via environment variables:
docker run -d \
-p 8080:8080 \
-v ./data:/data \
-e Quotinator__DataDir=/data \
-v ./certs:/ssl:ro \
-e Quotinator__Ssl=true \
-e Quotinator__SslCertFile=/ssl/fullchain.pem \
-e Quotinator__SslKeyFile=/ssl/privkey.pem \
ghcr.io/dutchjafo/quotinator:latestWhen running behind a reverse proxy (NGINX, Caddy, Traefik) that terminates TLS, leave Quotinator__Ssl=false β the app reads X-Forwarded-Proto and sets cookies correctly.
- .NET 10 SDK
- Docker Desktop (optional, for container testing)
git clone https://github.com/DutchJaFO/Quotinator.git
cd quotinator
dotnet run --project src/Quotinator.ApiThe API will be available at https://localhost:7028. See docs/running-locally.md for all available URLs.
Upcoming work is tracked in GitHub Milestones.
See CHANGELOG.md for the full release history.
MIT. See LICENSE for details. Quote data attribution: see SOURCES.md.