Help us shape the future of Go! Take the 2026 Go Developer Survey

exemplar

command module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 28, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

README

exemplar

A library of test fixtures: synthetic emails, documents and records about a registry of fake people, and real ones anonymised through Presidio and reviewed in quarantine before use. Served over a REST API, a read-only MCP server and a CLI.

Project Structure

.
├── main.go                  # Application entry point
├── cmd/                     # CLI commands (serve, bootstrap, init, completion)
├── cmd/routes/              # HTTP routes: handler wiring and auth only
├── cmd/mcp/                 # MCP server tools
├── internal/
│   ├── app/                 # Opens the database and blob store, builds the services
│   ├── account/             # Accounts and API keys (handler -> service -> storage)
│   ├── dataset/             # Datasets and their access rules
│   ├── fixture/             # Fixtures, content and review state
│   ├── blob/                # Content-addressed file store
│   ├── audit/               # Append-only audit log
│   ├── principal/           # The authenticated caller passed to services
│   ├── auth/                # API key middleware (calls the account service)
│   ├── db/                  # SQLite connection and migrations
│   └── rest/                # JSON helpers and service error mapping
├── skills/                  # Agent skills to install (exemplar-anonymise)
├── build/                   # Build information
├── exemplar-config.toml     # Configuration file
├── Dockerfile               # Docker build file
├── docker-compose.yml       # exemplar and Presidio on one machine
├── exemplar.nomad           # exemplar and Presidio on Nomad
└── Taskfile.yml             # Task runner

All database and file access, and every access check, happens in the service layer. REST handlers, CLI commands and MCP tools get their services from internal/app and pass the caller's principal.Principal to every call.

Documentation

See docs/: architecture, security model, generation, data model, CLI, API, MCP and development conventions.

To let an AI agent anonymise files without reading the originals, install the exemplar-anonymise skill; see docs/anonymisation.md.

Features

  • Datasets of fixtures (global, workspace and quarantine) with accounts, scoped API keys, visibility rules and an audit log.
  • Synthetic data: a registry of fake people and companies, and generated emails, documents and records about them, reproducible from a seed.
  • Real data, anonymised: ingest emails and documents (text, Markdown, CSV, TSV, JSON, HTML, Twig and Jinja, DOCX, RTF) into quarantine for review, or anonymise files on the spot without storing anything (exemplar anonymise, also --local).
  • Three interfaces over the same services: REST, MCP (read-only) and the CLI.
  • Moving data: export and import of datasets, backups and restores, and pushing fixtures to Mailpit.

Install

brew install --cask martinsuchenak/tap/exemplar          # macOS
docker pull ghcr.io/martinsuchenak/exemplar:latest        # linux/amd64, linux/arm64

Or download an archive for Linux, macOS or Windows from the releases; each is signed and has an SBOM. To deploy it with Presidio, see docs/deployment.md.

Quick Start

# Create the first admin account and key (run on the host; prints the key once)
go run . bootstrap

# Lost the admin key? Revoke its keys and issue a new one (audit-logged)
go run . bootstrap --recover --name admin

# Run the service
go run . serve

# Use the CLI against it
export EXEMPLAR_API_KEY=exm_...
go run . datasets create --name replies
go run . upload <dataset-id> ./samples --scenario outlook-reply
go run . generate <dataset-id> --kind emails --count 24
go run . push-mailpit <dataset-id> --run my-test
go run . fixtures list <dataset-id>

# Or the API
curl -H "X-API-Key: $EXEMPLAR_API_KEY" http://localhost:8080/api/v1/me

bootstrap opens the database directly, so it only works on the host or inside the container (docker exec). It refuses to run once an admin exists.

To deploy it with Presidio, use docker compose up -d --build or the Nomad job exemplar.nomad; see docs/deployment.md.

Back up with exemplar backup --output FILE (also host-only, safe while the server runs) and move datasets between instances with exemplar export and exemplar import; see docs/backup-and-exchange.md.

Configuration

Configuration is loaded from exemplar-config.toml and can be overridden via environment variables or flags.

Option Env Var Default Description
log.level LOG_LEVEL info Log level (debug, info, warn, error)
log.format LOG_FORMAT text Log format (text, json)
server.host SERVER_HOST 0.0.0.0 HTTP server host
server.port SERVER_PORT 8080 HTTP server port
database.path DB_PATH data/exemplar.db SQLite database file (created with mode 600)
storage.blob_dir BLOB_DIR data/blobs Fixture contents, stored once per SHA-256
storage.max_fixture_mb MAX_FIXTURE_MB 100 Largest fixture accepted (0 for no limit)
storage.max_ingest_mb MAX_INGEST_MB 200 Largest ingest request (0 for no limit)
storage.max_import_mb MAX_IMPORT_MB 2048 Largest import archive (0 for no limit)
anonymiser.key_file ANON_KEY_FILE Anonymisation key file, mode 600; or the key in EXEMPLAR_ANON_KEY
anonymiser.presidio_url PRESIDIO_URL Presidio Analyzer, e.g. http://localhost:3000; with a key, turns ingest on
anonymiser.profiles_dir ANON_PROFILES_DIR Extra anonymisation profiles (*.toml)
mcp.enabled MCP_ENABLED true Read-only MCP endpoint at /mcp on the server port
client.server EXEMPLAR_SERVER http://localhost:8080 Server the remote CLI commands call
mailpit.smtp MAILPIT_SMTP localhost:1025 Where push-mailpit sends emails
(none) EXEMPLAR_API_KEY API key for remote CLI commands

Dependencies

Key dependencies used in this project:

License

Apache License 2.0 - see LICENSE and NOTICE.

Documentation

Overview

Command exemplar serves test fixtures over REST, MCP and a CLI.

Directories

Path Synopsis
Package build holds version information injected at build time with -ldflags: by Taskfile.yml, the Dockerfile and GoReleaser (.goreleaser.yml).
Package build holds version information injected at build time with -ldflags: by Taskfile.yml, the Dockerfile and GoReleaser (.goreleaser.yml).
cmd
Package cmd defines the CLI commands.
Package cmd defines the CLI commands.
mcp
Package mcp is the MCP endpoint; serve mounts Handler at /mcp on the same listener as the REST API.
Package mcp is the MCP endpoint; serve mounts Handler at /mcp on the same listener as the REST API.
routes
Package routes registers the HTTP routes.
Package routes registers the HTTP routes.
internal
account
Package account manages accounts and their API keys.
Package account manages accounts and their API keys.
anonymise
Package anonymise turns real emails into fixtures that are safe to share: it finds personal and commercial data (with a Presidio sidecar for names and Go patterns for the rest), replaces it with consistent fakes from the person registry, and keeps everything a mail parser depends on: MIME structure, transfer encodings, HTML markup, threading and cid: references.
Package anonymise turns real emails into fixtures that are safe to share: it finds personal and commercial data (with a Presidio sidecar for names and Go patterns for the rest), replaces it with consistent fakes from the person registry, and keeps everything a mail parser depends on: MIME structure, transfer encodings, HTML markup, threading and cid: references.
anonymise/anonymisetest
Package anonymisetest gives tests outside the anonymiser a stand-in for Presidio, Presidio clients that fail, and a few small real-looking emails.
Package anonymisetest gives tests outside the anonymiser a stand-in for Presidio, Presidio clients that fail, and a few small real-looking emails.
app
Package app opens the database and blob store and builds the services.
Package app opens the database and blob store and builds the services.
apperr
Package apperr defines the error kinds services return, so every caller (REST, CLI, MCP) can map them the same way with errors.Is.
Package apperr defines the error kinds services return, so every caller (REST, CLI, MCP) can map them the same way with errors.Is.
archive
Package archive reads and writes exemplar archives, the one format behind both export/import and backup/restore: a gzip-compressed tar whose first entry is manifest.json, followed by JSON-lines record files, an optional database snapshot and content blobs named by their SHA-256 hash.
Package archive reads and writes exemplar archives, the one format behind both export/import and backup/restore: a gzip-compressed tar whose first entry is manifest.json, followed by JSON-lines record files, an optional database snapshot and content blobs named by their SHA-256 hash.
audit
Package audit records security-relevant actions in the append-only audit_events table.
Package audit records security-relevant actions in the append-only audit_events table.
auth
Package auth holds HTTP authentication middleware.
Package auth holds HTTP authentication middleware.
backup
Package backup copies a whole instance into an archive (see archive) and restores one.
Package backup copies a whole instance into an archive (see archive) and restores one.
blob
Package blob stores file contents once, addressed by SHA-256, on the filesystem, and tracks them in the blobs table.
Package blob stores file contents once, addressed by SHA-256, on the filesystem, and tracks them in the blobs table.
client
Package client is a typed HTTP client for the exemplar REST API.
Package client is a typed HTTP client for the exemplar REST API.
ctxkeys
Package ctxkeys defines typed context keys shared across packages.
Package ctxkeys defines typed context keys shared across packages.
dataset
Package dataset manages datasets and decides who may read or change them.
Package dataset manages datasets and decides who may read or change them.
db
Package db opens the SQLite database and runs its migrations.
Package db opens the SQLite database and runs its migrations.
emailtemplate
Package emailtemplate turns one email into many: it swaps the people an email is about, with their companies, for other people, and gives the message fresh Message-IDs.
Package emailtemplate turns one email into many: it swaps the people an email is about, with their companies, for other people, and gives the message fresh Message-IDs.
exchange
Package exchange moves datasets between exemplar instances as archives (see archive): export writes chosen datasets with their fixtures, linked people and contents; import adds them here.
Package exchange moves datasets between exemplar instances as archives (see archive): export writes chosen datasets with their fixtures, linked people and contents; import adds them here.
fixture
Package fixture manages the items in a dataset: their metadata, their content (through the blob store) and the review state of real-derived data.
Package fixture manages the items in a dataset: their metadata, their content (through the blob store) and the review state of real-derived data.
generate
Package generate fills datasets with synthetic fixtures: files, emails and records about people from the person registry, and emails rendered from template emails (see emailtemplate).
Package generate fills datasets with synthetic fixtures: files, emails and records about people from the person registry, and emails rendered from template emails (see emailtemplate).
ingest
Package ingest takes real emails, anonymises them and stores the results in a quarantine dataset, where they wait for an admin's review.
Package ingest takes real emails, anonymises them and stores the results in a quarantine dataset, where they wait for an admin's review.
mailpit
Package mailpit sends stored email fixtures to a test SMTP server such as Mailpit.
Package mailpit sends stored email fixtures to a test SMTP server such as Mailpit.
mailpit/mailpittest
Package mailpittest is a minimal in-process SMTP server for tests: it accepts every message and records the envelope and data, like Mailpit.
Package mailpittest is a minimal in-process SMTP server for tests: it accepts every message and records the envelope and data, like Mailpit.
mimetree
Package mimetree parses an email into an editable MIME tree and writes it back with the same structure: header order, part order, boundaries, preambles and transfer encodings are kept, so a rewritten message parses the same way as the original.
Package mimetree parses an email into an editable MIME tree and writes it back with the same structure: header order, part order, boundaries, preambles and transfer encodings are kept, so a rewritten message parses the same way as the original.
person
Package person is the person registry: the fake people that records, generated files and emails refer to, so the same people appear everywhere.
Package person is the person registry: the fake people that records, generated files and emails refer to, so the same people appear everywhere.
principal
Package principal describes the authenticated caller that every service method receives.
Package principal describes the authenticated caller that every service method receives.
rest
Package rest holds JSON request and response helpers for HTTP handlers.
Package rest holds JSON request and response helpers for HTTP handlers.
scrub
Package scrub anonymises files on request and gives them back, storing nothing: the anonymise tool.
Package scrub anonymises files on request and gives them back, storing nothing: the anonymise tool.
storeutil
Package storeutil holds small helpers shared by the storage layers: IDs, timestamp and JSON column encoding, and constraint error detection.
Package storeutil holds small helpers shared by the storage layers: IDs, timestamp and JSON column encoding, and constraint error detection.
synth
Package synth generates fake but realistic test content: people, companies, business text, files (PDF, DOCX, XLSX, CSV, TXT, RTF, HTML, images), emails in many shapes, and records.
Package synth generates fake but realistic test content: people, companies, business text, files (PDF, DOCX, XLSX, CSV, TXT, RTF, HTML, images), emails in many shapes, and records.
testutil
Package testutil builds a real app (SQLite file and blob directory in a temp dir) and principals for service tests.
Package testutil builds a real app (SQLite file and blob directory in a temp dir) and principals for service tests.
textswap
Package textswap replaces many strings in text in one pass: longest match first, case-insensitively, only at word boundaries, and HTML-escaped forms too.
Package textswap replaces many strings in text in one pass: longest match first, case-insensitively, only at word boundaries, and HTML-escaped forms too.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL