feat: initial commit — antigravity proxy with MITM, standalone LS, and snapshot tooling

This commit is contained in:
Nikketryhard
2026-02-14 02:24:35 -06:00
commit d5e7f09225
30 changed files with 9980 additions and 0 deletions

332
src/main.rs Normal file
View File

@@ -0,0 +1,332 @@
//! Antigravity OpenAI Proxy — Rust edition v3 (stealth hardened).
//!
//! Single-binary replacement for server.py. BoringSSL TLS impersonation,
//! byte-exact protobuf encoding, Chrome header fingerprinting, cascade
//! session management, warmup + heartbeat lifecycle mimicry.
mod api;
mod backend;
mod constants;
mod mitm;
mod proto;
mod quota;
mod session;
mod warmup;
use api::AppState;
use backend::Backend;
use clap::Parser;
use session::SessionManager;
use std::sync::Arc;
use tracing::{info, warn};
use mitm::store::MitmStore;
#[derive(Parser)]
#[command(name = "antigravity-proxy", about = "Antigravity OpenAI Proxy (stealth)")]
struct Cli {
/// Port to listen on
#[arg(long, default_value_t = 8741)]
port: u16,
/// Enable info-level logging (-v)
#[arg(short, long)]
verbose: bool,
/// Enable debug-level logging (-d)
#[arg(short, long)]
debug: bool,
/// Disable the MITM proxy (no API interception)
#[arg(long)]
no_mitm: bool,
/// MITM proxy port (default: 8742, matches wrapper script)
#[arg(long, default_value_t = 8742)]
mitm_port: u16,
}
#[tokio::main]
async fn main() {
let cli = Cli::parse();
// Flag > env var > default (warn)
let log_level = if cli.debug {
"debug"
} else if cli.verbose {
"info"
} else {
// Fall back to RUST_LOG env, or warn-only
""
};
let filter = if log_level.is_empty() {
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "warn".into())
} else {
tracing_subscriber::EnvFilter::new(log_level)
};
tracing_subscriber::fmt()
.with_env_filter(filter)
.init();
// ── Step 1: Bind main port FIRST (fail fast, before spawning anything) ────
let addr = format!("127.0.0.1:{}", cli.port);
let listener = match tokio::net::TcpListener::bind(&addr).await {
Ok(l) => l,
Err(e) => {
eprintln!("Fatal: cannot bind to {addr}: {e}");
eprintln!("Hint: kill $(lsof -ti:{}) 2>/dev/null", cli.port);
std::process::exit(1);
}
};
// ── Step 2: Backend discovery ─────────────────────────────────────────────
let backend = Arc::new(match Backend::new() {
Ok(b) => b,
Err(e) => {
eprintln!("Fatal: {e}");
std::process::exit(1);
}
});
let (pid, https_port, csrf, token) = backend.info().await;
// ── Step 3: MITM proxy (after port is secured) ────────────────────────────
let mitm_store = MitmStore::new();
let (mitm_port_actual, mitm_handle) = if !cli.no_mitm {
let data_dir = dirs_data_dir();
match mitm::ca::MitmCa::load_or_generate(&data_dir) {
Ok(ca) => {
let ca = Arc::new(ca);
let ca_pem = ca.ca_pem_path.display().to_string();
let config = mitm::proxy::MitmConfig {
port: cli.mitm_port,
modify_requests: false,
};
match mitm::proxy::run(ca, mitm_store.clone(), config).await {
Ok((port, handle)) => {
info!(port, ca = %ca_pem, "MITM proxy started");
// Write actual port to file for wrapper script discovery
let port_file = data_dir.join("mitm-port");
if let Err(e) = std::fs::write(&port_file, port.to_string()) {
warn!("Failed to write MITM port file: {e}");
}
(Some((port, ca_pem)), Some(handle))
}
Err(e) => {
warn!("MITM proxy failed to start: {e}");
(None, None)
}
}
}
Err(e) => {
warn!("MITM CA generation failed: {e}");
(None, None)
}
}
} else {
info!("MITM proxy disabled (--no-mitm)");
(None, None)
};
// ── Step 4: Warmup + heartbeat ────────────────────────────────────────────
warmup::warmup_sequence(&backend).await;
let heartbeat_handle = warmup::start_heartbeat(Arc::clone(&backend));
// ── Step 4b: Quota monitor ────────────────────────────────────────────────
let quota_store = quota::QuotaStore::new();
quota_store.clone().start_polling(Arc::clone(&backend));
info!("Quota monitor started (polling every 60s)");
let state = Arc::new(AppState {
backend,
sessions: SessionManager::new(),
mitm_store,
quota_store,
});
// Periodic backend refresh — keeps LS connection details fresh
let refresh_backend = Arc::clone(&state.backend);
let refresh_handle = tokio::spawn(async move {
loop {
tokio::time::sleep(tokio::time::Duration::from_secs(60)).await;
if let Err(e) = refresh_backend.refresh().await {
warn!("Periodic refresh failed: {e}");
}
}
});
// ── Step 5: Start serving ─────────────────────────────────────────────────
let app = api::router(state.clone());
print_banner(cli.port, &pid, &https_port, &csrf, &token, &mitm_port_actual);
info!("Listening on http://{addr}");
axum::serve(listener, app)
.with_graceful_shutdown(shutdown_signal())
.await
.expect("server error");
// ── Cleanup: abort all background tasks ───────────────────────────────────
heartbeat_handle.abort();
refresh_handle.abort();
if let Some(h) = mitm_handle {
h.abort();
}
// Remove stale MITM port file
let _ = std::fs::remove_file(dirs_data_dir().join("mitm-port"));
info!("Server shutdown complete");
}
/// Wait for SIGINT (Ctrl+C) or SIGTERM for graceful shutdown.
async fn shutdown_signal() {
let ctrl_c = async {
tokio::signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.expect("failed to install SIGTERM handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => info!("Received SIGINT, shutting down..."),
_ = terminate => info!("Received SIGTERM, shutting down..."),
}
}
fn print_banner(port: u16, pid: &str, https_port: &str, csrf: &str, token: &str, mitm: &Option<(u16, String)>) {
let chrome_major = &*constants::CHROME_MAJOR;
let ver = crate::constants::antigravity_version();
println!();
println!(" \x1b[1;35m>> antigravity-proxy\x1b[0m \x1b[2mv{ver}\x1b[0m");
println!(" \x1b[2m────────────────────────────────────────────────\x1b[0m");
println!();
println!(" \x1b[1mcore\x1b[0m");
println!(" \x1b[36m tls\x1b[0m BoringSSL (Chrome {chrome_major})");
println!(" \x1b[36m listen\x1b[0m http://127.0.0.1:{port}");
println!(" \x1b[36m ls pid\x1b[0m {pid}");
println!(" \x1b[36m https\x1b[0m :{https_port}");
println!(" \x1b[36m csrf\x1b[0m {csrf}");
println!(" \x1b[36m oauth\x1b[0m {token}");
println!();
// MITM section
if let Some((mitm_port, ca_path)) = mitm {
println!(" \x1b[1mmitm\x1b[0m");
println!(" \x1b[36m proxy\x1b[0m 127.0.0.1:{mitm_port}");
println!(" \x1b[36m ca cert\x1b[0m {ca_path}");
// Check if wrapper is installed
let wrapper_installed = check_wrapper_installed();
if wrapper_installed {
println!(" \x1b[36m wrapper\x1b[0m \x1b[32minstalled\x1b[0m");
} else {
println!(" \x1b[36m wrapper\x1b[0m \x1b[33mnot installed\x1b[0m");
}
println!();
} else {
println!(" \x1b[1mmitm\x1b[0m \x1b[33mdisabled\x1b[0m");
println!();
}
// Routes
println!(" \x1b[1mroutes\x1b[0m");
println!(" \x1b[33m POST\x1b[0m /v1/responses");
println!(" \x1b[33m POST\x1b[0m /v1/chat/completions");
println!(" \x1b[32m GET \x1b[0m /v1/models");
println!(" \x1b[32m GET \x1b[0m /v1/sessions");
println!(" \x1b[31m DEL \x1b[0m /v1/sessions/:id");
println!(" \x1b[33m POST\x1b[0m /v1/token");
println!(" \x1b[32m GET \x1b[0m /v1/usage");
println!(" \x1b[32m GET \x1b[0m /v1/quota");
println!(" \x1b[32m GET \x1b[0m /health");
println!();
// Status line
let mitm_tag = if mitm.is_some() { "\x1b[32mmitm\x1b[0m" } else { "\x1b[31mmitm\x1b[0m" };
println!(" \x1b[2mstealth:\x1b[0m \x1b[32mwarmup\x1b[0m \x1b[32mheartbeat\x1b[0m \x1b[32mjitter\x1b[0m {mitm_tag}");
println!();
// Setup hints
if let Some((mitm_port, ca_path)) = mitm {
if !check_wrapper_installed() {
println!(" \x1b[1;33m[!]\x1b[0m mitm wrapper not installed");
println!(" \x1b[2mrun:\x1b[0m ./scripts/mitm-wrapper.sh install");
println!(" \x1b[2mor:\x1b[0m HTTPS_PROXY=http://127.0.0.1:{mitm_port}");
println!(" NODE_EXTRA_CA_CERTS={ca_path}");
println!();
}
}
if token == "NOT SET" {
println!(" \x1b[1;33m[!]\x1b[0m no oauth token");
println!(" export ANTIGRAVITY_OAUTH_TOKEN=ya29.xxx");
println!(" curl -X POST http://127.0.0.1:{port}/v1/token -d '{{\"token\":\"ya29.xxx\"}}'");
println!(" echo 'ya29.xxx' > ~/.config/antigravity-proxy-token");
println!();
}
}
/// Check if the MITM wrapper is installed by looking for the .real backup file
/// next to the LS binary. Uses /proc to find the real LS path dynamically.
fn check_wrapper_installed() -> bool {
// Find the LS binary path from known PID or by scanning /proc
if let Some(ls_path) = find_ls_binary_path() {
let real_path = format!("{ls_path}.real");
return std::path::Path::new(&real_path).exists();
}
false
}
/// Find the LS binary path by reading /proc/<pid>/exe for known language server processes.
fn find_ls_binary_path() -> Option<String> {
// Try all running processes, look for ones that look like the LS
let proc = std::path::Path::new("/proc");
if !proc.exists() {
return None;
}
if let Ok(entries) = std::fs::read_dir(proc) {
for entry in entries.flatten() {
let name = entry.file_name();
let name_str = name.to_string_lossy();
// Only look at numeric dirs (PIDs)
if !name_str.chars().all(|c| c.is_ascii_digit()) {
continue;
}
let exe_link = entry.path().join("exe");
if let Ok(target) = std::fs::read_link(&exe_link) {
let target_str = target.to_string_lossy();
// Strip " (deleted)" suffix from unlinked binaries
let target_clean = target_str.trim_end_matches(" (deleted)");
// Match any binary that looks like the Antigravity LS
if target_clean.contains("language_server_linux")
|| target_clean.contains("antigravity-language-server")
{
// Strip .real suffix — if the wrapper exec'd the backup, we want the base name
let path = target_clean.trim_end_matches(".real");
return Some(path.to_string());
}
}
}
}
None
}
/// Get the data directory for storing MITM CA cert/key.
fn dirs_data_dir() -> std::path::PathBuf {
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
std::path::PathBuf::from(home).join(".config").join("antigravity-proxy")
}