Skip to content

Commit 12796e0

Browse files
committed
Cargo publish headlessly
1 parent 0102774 commit 12796e0

9 files changed

Lines changed: 182 additions & 55 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ jobs:
7070
strategy:
7171
fail-fast: false
7272
matrix:
73-
features: [tiny, minimal, standard, full]
73+
features: [tiny, minimal, standard, full, headless]
7474
steps:
7575
- uses: actions/checkout@v4
7676

.github/workflows/release.yml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ jobs:
7979
include:
8080
- target: x86_64-unknown-linux-gnu
8181
os: ubuntu-latest
82-
features: "full standard minimal tiny"
82+
features: "full headless standard minimal tiny"
8383
- target: x86_64-unknown-linux-musl
8484
os: ubuntu-latest
8585
features: "standard minimal tiny"
@@ -88,7 +88,7 @@ jobs:
8888
features: "standard minimal tiny"
8989
- target: aarch64-apple-darwin
9090
os: macos-latest
91-
features: "full standard minimal tiny"
91+
features: "full headless standard minimal tiny"
9292
- target: x86_64-pc-windows-msvc
9393
os: windows-latest
9494
features: "standard minimal tiny"
@@ -236,16 +236,13 @@ jobs:
236236
with:
237237
shared-key: publish
238238

239-
- name: Fetch model catalog
240-
run: mkdir -p data && curl -sSL https://models.dev/api.json -o data/models-dev-catalog.json
241-
242239
- name: Set version from tag
243240
run: |
244241
VERSION="${GITHUB_REF_NAME#v}"
245242
sed -i "s/^version = \".*\"/version = \"$VERSION\"/" Cargo.toml
246243
247244
- name: Publish to crates.io
248-
run: cargo publish --no-default-features --features tiny --allow-dirty
245+
run: cargo publish --no-default-features --features headless --allow-dirty
249246
env:
250247
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
251248

Cargo.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ tiny = ["provider-openai", "provider-test"]
2828
minimal = [
2929
"tiny",
3030
"database-sqlite",
31+
"embed-catalog",
3132
"embed-ui",
3233
"provider-anthropic",
3334
"provider-azure",
@@ -63,6 +64,40 @@ full = [
6364
"virus-scan",
6465
]
6566

67+
# All features except embedded assets (UI, docs, catalog).
68+
# Suitable for `cargo install` users who don't have build artifacts,
69+
# and for deployments that serve the frontend separately.
70+
headless = [
71+
"cel",
72+
"csv-export",
73+
"database-postgres",
74+
"database-sqlite",
75+
"document-extraction-basic",
76+
"document-extraction-full",
77+
"forecasting",
78+
"json-schema",
79+
"otlp",
80+
"prometheus",
81+
"provider-anthropic",
82+
"provider-azure",
83+
"provider-bedrock",
84+
"provider-openai",
85+
"provider-test",
86+
"provider-vertex",
87+
"redis",
88+
"response-validation",
89+
"s3-storage",
90+
"saml",
91+
"secrets-aws",
92+
"secrets-azure",
93+
"secrets-gcp",
94+
"sso",
95+
"utoipa",
96+
"vault",
97+
"virus-scan",
98+
"wizard",
99+
]
100+
66101
# Providers (always-available: openai, anthropic, test)
67102
provider-openai = []
68103
provider-anthropic = []
@@ -99,6 +134,7 @@ s3-storage = ["aws-sdk", "dep:aws-sdk-s3"]
99134
# Embedded assets
100135
embed-ui = ["dep:rust-embed"]
101136
embed-docs = ["dep:rust-embed"]
137+
embed-catalog = ["dep:rust-embed"]
102138

103139
# Databases
104140
database-sqlite = ["dep:sqlx", "sqlx/sqlite"]
@@ -214,7 +250,7 @@ opentelemetry-otlp = { version = "0.30", features = ["trace", "logs", "grpc-toni
214250
opentelemetry-semantic-conventions = { version = "0.30", optional = true }
215251
opentelemetry_sdk = { version = "0.30", features = ["rt-tokio", "logs"], optional = true }
216252
redis = { version = "0.32.7", features = ["aio", "tokio-comp", "cluster-async"], optional = true }
217-
rust-embed = { version = "8", features = ["mime-guess"], optional = true }
253+
rust-embed = { version = "8", features = ["mime-guess", "include-exclude"], optional = true }
218254
samael = { version = "0.0.19", optional = true }
219255
schemars = { version = "0.8", optional = true }
220256
sqlx = { version = "0.8", features = ["runtime-tokio", "uuid", "chrono", "rust_decimal", "migrate", "json"], optional = true }

docs/content/docs/deployment/index.mdx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,13 @@ Hadrian uses Cargo feature flags for modular compilation. Choose a profile based
9191

9292
### Profiles
9393

94-
| Profile | Features | Use Case |
95-
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
96-
| `tiny` | OpenAI + Test providers only — no database, no embedded assets | Stateless API proxy, smallest binary |
97-
| `minimal` | tiny + all providers (Anthropic, Azure, Bedrock, Vertex), SQLite, embedded UI, wizard | Development, Windows, embedded |
98-
| `standard` | minimal + PostgreSQL, Redis, OTLP, Prometheus, CEL, SSO, basic doc extraction, embedded docs, OpenAPI docs, S3, secrets managers (AWS/Azure/GCP/Vault), forecasting, JSON schema, response validation, CSV export | Typical deployment |
99-
| `full` (default) | standard + SAML, Kreuzberg (full doc extraction), ClamAV (virus scan) | Production multi-tenant |
94+
| Profile | Features | Use Case |
95+
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
96+
| `tiny` | OpenAI + Test providers only — no database, no embedded assets | Stateless API proxy, smallest binary |
97+
| `minimal` | tiny + all providers (Anthropic, Azure, Bedrock, Vertex), SQLite, embedded UI, embedded catalog, wizard | Development, Windows, embedded |
98+
| `standard` | minimal + PostgreSQL, Redis, OTLP, Prometheus, CEL, SSO, basic doc extraction, embedded docs, OpenAPI docs, S3, secrets managers (AWS/Azure/GCP/Vault), forecasting, JSON schema, response validation, CSV export | Typical deployment |
99+
| `full` (default) | standard + SAML, Kreuzberg (full doc extraction), ClamAV (virus scan) | Production multi-tenant |
100+
| `headless` | All `full` features except embedded assets (no UI, docs, or catalog) | `cargo install`, separate frontend, CI/CD |
100101

101102
Build with a specific profile:
102103

@@ -112,6 +113,9 @@ cargo build --release --no-default-features --features standard
112113

113114
# Everything enabled (default)
114115
cargo build --release
116+
117+
# All features without embedded assets — for cargo install or separate frontend
118+
cargo build --release --no-default-features --features headless
115119
```
116120

117121
### Individual Feature Flags
@@ -128,6 +132,7 @@ Select individual features for a custom build by combining flags with `--no-defa
128132
| | `provider-azure` | Azure OpenAI (pulls in `azure-sdk`) | minimal |
129133
| **Assets** | `embed-ui` | Embedded web UI | minimal |
130134
| | `embed-docs` | Embedded documentation site | standard |
135+
| | `embed-catalog` | Embedded model catalog from models.dev | minimal |
131136
| **Databases** | `database-sqlite` | SQLite database support | minimal |
132137
| | `database-postgres` | PostgreSQL database support | standard |
133138
| **Secrets** | `vault` | HashiCorp Vault | standard |

docs/content/docs/getting-started.mdx

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,16 @@ Get Hadrian Gateway running in under a minute. No complex setup required.
99

1010
## Quick Start
1111

12-
### Using Cargo (Recommended)
12+
### Pre-built Binary (Recommended)
1313

14-
```bash
15-
# Download and run (creates config automatically)
16-
cargo install hadrian
17-
hadrian
14+
Download the latest release for your platform from the
15+
[GitHub Releases](https://github.com/ScriptSmith/hadrian/releases) page. Pre-built binaries
16+
include the embedded web UI, documentation, and model catalog.
1817

19-
# Or with a custom config
20-
hadrian --config hadrian.toml
18+
```bash
19+
# Extract and run (creates config automatically)
20+
tar xzf hadrian-*.tar.gz
21+
./hadrian
2122
```
2223

2324
### Using Docker
@@ -30,7 +31,21 @@ docker run -p 8080:8080 -e OPENROUTER_API_KEY=sk-... ghcr.io/ScriptSmith/hadrian
3031
docker run -p 8080:8080 -v ./hadrian.toml:/etc/hadrian/hadrian.toml ghcr.io/ScriptSmith/hadrian
3132
```
3233

33-
The gateway starts at `http://localhost:8080` with the chat UI. No database required for basic use.
34+
### Using Cargo
35+
36+
```bash
37+
cargo install hadrian
38+
hadrian
39+
```
40+
41+
<Callout type="warn">
42+
`cargo install` builds the **headless** profile, which includes all backend features but does not
43+
embed the web UI, documentation site, or model catalog. For the full experience with embedded
44+
assets, use a [pre-built binary](https://github.com/ScriptSmith/hadrian/releases) or [build from
45+
source](#building-from-source).
46+
</Callout>
47+
48+
The gateway starts at `http://localhost:8080` with the chat UI (when embedded assets are included). No database required for basic use.
3449

3550
## Zero-Config Mode
3651

@@ -115,13 +130,34 @@ open http://localhost:8080/api/docs
115130

116131
## Building from Source
117132

133+
Build Hadrian with all features and embedded assets:
134+
118135
```bash
119136
git clone https://github.com/ScriptSmith/hadrian.git
120-
cd hadrian/gateway
137+
cd hadrian
138+
139+
# Build frontend assets
140+
cd ui && pnpm install && pnpm build && cd ..
141+
142+
# Build documentation site
143+
cd docs && pnpm install && pnpm build && cd ..
144+
145+
# Fetch model catalog
146+
./scripts/fetch-model-catalog.sh
147+
148+
# Build the full binary
121149
cargo build --release
122150
./target/release/hadrian
123151
```
124152

153+
To build without frontend assets (headless), skip the UI/docs/catalog steps:
154+
155+
```bash
156+
cargo build --release --no-default-features --features headless
157+
```
158+
159+
See [Deployment](/docs/deployment) for build profiles and feature flags.
160+
125161
## Next Steps
126162

127163
<Cards>

docs/content/docs/index.mdx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ Hadrian is an **AI Gateway** that provides a unified OpenAI-compatible API for r
2727

2828
## Documentation Sections
2929

30-
| Section | Description |
31-
| ---------------------------------------- | ----------------------------------- |
32-
| [Getting Started](/docs/getting-started) | Installation and basic setup |
33-
| [Configuration](/docs/configuration) | All configuration options |
34-
| [Features](/docs/features) | Detailed feature guides |
35-
| [API Reference](/docs/api) | OpenAI-compatible API documentation |
36-
| [Deployment](/docs/deployment) | Production deployment guides |
37-
| [Security](/docs/security) | Security best practices |
38-
| [Troubleshooting](/docs/troubleshooting) | Common issues and solutions |
30+
| Section | Description |
31+
| ---------------------------------------- | ---------------------------------------------------------------------------------- |
32+
| [Getting Started](/docs/getting-started) | Installation and basic setup |
33+
| [Configuration](/docs/configuration) | All configuration options |
34+
| [Features](/docs/features) | Detailed feature guides |
35+
| [API Reference](/docs/api) | OpenAI-compatible API documentation |
36+
| [Deployment](/docs/deployment) | Production deployment guides, build profiles (`full`, `headless`, `standard`, ...) |
37+
| [Security](/docs/security) | Security best practices |
38+
| [Troubleshooting](/docs/troubleshooting) | Common issues and solutions |

src/catalog/mod.rs

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@
33
//! This module provides access to the models.dev model catalog, which contains
44
//! per-model metadata including capabilities, pricing, context limits, and modalities.
55
//!
6-
//! The catalog is embedded at build time as a fallback and optionally synced at
7-
//! runtime via a background job.
6+
//! The catalog is optionally embedded at build time as a fallback (via the `embed-catalog`
7+
//! feature) and can be synced at runtime via a background job.
88
//!
99
//! # Usage
1010
//!
1111
//! ```rust,ignore
1212
//! use crate::catalog::ModelCatalogRegistry;
1313
//!
1414
//! let registry = ModelCatalogRegistry::new();
15-
//! registry.load_from_json(EMBEDDED_CATALOG)?;
15+
//! if let Some(json) = embedded_catalog() {
16+
//! registry.load_from_json(&json)?;
17+
//! }
1618
//!
1719
//! // Look up model metadata
1820
//! if let Some(enrichment) = registry.lookup("anthropic", "claude-opus-4-5") {
@@ -28,20 +30,39 @@ pub use registry::{
2830
ModelCapabilities, ModelCatalogRegistry, ModelModalities, resolve_catalog_provider_id,
2931
};
3032

31-
/// The embedded model catalog from models.dev.
32-
///
33-
/// This is loaded at compile time and serves as a fallback when runtime sync is
34-
/// disabled or unavailable. The catalog is fetched via `scripts/fetch-model-catalog.sh`.
35-
pub const EMBEDDED_CATALOG: &str = include_str!("../../data/models-dev-catalog.json");
33+
/// Embedded catalog assets from the data/ directory.
34+
/// Gated behind the `embed-catalog` feature and uses `allow_missing` so the
35+
/// build succeeds even when `data/models-dev-catalog.json` is absent.
36+
#[cfg(feature = "embed-catalog")]
37+
#[derive(rust_embed::Embed)]
38+
#[folder = "data"]
39+
#[include = "models-dev-catalog.json"]
40+
#[allow_missing = true]
41+
struct CatalogAssets;
42+
43+
/// Returns the embedded model catalog JSON, if the `embed-catalog` feature is
44+
/// enabled and the catalog file was present at build time.
45+
pub fn embedded_catalog() -> Option<String> {
46+
#[cfg(feature = "embed-catalog")]
47+
{
48+
CatalogAssets::get("models-dev-catalog.json")
49+
.map(|file| String::from_utf8_lossy(&file.data).into_owned())
50+
}
51+
#[cfg(not(feature = "embed-catalog"))]
52+
{
53+
None
54+
}
55+
}
3656

37-
#[cfg(test)]
57+
#[cfg(all(test, feature = "embed-catalog"))]
3858
mod tests {
3959
use super::*;
4060

4161
#[test]
4262
fn test_embedded_catalog_parses() {
63+
let json = embedded_catalog().expect("Embedded catalog should be present");
4364
let catalog: types::ModelCatalog =
44-
serde_json::from_str(EMBEDDED_CATALOG).expect("Embedded catalog should be valid JSON");
65+
serde_json::from_str(&json).expect("Embedded catalog should be valid JSON");
4566

4667
// Should have multiple providers
4768
assert!(
@@ -67,9 +88,10 @@ mod tests {
6788

6889
#[test]
6990
fn test_load_embedded_catalog_into_registry() {
91+
let json = embedded_catalog().expect("Embedded catalog should be present");
7092
let registry = ModelCatalogRegistry::new();
7193
registry
72-
.load_from_json(EMBEDDED_CATALOG)
94+
.load_from_json(&json)
7395
.expect("Should load embedded catalog");
7496

7597
// Should have many models

0 commit comments

Comments
 (0)