Hadrian is an AI Gateway providing a unified OpenAI-compatible API for routing requests to multiple LLM providers. Fully open source, no restrictions — runs on anything from a Raspberry Pi to globally distributed cloud infrastructure.
Backend: Rust with Axum. Frontend: React 19, TypeScript, TailwindCSS. Config: hadrian.toml.
- Write high-quality, idiomatic code using modern language features — terse, not verbose
- Rely on linting, formatting, and type checking; aim for high test coverage
- No backwards compatibility concerns yet — modify migrations, schemas, APIs, config as needed (keep sqlite and postgres in sync)
- No unused imports,
todo!s, or dead code — implement or explain why not
Read files in agent_instructions/ for detailed guidance on specific tasks:
adding_admin_endpoint.md— Admin endpoints and pagination patternsadding_frontend_tool.md— Frontend toolsadding_provider.md— LLM providersarchitecture.md— Multi-tenancy, auth, RBAC, SSO, request flow, RAG, chat modes, cachingci_cd.md— CI, release, and deploy pipelinesconfiguration.md— Config sections, feature flags, provider optionsdatabase_changes.md— Database migrations and schema changesdocumentation.md— Documentation site, writing guidelines, Storybook embedsfrontend_conventions.md— Frontend conventions, accessibility (WCAG 2.1 AA)key_files.md— Comprehensive file listing by subsystemmodifying_chat_ui.md— Chat UI performance (stores, selectors, memoization)testing.md— Provider e2e tests (wiremock), university E2E testswasm.md— WASM build architecture and frontend development
If you encounter issues, unusual behavior, etc. during a session, you MUST document these quirks by updating this CLAUDE.md file and the agent instructions.
cargo build # Build (default: full features)
cargo build --release # Release build
cargo test # Unit tests
cargo test -- --ignored # Integration tests
cargo clippy # Lint
cargo +nightly fmt # Format (requires nightly)
cargo run # Run with default config (hadrian.toml)
cargo run -- --config path.toml # Run with custom config
cd deploy/tests && pnpm test # E2E tests with testcontainersHierarchical profiles (default: full):
tiny— OpenAI + Test providers, no DB, no embedded assetsminimal— tiny + all providers, SQLite, embedded UI/catalog, wizardstandard— minimal + Postgres, Redis, OTLP, Prometheus, SSO, CEL, S3, secrets managersfull— standard + SAML, Kreuzberg, ClamAVheadless— full without embedded assetswasm— Browser-only build (seeagent_instructions/wasm.md)
cargo build --no-default-features --features tiny # Smallest binary
cargo build --no-default-features --features minimal # Fast compile
cargo build --no-default-features --features standard # Typical deployment
cargo build --no-default-features --features headless # No embedded assetsServer runs on http://0.0.0.0:8080 by default.
cargo check— compile errorscargo clippy— lintcargo +nightly fmt— formatcargo test— tests
The UI is in ui/ — React 19, TypeScript, TailwindCSS, Storybook, @tanstack/react-query, hey-api.
cd ui
pnpm install # Install dependencies
pnpm dev # Dev server
pnpm build # Production build
pnpm lint:fix # Fix lint errors
pnpm format # Format code
pnpm storybook # Component development
pnpm test-storybook # Storybook tests
pnpm openapi-ts # Regenerate API client from /api/openapi.jsonpnpm lint:fix— fix lint errorspnpm format— formatpnpm test-storybook— Storybook testspnpm build— production build
Lint, formatting, and a11y errors must be resolved. If ignoring, prompt the user to explain why.
See agent_instructions/frontend_conventions.md for conventions and accessibility requirements.
Multi-tenancy: Organization → Teams → Users → Projects. Resources (conversations, providers, API keys, vector stores, files) owned by teams, users, or projects.
Principals: User (human identity), ServiceAccount (machine with roles), Machine (shared credential, no roles).
Request flow: Client → Middleware (auth → budget) → Route Handler (resolve provider) → LLM Provider (stream) → Usage Tracking (async).
See agent_instructions/architecture.md for details on RBAC, SSO, RAG, chat modes, and more.
- Admin endpoints:
/admin/v1/— OpenAI-compatible endpoints:/v1/ - OpenAI spec conformance with
**Hadrian Extension:**doc comments; verify with./scripts/openapi-conformance.py - Spec generates client for the frontend with
./scripts/generate-openapi.sh - Reference specs in
openapi/directory - Plural nouns for resources, consistent JSON error shapes
- Cursor-based pagination on all list endpoints (see
agent_instructions/adding_admin_endpoint.md)
- Unit tests: same file as code (
#[cfg(test)]) - E2E tests:
cd deploy/tests && pnpm test - Test both SQLite and PostgreSQL paths
- See
agent_instructions/testing.mdfor provider and university E2E tests
Docs site in docs/ using Fumadocs (Next.js). Keep docs up-to-date when code changes affect user-facing behavior.
cd docs && pnpm build # Build static site
cd docs && pnpm dev # Dev server at localhost:3000Read https://www.fumadocs.dev/llms.txt before updating docs (fetch with curl). See agent_instructions/documentation.md
for writing guidelines.
Every admin endpoint must extract Extension(authz): Extension<AuthzContext> and call
authz.require(resource, action) before any operation. Reference routes/admin/teams.rs.
Admin handler get_by_id() calls with org context must use org-scoped variants (get_by_id_and_org()).
User-supplied URLs the server fetches must go through validate_base_url() to block SSRF.
Never expose internal paths, UUIDs, infrastructure details, or secret manager references to clients.
Never return provider credentials in API responses. Never treat a secret reference as a literal value.
SQLite repos that use cursor pagination must call truncate_to_millis(Utc::now()) when creating
or updating timestamps. Cursors encode at millisecond precision; without truncation, SQLite TEXT
comparisons fail. See src/db/repos/cursor.rs for details.
Fail-closed: invalid credentials = 401, fail_on_evaluation_error = true, IAP auth requires explicit trusted_proxies.
- Use
/usr/bin/lsinstead ofls(aliased to exa) - Use
sleep 5snotsleep -s 5
RUST_LOG=debugfor verbose loggingobservability.logging.format = "pretty"for readable logs/healthfor DB connectivity,/docsfor docs,/api/docsfor Scalar API reference