Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,12 @@ FROM debian:trixie-slim

RUN apt-get update && apt-get install -y \
ca-certificates \
curl \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY --from=builder /app/target/release/compass /app/compass
COPY scripts/ /app/scripts/
RUN chmod +x /app/scripts/*.sh && mkdir -p /app/data
RUN mkdir -p /app/data

ENV PORT=4001
ENV DATA_DIR=/app/data
Expand Down
4 changes: 1 addition & 3 deletions Dockerfile.gpu
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,12 @@ FROM nvidia/cuda:12.4.1-runtime-ubuntu22.04

RUN apt-get update && apt-get install -y \
ca-certificates \
curl \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY --from=builder /app/target/release/compass /app/compass
COPY scripts/ /app/scripts/
RUN chmod +x /app/scripts/*.sh && mkdir -p /app/data
RUN mkdir -p /app/data

ENV PORT=4001
ENV DATA_DIR=/app/data
Expand Down
72 changes: 0 additions & 72 deletions MOSAIC.md

This file was deleted.

61 changes: 5 additions & 56 deletions crates/compass/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
//
// All HTTP endpoints wired up here. Includes v2 endpoints for
// vector space CRUD, rebuild triggers, and status checks.
//
// mosaic-compass: Bearer-token auth middleware is applied to all routes
// except /health. See `AuthConfig` and `auth_middleware` below.

pub mod collections;
pub mod ingest;
Expand All @@ -13,10 +10,7 @@ pub mod search;
use crate::collections::CollectionManager;
use crate::embed::EmbedState;
use crate::models::HealthResponse;
use axum::extract::{Request, State};
use axum::http::{header::AUTHORIZATION, StatusCode};
use axum::middleware::{from_fn_with_state, Next};
use axum::response::Response;
use axum::extract::State;
use axum::routing::{delete, get, post, put};
use axum::{Json, Router};
use std::sync::Arc;
Expand All @@ -27,50 +21,9 @@ pub struct AppState {
pub embed_state: Arc<EmbedState>,
}

/// Auth configuration for the protected-route middleware.
/// `None` disables auth (dev mode); a `Some(key)` requires `Authorization: Bearer <key>`.
#[derive(Clone)]
pub struct AuthConfig {
pub expected_key: Option<String>,
}

/// Constant-time byte comparison to avoid timing attacks on the API key check.
fn ct_eq(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
let mut diff: u8 = 0;
for (x, y) in a.iter().zip(b.iter()) {
diff |= x ^ y;
}
diff == 0
}

/// Bearer-token middleware. Applied to every route except `/health`.
async fn auth_middleware(
State(cfg): State<Arc<AuthConfig>>,
req: Request,
next: Next,
) -> Result<Response, StatusCode> {
let provided = req
.headers()
.get(AUTHORIZATION)
.and_then(|h| h.to_str().ok())
.and_then(|s| s.strip_prefix("Bearer "));

match (cfg.expected_key.as_deref(), provided) {
(Some(expected), Some(got)) if ct_eq(expected.as_bytes(), got.as_bytes()) => {
Ok(next.run(req).await)
}
(None, _) => Ok(next.run(req).await),
_ => Err(StatusCode::UNAUTHORIZED),
}
}

/// Build the Axum router with all Compass endpoints. `/health` is unauthenticated;
/// every other route requires `Authorization: Bearer <COMPASS_API_KEY>` when the key is set.
pub fn build_router(state: Arc<AppState>, auth: Arc<AuthConfig>) -> Router {
let protected = Router::new()
/// Build the Axum router with all Compass endpoints.
pub fn build_router(state: Arc<AppState>) -> Router {
Router::new()
// ── Collection CRUD ──────────────────────────────────────────────
.route("/collections", post(collections::create_collection))
.route("/collections", get(collections::list_collections))
Expand Down Expand Up @@ -112,12 +65,8 @@ pub fn build_router(state: Arc<AppState>, auth: Arc<AuthConfig>) -> Router {
post(search::search_collection),
)
.route("/collections/{name}/facets", get(search::get_facets))
.layer(from_fn_with_state(auth, auth_middleware));

Router::new()
// ── Health (unauthenticated) ─────────────────────────────────────
// ── Health ───────────────────────────────────────────────────────
.route("/health", get(health_check))
.merge(protected)
// 64 MB body limit. Default 2 MB is too small for batched ingest with embeddings.
.layer(axum::extract::DefaultBodyLimit::max(64 * 1024 * 1024))
.with_state(state)
Expand Down
15 changes: 2 additions & 13 deletions crates/compass/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ mod scoring;
mod search;
mod telemetry;

use api::{AppState, AuthConfig};
use api::AppState;
use std::env;
use std::path::PathBuf;
use std::sync::Arc;
Expand Down Expand Up @@ -71,18 +71,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Anonymous telemetry — opt out with COMPASS_TELEMETRY=off or DO_NOT_TRACK=1
telemetry::spawn_telemetry(data_dir.clone(), app_state.manager.clone());

// mosaic-compass: Bearer-token auth via COMPASS_API_KEY. When unset, auth is disabled.
let api_key = env::var("COMPASS_API_KEY").ok().filter(|s| !s.is_empty());
if api_key.is_some() {
tracing::info!("API key auth enabled (Authorization: Bearer required on all routes except /health)");
} else {
tracing::warn!("COMPASS_API_KEY not set — API is unauthenticated (dev mode)");
}
let auth_config = Arc::new(AuthConfig {
expected_key: api_key,
});

let app = api::build_router(app_state, auth_config).layer(cors);
let app = api::build_router(app_state).layer(cors);

let addr = format!("0.0.0.0:{}", port);
tracing::info!("Compass listening on {}", addr);
Expand Down
35 changes: 0 additions & 35 deletions porter.yaml

This file was deleted.

36 changes: 0 additions & 36 deletions scripts/download-models.sh

This file was deleted.

Loading