WaddleAI is an AI proxy and management system that provides OpenAI-compatible and Anthropic-compatible APIs with advanced routing, security, and dual-token usage management.
Key Features:
- OpenAI-compatible (
/v1/chat/completions) and Anthropic-compatible (/v1/messages) proxy endpoints, sharing one ordered pipeline (auth → rate limit → security → memory → dispatch → metering) - Multi-provider routing: OpenAI, xAI, Anthropic, Google Gemini, Ollama, llama.cpp, AWS Bedrock — SSE streaming across all of them
- Dual token system (WaddleAI tokens + raw LLM tokens)
- Role-based access control (Admin, Resource Manager, Reporter, User)
- 4-tier content filtering pipeline (regex → org rules → NER → LLM auditor)
- Quota and usage management, OpenTelemetry
gen_ai.*spans, Prometheus metrics
Python Stack:
- Python: 3.13 (see
.python-version/ CI matrix) - Web framework: Quart (async-native), served by hypercorn — Flask and Flask-Security-Too are retired, not present in this repo
- Database at runtime:
penguin-dalinservices/management/(the control plane). The proxy (proxy/apps/proxy_server/main.py) is the one exception: its synchronous API-key auth path (RBACManager.authenticate_api_keyinshared/auth/rbac.py) still uses raw PyDAL, offloaded to a thread pool viaasyncio.to_threadso it doesn't block the event loop. This is tracked debt, not the target state — fix-on-sight perbackend-database.mdif you touch that code path. - Schema & migrations: SQLAlchemy models + Alembic (
services/management/alembic/) — schema/migration authority only, never used for runtime queries.init_schema()(services/management/app/models_sqlalchemy.py) also idempotently creates missing tables on startup; Alembic upgrades are run manually or via a Helm-templated Kubernetes Job, never automatically at app boot. - Auth:
penguin-aaa(OIDC/JWT) — Flask-Security-Too is gone - Performance:
@dataclass(slots=True), type hints, async/await throughout
Frontend Stack:
- React (TypeScript) at
services/webui/, served via Express, built with Vite - Node.js: 24.x for build tooling
- Deployment: Kubernetes + Helm only, every environment — Docker Compose is
deprecated and not used anywhere in this repo, including local development. Docker
itself is still used to build service images and to run standalone dependency
containers (Postgres, Valkey) for direct-run local dev — see
docs/DEVELOPMENT.md. - CI/CD: GitHub Actions — see Release Pipeline below
- Monitoring: Prometheus metrics, OpenTelemetry
gen_ai.*span attributes on the dispatch span - Logging: Structured logging, configurable levels; no secrets in span/log data
- Primary: PostgreSQL +
pgvector(memory/embedding storage) — the only supported database; noDB_TYPEswitch in this repo today - Cache: Valkey (Redis-protocol-compatible) — replaces Redis throughout; the
REDIS_URLenv var name is historical
penguin-aaa: OIDC/JWT authentication and scope-based authorization- TLS: 1.2 minimum enforced
- Security scanning: prompt injection detection, jailbreak prevention, PII/PCI detection — see Content Filtering & Licence Model
NEVER take shortcuts or the "easy route" - ALWAYS prioritize safety, stability, and feature completeness
- No Quick Fixes: Resist quick workarounds or partial solutions
- Complete Features: Fully implemented with proper error handling and validation
- Safety First: Security, data integrity, and fault tolerance are non-negotiable
- Stable Foundations: Build on solid, tested components
- Future-Proof Design: Consider long-term maintainability and scalability
- No Technical Debt: Address issues properly the first time
- Skipping input validation "just this once"
- Hardcoding credentials or configuration
- Ignoring error returns or exceptions
- Commenting out failing tests to make CI pass
- Deploying without proper testing
- Using deprecated or unmaintained dependencies
- Implementing partial features with "TODO" placeholders
- Bypassing security checks for convenience
- Assuming data is valid without verification
- Leaving debug code or backdoors in production
- All error cases handled properly
- Unit tests cover all code paths
- Integration tests verify component interactions
- Security requirements fully implemented
- Performance meets acceptable standards
- Documentation complete and accurate
- Code review standards met
- No hardcoded secrets or credentials
- Logging and monitoring in place
- Build passes in containerized environment
- No security vulnerabilities in dependencies
- Edge cases and boundary conditions tested
- NEVER commit automatically unless explicitly requested by the user
- NEVER push to remote repositories under any circumstances
- ONLY commit when explicitly asked - never assume commit permission
- Always use feature branches for development
- Require pull request reviews for main branch
- Automated testing must pass before merge
- ALWAYS maintain local .PLAN and .TODO files for crash recovery
- Keep .PLAN file updated with current implementation plans and progress
- Keep .TODO file updated with task lists and completion status
- Update these files in real-time as work progresses
- Add to .gitignore: Both .PLAN and .TODO files must be in .gitignore
- File format: Use simple text format for easy recovery
- Automatic recovery: Upon restart, check for existing files to resume work
- ALWAYS check for Dependabot alerts before every commit
- Monitor vulnerabilities via Socket.dev for all dependencies
- Mandatory security scanning before any dependency changes
- Fix all security alerts immediately - no commits with outstanding vulnerabilities
- Regular security audits:
pip-audit,npm audit(seemake test-security)
- ALL code must pass
make lintbefore commit — no exceptions - Python:
ruff(lint + format + import sort — the only Python linter/formatter; flake8/black/isort are not used),mypy(advisory, not a hard gate today),bandit(security) - JavaScript/TypeScript: ESLint (
services/webui/) - Docker: hadolint
- YAML: yamllint
- OpenAPI:
spectral lint openapi/v1.yaml(make openapi-lint) - CodeQL: Python, JavaScript/TypeScript and Actions —
.github/workflows/codeql.yml - PEP Compliance: PEP 8, PEP 257 (docstrings), PEP 484 (type hints)
- NEVER mark tasks as completed until successful build verification
- All Python and Node builds MUST be executed within Docker containers (CI and, where practical, local)
- Build failures must be resolved before task completion
- README.md: Keep as overview and pointer to comprehensive docs/ folder
- docs/ folder: Create comprehensive documentation for all aspects
- RELEASE_NOTES.md: Maintain in docs/ folder, prepend new version releases to top
- Update CLAUDE.md when adding significant context
- Build status badges: Always include in README.md
- Company homepage: Point to www.penguintech.io
- License: All projects use Limited AGPL3 with preamble for fair use
- Maximum file size: 25,000 characters for ALL code and markdown files
- Split large files: Decompose into modules, libraries, or separate documents
- CLAUDE.md exception: Maximum 39,000 characters (only exception to 25K rule)
- Use Task Agents: Utilize task agents (subagents) for large file operations
All projects integrate with the centralized PenguinTech License Server at https://license.penguintech.io for feature gating and enterprise functionality.
IMPORTANT: License enforcement is ONLY enabled when project is marked as release-ready
- Development phase: All features available, no license checks
- Release phase: License validation required, feature gating active
License Key Format: PENG-XXXX-XXXX-XXXX-XXXX-ABCD
Core Endpoints:
POST /api/v2/validate- Validate licensePOST /api/v2/features- Check feature entitlementsPOST /api/v2/keepalive- Report usage statistics
Environment Variables:
LICENSE_KEY=PENG-XXXX-XXXX-XXXX-XXXX-ABCD
LICENSE_SERVER_URL=https://license.penguintech.io
PRODUCT_NAME=waddleai
RELEASE_MODE=false # Development (default)The content filter (shared/security/content_filter.py) runs a 4-tier pipeline:
- Regex patterns — 23 built-in PII/PCI patterns (credit cards, SSNs, phone numbers, emails, API keys, etc.), toggleable per organization — never licence-gated
- Custom organization rules — org-defined pattern overrides — never licence-gated
- NER (Named Entity Recognition) — Presidio + spaCy (
en_core_web_lg), transformer fallback, for PERSON/LOCATION/NRP/MEDICAL_LICENSE and more — licence-gated: both a PostHog flag and apenguin_licensingentitlement (NER_TIER_LICENSE_FEATURE = "pii_ner_detection") must pass; if either check is unavailable or fails, the NER tier is skipped and tiers 1–2 still run, so a caller without a licence client gets baseline PII protection rather than none - LLM auditor — ShieldGemma 2B default safety classifier (YES/NO policy format) — not licence-gated by tier, but governed by the same PostHog-flag-first pattern as every other feature
Tiers 1 and 2 are always available regardless of licence tier; tier 3 (NER) is the only
one gated on penguin_licensing/PostHog.
Format: vMajor.Minor.Patch.build
- Major: Breaking changes, API changes, removed features
- Minor: Significant new features and functionality additions
- Patch: Minor updates, bug fixes, security patches
- Build: Epoch64 timestamp of build time
Update Commands:
./scripts/version/update-version.sh # Increment build timestamp
./scripts/version/update-version.sh patch # Increment patch version
./scripts/version/update-version.sh minor # Increment minor version
./scripts/version/update-version.sh major # Increment major versionwaddleai/
├── proxy/ # OpenAI/Anthropic-compatible data-plane (Quart, port 8080)
├── services/
│ ├── management/ # Control plane API (Quart, port 8001)
│ ├── webui/ # React/TypeScript frontend (Express-served)
│ └── penguincode/ # Vendored PenguinCode CLI/extension (own release story)
├── shared/ # Shared library code (auth, security, DAL helpers)
├── k8s/helm/waddleai/ # The one Helm chart deploying every environment
├── docs/ # Documentation (this tree + docs/docs-site/ mkdocs site)
├── tests/ # unit/, contract/, integration/, e2e/, smoke/
├── openapi/v1.yaml # Generated management API OpenAPI 3.x spec
├── Makefile # Build/test/lint/deploy automation
├── .version # Version tracking
└── CLAUDE.md # This file
There is no root docker-compose.yml — the Helm chart is the only supported way to run
more than one service together, in every environment including local dev (see
docs/DEVELOPMENT.md for the direct-run-processes alternative for fast local iteration).
The full, current target list, defined in the root Makefile (run make help-style
grep -nE '^[a-z][a-z-]*:' Makefile to regenerate this list):
| Target | Purpose |
|---|---|
make venv |
Create .venv (Python 3.13) from the hash-pinned lockfiles — published deps only |
make setup |
install-hooks |
make install-hooks |
Install the pre-commit framework + register pre-commit/pre-push hooks |
make verify-hooks |
Report whether the hooks are installed and non-empty |
make dev |
Start development services |
make build |
Build all services |
make docker-build |
Build container images |
make docker-push |
Push container images |
make lint |
Lint everything — fails on error, no || true, no silent skips |
make generate-openapi |
Regenerate openapi/v1.yaml from the quart-schema annotations |
make openapi-lint |
spectral lint openapi/v1.yaml — gates on error |
make test |
test-unit |
make test-unit |
pytest tests/unit — 90% coverage gate, branch coverage on |
make test-integration |
pytest tests/integration |
make test-e2e |
pytest tests/e2e |
make test-functional |
(placeholder) |
make test-contract |
pytest tests/contract — request/response snapshot tests |
make test-security |
Security scans over first-party code — bandit, gitleaks, pip-audit, npm audit, licence gate; fails on findings |
make smoke-test |
Fast post-build verification |
make smoke-test-production |
Live prod checks (network + real deployment required) — not part of pre-commit |
make seed-mock-data |
Mock data seeding |
make clean |
Remove build artifacts |
make deploy-dev |
Deploy to dev/alpha |
make deploy-prod |
Deploy to production |
make pre-commit |
The pre-commit gate sequence |
See docs/PRE_COMMIT.md, docs/TESTING.md, and docs/DEVELOPMENT.md for the full
procedures behind each target.
.coveragerc: fail_under = 90, branch = True. Coverage source is shared and
services/management/app; pytest.ini additionally instruments proxy for reporting.
Run pytest tests/unit -v --cov-report=term-missing to see exactly which lines are
uncounted before you add a test — the Missing column names the line ranges, not just a
percentage. CI also asserts a minimum collected-test count for tests/unit, so a
misconfigured path filter that silently collects zero tests fails loudly instead of
reporting a false "clean" 90%.
Five tiers, each with its own source branch/event, tag, and cluster — canonical table
lives in docs/docs-site/docs/deployment/kubernetes.md (§Release pipeline):
| Tier | Source | Tag | Cluster / deploy |
|---|---|---|---|
| Pre-alpha | feature/ fix/ chore/ hotfix/ docs/ refactor/ branches |
build-only in CI (not pushed) | Local K8s, destroy + fresh |
| Alpha | release/v{Major}.{Minor}.X branches |
alpha-<epoch64> |
Local K8s today, destroy + fresh |
| Beta | Merge to main |
beta-<epoch64> |
dal2-beta context, destroy + fresh |
| Gamma | GitHub release flagged pre-release | gamma-<epoch64> |
DigitalOcean (context TBD), upgrade in place — the only upgrade-path test |
| Prod | GitHub release (non-pre-release), v1.x+ only | v{Major}.{Minor}.{Patch} |
DigitalOcean, separate cluster (not built yet), upgrade in place |
Namespace is always waddleai in every context — never environment-suffixed; registry
is ghcr.io/penguintechinc/waddleai/{proxy,management,webui,ollama}.
WaddleAI provides a fully compatible OpenAI API that can be used as a drop-in replacement:
import openai
client = openai.OpenAI(
api_key="<your-waddleai-key>", base_url="https://your-waddleai-proxy.com/v1"
)
response = client.chat.completions.create(
model="gpt-4", messages=[{"role": "user", "content": "Hello, how are you?"}]
)
print(response.choices[0].message.content)import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: '<your-waddleai-key>',
baseURL: 'https://your-waddleai-proxy.com/v1'
});
const completion = await openai.chat.completions.create({
messages: [{ role: 'user', content: 'Hello!' }],
model: 'gpt-4',
});import requests
auth_response = requests.post(
"https://your-waddleai-mgmt.com/auth/login",
json={"username": "admin", "password": "your-password"},
)
token = auth_response.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}usage = requests.get(
"https://your-waddleai-mgmt.com/analytics/tokens/waddleai", headers=headers
).json()quota_update = requests.post(
"https://your-waddleai-mgmt.com/analytics/quotas/user123",
headers=headers,
json={"monthly_limit": 200000, "daily_limit": 20000},
)- Full system access via management API
- All endpoints and functionality available
- Cross-organization visibility and control
- Organization-scoped quota management
- User management within assigned organizations
- Token limit control for assigned organizations
- Read-only analytics and reporting for assigned organizations
- Usage trend analysis and reporting
- Security incident reporting
- OpenAI-compatible API access only
- Personal API key management
- Own usage statistics
WaddleAI uses a dual token system for accurate billing and analytics:
- Normalized billing units across all LLM providers
- Used for quota enforcement and cost calculation
- Raw provider token counts (input/output)
- Used for detailed analytics and optimization
{
"usage": {
"prompt_tokens": 100,
"completion_tokens": 50,
"total_tokens": 150,
"waddleai_tokens": 15,
}
}# Automatic routing
response = client.chat.completions.create(
model="smart-router", messages=[{"role": "user", "content": "Complex reasoning task..."}]
)
# Force specific provider
response = client.chat.completions.create(
model="ollama:llama2", messages=[{"role": "user", "content": "Local processing needed"}]
)response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "Remember my preferences"}],
extra_headers={
"X-WaddleAI-Memory": "user-session-123",
"X-WaddleAI-Memory-Type": "conversation",
},
)# Proxy Server -- binds 0.0.0.0 always; only the port is configurable
export HTTP_PORT=8080 # default; proxy/apps/proxy_server/main.py
export DATABASE_URL=postgresql://user:pass@localhost/waddleai
export JWT_SECRET=your-jwt-secret
export SECURITY_POLICY=balanced
# Management Server -- no host/port env var; bound via hypercorn CLI/Dockerfile
# (0.0.0.0:8001, see services/management/Dockerfile)
export ADMIN_INITIAL_PASSWORD=secure-admin-passwordFull variable list and defaults: services/management/app/config.py,
proxy/apps/proxy_server/main.py, and k8s/helm/waddleai/values.yaml. There is no
.env.example in this repo — see docs/DEVELOPMENT.md.
Every environment deploys via the Helm chart at k8s/helm/waddleai — see
Release Pipeline / CI Image Tags above and
docs/docs-site/docs/deployment/kubernetes.md for the full procedure. Local multi-service
runs go through ./scripts/deploy-alpha.sh against a MicroK8s/Docker Desktop cluster;
there is no Docker Compose path.
curl https://your-waddleai-proxy.com/healthz
curl https://your-waddleai-proxy.com/readyz
curl https://your-waddleai-proxy.com/api/status
curl https://your-waddleai-proxy.com/metricsManagement (port 8001) exposes the equivalent /healthz and /readyz.
401- Invalid or expired API key/token403- Insufficient permissions429- Rate limit or quota exceeded400- Blocked by security scanning503- Service unavailable
- Port Conflicts: Check what's bound to 8080 (proxy) / 8001 (management) / 3000 (webui)
- Database Connections: Verify
DATABASE_URLand that Postgres/pgvector is reachable - License Validation: Check license key format and network access to
license.penguintech.io - Build Failures: Check dependency versions; builds run inside containers
- Quota Exceeded: Monitor WaddleAI token consumption
kubectl --context <ctx> logs -n waddleai -l app=waddleai-proxy -f
kubectl --context <ctx> exec -it -n waddleai deploy/waddleai-proxy -- /bin/shFor direct-run local dev (no cluster), each service logs to its own terminal — see
docs/DEVELOPMENT.md.
- Use environment variables for API keys
- Rotate keys regularly
- Use minimal required permissions
- Monitor usage patterns
- Implement connection pooling
- Cache frequently used data
- Monitor response times
- Use appropriate models for tasks
- Monitor WaddleAI token consumption
- Set appropriate quotas
- Use cheaper models when possible
- Implement usage alerts
- Technical Documentation: See
/docs/folder - Integration Support: support@penguintech.io
- Sales Inquiries: sales@penguintech.io
- License Server Status: https://status.penguintech.io
Version: 1.1.0 Last Updated: 2026-08-21 Maintained by: Penguin Tech Inc License Server: https://license.penguintech.io