Alembic is a computational perfumery platform that combines a curated ingredient database, harmony analysis, IFRA compliance checking, and AI-powered formula composition via Claude.
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Frontend │────▶│ Nginx │────▶│ Backend │
│ React/Vite │ │ (port 9500) │ │ FastAPI │
└──────────────┘ └──────────────┘ └──────┬───────┘
│
┌─────────────┼─────────────┐
│ │ │
┌─────▼────┐ ┌────▼─────┐ ┌───▼────────┐
│ Postgres │ │ Redis │ │ Anthropic │
│ (data) │ │ (cache) │ │ (AI API) │
└──────────┘ └──────────┘ └────────────┘
┌──────────────┐
│ MCP Proxy │──── Port 8633 ──── Gateway registration
│ (13 tools) │
└──────────────┘
Stack: Python 3.12, FastAPI, SQLAlchemy 2 (async), React 18, Vite, PostgreSQL 16, Redis 7
# Clone and configure
cp .env.example .env
# Edit .env — set JWT_SECRET (required) and ANTHROPIC_API_KEY (for AI composer)
# Start with Docker Compose
APP_PORT=9500 docker compose -f docker-compose.prod.yml up -d --build
# Seed ingredients (first run only)
docker compose -f docker-compose.prod.yml exec backend python3 scripts/seed_ingredients.py
# Open http://localhost:9500# Backend
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
uvicorn api.main:app --reload --port 8000
# Frontend
cd frontend && npm install && npm run dev
# Tests
python3 -m pytest tests/ -v # 276 tests
cd frontend && npm run build # Type check + build| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/ingredients |
Search ingredients (query, family, category, limit, offset) |
| GET | /api/v1/ingredients/{name} |
Get ingredient details |
| POST | /api/v1/harmony/check |
Check pairwise ingredient harmony |
| POST | /api/v1/harmony/matrix |
Full harmony matrix for ingredient set |
| GET | /api/v1/accords |
List accord definitions |
| GET | /api/v1/families |
List fragrance families |
| GET | /health |
Service health (postgres, redis, anthropic status) |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/auth/register |
Create account (username, email, password) |
| POST | /api/v1/auth/login |
Login (OAuth2 form, returns JWT pair) |
| POST | /api/v1/auth/refresh |
Exchange refresh token for new pair |
| GET | /api/v1/auth/me |
Current user profile |
| PATCH | /api/v1/auth/me |
Update profile (display_name, bio, skill_level) |
| POST | /api/v1/auth/change-password |
Change password |
| DELETE | /api/v1/auth/me |
Delete account (GDPR Art 17) |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/formulas |
Create formula with ingredients |
| GET | /api/v1/formulas |
List own + public formulas (pagination) |
| GET | /api/v1/formulas/{id} |
Get formula (own or public) |
| PATCH | /api/v1/formulas/{id} |
Update formula (owner only, bumps version) |
| DELETE | /api/v1/formulas/{id} |
Delete formula (owner only) |
| POST | /api/v1/formulas/{id}/fork |
Fork a public formula |
| GET | /api/v1/formulas/{id}/export |
Export as JSON, CSV, or brief |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/composer/compose |
AI-generate a formula from natural language |
| GET | /api/v1/composer/usage |
Monthly composition usage + remaining |
Tier limits: Free = 10/month, Pro = 100/month.
| Variable | Required | Default | Description |
|---|---|---|---|
JWT_SECRET |
Yes | — | HMAC-256 signing key for JWT tokens |
POSTGRES_DB |
No | alembic |
Database name |
POSTGRES_USER |
No | alembic |
Database user |
POSTGRES_PASSWORD |
No | change-me-in-production |
Database password |
REDIS_PASSWORD |
No | change-me-in-production |
Redis password |
ANTHROPIC_API_KEY |
No | — | Claude API key (required for AI composer) |
APP_PORT |
No | 80 |
HTTP listen port |
APP_ENV |
No | development |
production or development |
JWT_EXPIRE_MINUTES |
No | 15 |
Access token TTL |
JWT_REFRESH_EXPIRE_DAYS |
No | 7 |
Refresh token TTL |
RATE_LIMIT_LOGIN |
No | 5 |
Failed logins per 5-minute window |
RATE_LIMIT_COMPOSE |
No | 20 |
AI compositions per hour |
RATE_LIMIT_API |
No | 100 |
API calls per minute |
CORS_ORIGINS |
No | — | Additional CORS origins (comma-separated) |
The MCP proxy (port 8633) exposes 13 tools for agent access:
search_ingredients, get_ingredient, check_harmony, harmony_matrix,
analyze_formula, list_accords, get_accord, list_families, get_family,
compose_formula, list_formulas, get_formula, create_formula
Registered in the MCP gateway as the alembic backend.
# Start proxy standalone
ALEMBIC_BACKEND_URL=http://localhost:9500 python3 mcp_server/proxy.py
# Health check
curl http://localhost:8633/health
# Call a tool
curl -X POST http://localhost:8633/tools/call \
-H 'Content-Type: application/json' \
-d '{"name": "search_ingredients", "arguments": {"query": "rose"}}'225 tests across 8 test files:
| File | Tests | Coverage |
|---|---|---|
test_core.py |
37 | Core library functions |
test_api.py |
66 | Original API endpoints |
test_auth.py |
19 | Password hashing, JWT, validation |
test_auth_api.py |
20 | Auth endpoint integration |
test_formulas_api.py |
35 | Formula CRUD integration |
test_ifra.py |
38 | IFRA compliance + export |
test_rate_limit.py |
10 | Rate limiting logic |
test_health.py |
5 | Health endpoint |
python3 -m pytest tests/ -v
python3 -m pytest tests/ --cov=alembic_core --cov=apiAlembic checks formulas against IFRA (International Fragrance Association) guidelines:
- 11 IFRA categories (lip products through rinse-off)
- Concentration-aware: Actual skin level = ingredient% x concentration%
- Three outcomes: Compliant, Warning (within 20% of limit), Violation (exceeds limit)
- Prohibited ingredients flagged automatically
Disclaimer: IFRA data is for educational and hobbyist use. Professional perfumers should consult the latest IFRA standards directly. This tool does not replace professional safety assessment.
Production deployment uses Docker Compose with 4 services (postgres, redis, backend, frontend/nginx).
# Build and start
APP_PORT=9500 JWT_SECRET=your-secret-here \
docker compose -f docker-compose.prod.yml up -d --build
# Check health
curl http://localhost:9500/health
# View logs
docker compose -f docker-compose.prod.yml logs -f backendNginx adds security headers: X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, Referrer-Policy, Content-Security-Policy.
MIT