feat: rebrand to ZeroGravity, replace proxyctl with zg Rust binary

Phase 1 - Rename:
- Crate: antigravity-proxy -> zerogravity
- Env: ANTIGRAVITY_OAUTH_TOKEN -> ZEROGRAVITY_TOKEN
- Paths: ~/.config/antigravity-proxy -> ~/.config/zerogravity
- Paths: /tmp/antigravity-* -> /tmp/zerogravity-*
- User: antigravity-ls -> zerogravity-ls
- Service: antigravity-proxy -> zerogravity

Phase 2 - zg daemon manager:
- New Rust binary src/bin/zg.rs replaces scripts/proxyctl bash
- Commands: start, stop, restart, rebuild, status, logs, test, health
- Auto-resolves project dir from binary location
- All commands exit immediately (safe for agent fast-bash)
This commit is contained in:
Nikketryhard
2026-02-18 01:54:54 -06:00
parent 409ee97405
commit 00587fcce8
18 changed files with 403 additions and 122 deletions

74
Cargo.lock generated
View File

@@ -103,43 +103,6 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "antigravity-proxy"
version = "3.0.0"
dependencies = [
"async-stream",
"axum",
"base64",
"brotli 7.0.0",
"bytes",
"chrono",
"clap",
"flate2",
"http",
"http-body-util",
"httparse",
"hyper",
"hyper-util",
"rand",
"rcgen",
"regex",
"rustls",
"rustls-native-certs",
"rustls-pemfile",
"serde",
"serde_json",
"time",
"tokio",
"tokio-rustls",
"tokio-stream",
"tower-http",
"tracing",
"tracing-subscriber",
"uuid",
"wreq",
"wreq-util",
]
[[package]]
name = "async-stream"
version = "0.3.6"
@@ -2396,6 +2359,43 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zerogravity"
version = "3.0.0"
dependencies = [
"async-stream",
"axum",
"base64",
"brotli 7.0.0",
"bytes",
"chrono",
"clap",
"flate2",
"http",
"http-body-util",
"httparse",
"hyper",
"hyper-util",
"rand",
"rcgen",
"regex",
"rustls",
"rustls-native-certs",
"rustls-pemfile",
"serde",
"serde_json",
"time",
"tokio",
"tokio-rustls",
"tokio-stream",
"tower-http",
"tracing",
"tracing-subscriber",
"uuid",
"wreq",
"wreq-util",
]
[[package]]
name = "zeroize"
version = "1.8.2"

View File

@@ -1,8 +1,16 @@
[package]
name = "antigravity-proxy"
name = "zerogravity"
version = "3.0.0"
edition = "2021"
[[bin]]
name = "zerogravity"
path = "src/main.rs"
[[bin]]
name = "zg"
path = "src/bin/zg.rs"
[dependencies]
axum = { version = "0.8", features = ["json"] }
tokio = { version = "1", features = ["full"] }

View File

@@ -27,41 +27,41 @@ This "LS as dumb relay" pattern keeps the LS interactions minimal and predictabl
## Agent Quick Reference
### `proxyctl` — Daemon Manager
### `zg` — Daemon Manager
`proxyctl` commands exit immediately (not foreground) — safe for agent use via fast-bash MCP.
`zg` commands exit immediately (not foreground) — safe for agent use via fast-bash MCP.
```bash
# Rebuild and restart after code changes
proxyctl restart
zg restart
# Quick test
proxyctl test "say hi in 3 words"
zg test "say hi in 3 words"
# Check status
proxyctl status
zg status
# Check health
proxyctl health
zg health
```
| Command | Description |
| --------------------- | ----------------------------------- |
| `proxyctl start` | Start the proxy daemon |
| `proxyctl stop` | Stop the proxy daemon |
| `proxyctl restart` | Rebuild + restart |
| `proxyctl rebuild` | Build release binary only |
| `proxyctl status` | Service status + quota + usage |
| `proxyctl logs [N]` | Tail last N lines + follow |
| `proxyctl logs-all` | Full log dump (no follow) |
| `proxyctl test [msg]` | Quick test request (gemini-3-flash) |
| `proxyctl health` | Health check |
| `zg start` | Start the proxy daemon |
| `zg stop` | Stop the proxy daemon |
| `zg restart` | Rebuild + restart |
| `zg rebuild` | Build release binary only |
| `zg status` | Service status + quota + usage |
| `zg logs [N]` | Tail last N lines + follow |
| `zg logs-all` | Full log dump (no follow) |
| `zg test [msg]` | Quick test request (gemini-3-flash) |
| `zg health` | Health check |
### Testing After Changes
```bash
# 1. Rebuild + restart
proxyctl restart
zg restart
# 2. Test an endpoint
curl -s http://localhost:8741/v1/chat/completions \
@@ -69,7 +69,7 @@ curl -s http://localhost:8741/v1/chat/completions \
-d '{"model": "gemini-3-flash", "messages": [{"role": "user", "content": "Say hi"}]}' | jq .
# 3. Inspect latest trace
TRACE_DIR=~/.config/antigravity-proxy/traces/$(date +%Y-%m-%d)
TRACE_DIR=~/.config/zerogravity/traces/$(date +%Y-%m-%d)
cat "$TRACE_DIR/$(ls -t "$TRACE_DIR" | head -1)/summary.md"
```
@@ -99,8 +99,8 @@ cat "$TRACE_DIR/$(ls -t "$TRACE_DIR" | head -1)/summary.md"
The proxy needs an OAuth token:
1. **Env var**: `ANTIGRAVITY_OAUTH_TOKEN=ya29.xxx`
2. **Token file**: `~/.config/antigravity-proxy-token`
1. **Env var**: `ZEROGRAVITY_TOKEN=ya29.xxx`
2. **Token file**: `~/.config/zerogravity-token`
3. **Runtime**: `curl -X POST http://localhost:8741/v1/token -d '{"token":"ya29.xxx"}'`
## CLI Flags

View File

@@ -23,10 +23,10 @@ graph LR
```bash
# Headless mode (no running Antigravity app needed)
RUST_LOG=info ./target/release/antigravity-proxy --headless
RUST_LOG=info ./target/release/zerogravity --headless
# Or use the daemon manager
proxyctl start
zg start
```
## Endpoints
@@ -50,22 +50,22 @@ proxyctl start
The proxy needs an OAuth token:
1. **Env var**: `ANTIGRAVITY_OAUTH_TOKEN=ya29.xxx`
2. **Token file**: `~/.config/antigravity-proxy-token`
1. **Env var**: `ZEROGRAVITY_TOKEN=ya29.xxx`
2. **Token file**: `~/.config/zerogravity-token`
3. **Runtime**: `curl -X POST http://localhost:8741/v1/token -d '{"token":"ya29.xxx"}'`
## `proxyctl` Commands
## `zg` Commands
| Command | Description |
| --------------------- | ------------------------------ |
| `proxyctl start` | Start the proxy daemon |
| `proxyctl stop` | Stop the proxy daemon |
| `proxyctl restart` | Rebuild + restart |
| `proxyctl rebuild` | Build release binary only |
| `proxyctl status` | Service status + quota + usage |
| `proxyctl logs [N]` | Tail last N lines + follow |
| `proxyctl test [msg]` | Quick test request |
| `proxyctl health` | Health check |
| `zg start` | Start the proxy daemon |
| `zg stop` | Stop the proxy daemon |
| `zg restart` | Rebuild + restart |
| `zg rebuild` | Build release binary only |
| `zg status` | Service status + quota + usage |
| `zg logs [N]` | Tail last N lines + follow |
| `zg test [msg]` | Quick test request |
| `zg health` | Health check |
## Documentation

View File

@@ -132,11 +132,11 @@ Events dispatched through `tokio::sync::mpsc` channels from MITM → API handler
### UID-Scoped iptables (Classic Mode)
```bash
# One-time setup — creates antigravity-ls user + iptables rule
# One-time setup — creates zerogravity-ls user + iptables rule
sudo ./scripts/mitm-redirect.sh install
# Run proxy (standalone LS + MITM both enabled by default)
RUST_LOG=info ./target/release/antigravity-proxy
RUST_LOG=info ./target/release/zerogravity
# Check intercepted usage
curl -s http://localhost:8741/v1/usage | jq .
@@ -150,7 +150,7 @@ sudo ./scripts/mitm-redirect.sh uninstall
No iptables or sudo needed. The LS connects through `HTTPS_PROXY` instead:
```bash
RUST_LOG=info ./target/release/antigravity-proxy --headless
RUST_LOG=info ./target/release/zerogravity --headless
```
---

View File

@@ -5,7 +5,7 @@ Per-call debug traces for inspecting request/response flow. Every API call write
## Location
```
~/.config/antigravity-proxy/traces/{YYYY-MM-DD}/{HH-MM-SS.sss}_{cascade_short}/
~/.config/zerogravity/traces/{YYYY-MM-DD}/{HH-MM-SS.sss}_{cascade_short}/
```
Disable with `--no-trace`.
@@ -108,11 +108,11 @@ Traces are designed for LLM consumption. To inspect the last trace:
```bash
# Find latest trace
ls -t ~/.config/antigravity-proxy/traces/$(date +%Y-%m-%d)/ | head -1
ls -t ~/.config/zerogravity/traces/$(date +%Y-%m-%d)/ | head -1
# Read the summary
cat ~/.config/antigravity-proxy/traces/$(date +%Y-%m-%d)/$(ls -t ~/.config/antigravity-proxy/traces/$(date +%Y-%m-%d)/ | head -1)/summary.md
cat ~/.config/zerogravity/traces/$(date +%Y-%m-%d)/$(ls -t ~/.config/zerogravity/traces/$(date +%Y-%m-%d)/ | head -1)/summary.md
# Grep for failures
grep 'outcome=.*error\|outcome=.*timeout' ~/.config/antigravity-proxy/traces/$(date +%Y-%m-%d)/*/meta.txt
grep 'outcome=.*error\|outcome=.*timeout' ~/.config/zerogravity/traces/$(date +%Y-%m-%d)/*/meta.txt
```

View File

@@ -6,7 +6,7 @@
# modification, no system-wide changes.
#
# Flow:
# 1. Standalone LS runs as 'antigravity-ls' user (via sudo -u)
# 1. Standalone LS runs as 'zerogravity-ls' user (via sudo -u)
# 2. iptables catches :443 traffic from that UID only → REDIRECT to MITM port
# 3. MITM terminates TLS (Go client trusts our CA via SSL_CERT_FILE)
# 4. MITM forwards upstream, captures usage
@@ -24,10 +24,10 @@
set -euo pipefail
MITM_PORT="${2:-8742}"
LS_USER="antigravity-ls"
LS_USER="zerogravity-ls"
DATA_DIR="/tmp/antigravity-standalone"
LS_BINARY="/usr/share/antigravity/resources/app/extensions/antigravity/bin/language_server_linux_x64"
SUDOERS_FILE="/etc/sudoers.d/antigravity-ls"
SUDOERS_FILE="/etc/sudoers.d/zerogravity-ls"
install() {
if [[ $EUID -ne 0 ]]; then
@@ -54,7 +54,7 @@ install() {
echo " + data dir: $DATA_DIR (mode 1777, writable by all)"
# ── 3. Sudoers entry ────────────────────────────────────────────────
# Allow the invoking user (SUDO_USER) to run ANY command as antigravity-ls.
# Allow the invoking user (SUDO_USER) to run ANY command as zerogravity-ls.
# This is needed for the proxy to spawn the LS binary.
local REAL_USER="${SUDO_USER:-$(logname 2>/dev/null || whoami)}"
cat > "$SUDOERS_FILE" <<EOF
@@ -78,7 +78,7 @@ EOF
echo
echo "[mitm-redirect] ✓ Installed (only affects uid=$LS_UID)"
echo " Restart the proxy to take effect:"
echo " RUST_LOG=info ./target/release/antigravity-proxy --standalone"
echo " RUST_LOG=info ./target/release/zerogravity --standalone"
}
uninstall() {

View File

@@ -390,7 +390,7 @@ pub(crate) async fn handle_completions(
if token.is_empty() {
return err_response(
StatusCode::UNAUTHORIZED,
"No OAuth token. POST to /v1/token or set ANTIGRAVITY_OAUTH_TOKEN env var.".into(),
"No OAuth token. POST to /v1/token or set ZEROGRAVITY_TOKEN env var.".into(),
"authentication_error",
);
}

View File

@@ -195,7 +195,7 @@ async fn handle_gemini_inner(
if token.is_empty() {
return err_response(
StatusCode::UNAUTHORIZED,
"No OAuth token. POST to /v1/token or set ANTIGRAVITY_OAUTH_TOKEN env var.".into(),
"No OAuth token. POST to /v1/token or set ZEROGRAVITY_TOKEN env var.".into(),
"authentication_error",
);
}

View File

@@ -226,7 +226,7 @@ pub(crate) async fn handle_responses(
if token.is_empty() {
return err_response(
StatusCode::UNAUTHORIZED,
"No OAuth token. POST to /v1/token or set ANTIGRAVITY_OAUTH_TOKEN env var.".into(),
"No OAuth token. POST to /v1/token or set ZEROGRAVITY_TOKEN env var.".into(),
"authentication_error",
);
}

View File

@@ -154,7 +154,7 @@ impl Backend {
}
// Then env var
if let Ok(env_token) = std::env::var("ANTIGRAVITY_OAUTH_TOKEN") {
if let Ok(env_token) = std::env::var("ZEROGRAVITY_TOKEN") {
if !env_token.is_empty() {
let mut guard = self.inner.write().await;
if guard.oauth_token != env_token {
@@ -607,12 +607,12 @@ fn discover() -> Result<BackendInner, String> {
https_port = "3100".to_string();
}
let oauth_token = std::env::var("ANTIGRAVITY_OAUTH_TOKEN")
let oauth_token = std::env::var("ZEROGRAVITY_TOKEN")
.ok()
.filter(|s| !s.is_empty())
.or_else(|| {
let home = std::env::var("HOME").unwrap_or_default();
let path = format!("{home}/.config/antigravity-proxy-token");
let path = format!("{home}/.config/zerogravity/token");
fs::read_to_string(&path)
.ok()
.map(|s| s.trim().to_string())

View File

@@ -1,3 +1,3 @@
fn main() {
antigravity_proxy::snapshot::run_cli();
zerogravity::snapshot::run_cli();
}

273
src/bin/zg.rs Normal file
View File

@@ -0,0 +1,273 @@
//! `zg` — ZeroGravity daemon manager.
//!
//! All commands exit immediately (safe for agent use via fast-bash MCP).
use std::process::{Command, Stdio};
const SERVICE: &str = "zerogravity";
const PORT: u16 = 8741;
// ANSI colors
const RED: &str = "\x1b[0;31m";
const GREEN: &str = "\x1b[0;32m";
const YELLOW: &str = "\x1b[1;33m";
const CYAN: &str = "\x1b[0;36m";
const BOLD: &str = "\x1b[1m";
const DIM: &str = "\x1b[2m";
const NC: &str = "\x1b[0m";
fn main() {
let args: Vec<String> = std::env::args().collect();
let cmd = args.get(1).map(|s| s.as_str()).unwrap_or("");
let arg = args.get(2).map(|s| s.as_str());
match cmd {
"start" => do_start(),
"stop" => do_stop(),
"restart" => do_restart(),
"rebuild" => do_build(),
"status" => do_status(),
"logs" => do_logs(arg.unwrap_or("30"), false),
"logs-follow" => do_logs(arg.unwrap_or("30"), true),
"logs-all" => do_logs_all(),
"test" => do_test(arg.unwrap_or("Say hello in exactly 3 words")),
"health" => do_health(),
_ => usage(),
}
}
fn usage() {
println!("{BOLD}zg{NC} — ZeroGravity daemon manager\n");
println!(" {CYAN}start{NC} Start the proxy daemon");
println!(" {CYAN}stop{NC} Stop the proxy daemon");
println!(" {CYAN}restart{NC} Rebuild + restart");
println!(" {CYAN}rebuild{NC} Build release binary only");
println!(" {CYAN}status{NC} Service status + quota + usage");
println!(" {CYAN}logs{NC} [N] Show last N lines (default 30)");
println!(" {CYAN}logs-follow{NC} [N] Tail last N lines + follow");
println!(" {CYAN}logs-all{NC} Full log dump");
println!(" {CYAN}test{NC} [msg] Quick test request (gemini-3-flash)");
println!(" {CYAN}health{NC} Health check");
}
fn project_dir() -> String {
// Resolve project dir from binary location:
// binary is at <project>/target/release/zg or <project>/target/debug/zg
let exe = std::env::current_exe().expect("cannot resolve exe path");
let exe_dir = exe.parent().expect("no parent dir");
// Walk up from target/release/ or target/debug/
if let Some(target_dir) = exe_dir.parent() {
if let Some(project) = target_dir.parent() {
return project.to_string_lossy().to_string();
}
}
// Fallback: use current dir
std::env::current_dir()
.expect("no cwd")
.to_string_lossy()
.to_string()
}
fn base_url() -> String {
let port = std::env::var("PROXY_PORT")
.ok()
.and_then(|p| p.parse::<u16>().ok())
.unwrap_or(PORT);
format!("http://localhost:{port}")
}
fn systemctl(args: &[&str]) -> bool {
Command::new("systemctl")
.arg("--user")
.args(args)
.status()
.map(|s| s.success())
.unwrap_or(false)
}
fn curl_get(path: &str) -> Option<String> {
let url = format!("{}{}", base_url(), path);
Command::new("curl")
.args(["-sf", &url])
.output()
.ok()
.filter(|o| o.status.success())
.map(|o| String::from_utf8_lossy(&o.stdout).to_string())
}
fn curl_post(path: &str, body: &str) -> Option<String> {
let url = format!("{}{}", base_url(), path);
Command::new("curl")
.args(["-sf", &url, "-H", "Content-Type: application/json", "-d", body])
.output()
.ok()
.filter(|o| o.status.success())
.map(|o| String::from_utf8_lossy(&o.stdout).to_string())
}
fn health_ok() -> bool {
curl_get("/health").is_some()
}
fn jq_print(json: &str) {
let mut child = Command::new("jq")
.arg(".")
.stdin(Stdio::piped())
.spawn()
.expect("jq not found");
if let Some(stdin) = child.stdin.as_mut() {
use std::io::Write;
let _ = stdin.write_all(json.as_bytes());
}
let _ = child.wait();
}
// ── Commands ──
fn do_build() {
let dir = project_dir();
println!("{YELLOW}Building release binary...{NC}");
let status = Command::new("cargo")
.args(["build", "--release"])
.current_dir(&dir)
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status()
.expect("failed to run cargo");
if status.success() {
println!("{GREEN}Build complete.{NC}");
} else {
eprintln!("{RED}Build failed.{NC}");
std::process::exit(1);
}
}
fn do_start() {
systemctl(&["daemon-reload"]);
systemctl(&["start", SERVICE]);
println!("{GREEN}Started.{NC} Waiting for ready...");
for _ in 0..20 {
if health_ok() {
println!("{GREEN}ZeroGravity is up on port {PORT}.{NC}");
return;
}
std::thread::sleep(std::time::Duration::from_millis(500));
}
eprintln!("{RED}Proxy didn't become healthy in 10s. Check logs:{NC}");
let _ = Command::new("journalctl")
.args(["--user", "-u", SERVICE, "--no-pager", "-n", "20"])
.status();
std::process::exit(1);
}
fn do_stop() {
let _ = systemctl(&["stop", SERVICE]);
println!("{YELLOW}Stopped.{NC}");
}
fn do_restart() {
println!("{YELLOW}Stopping...{NC}");
do_stop();
do_build();
do_start();
}
fn do_status() {
println!("{BOLD}── Service ──{NC}");
let output = Command::new("systemctl")
.args(["--user", "status", SERVICE, "--no-pager"])
.output();
match output {
Ok(o) => {
let text = String::from_utf8_lossy(&o.stdout);
// Print first 6 lines
for (i, line) in text.lines().enumerate() {
if i >= 6 { break; }
println!("{line}");
}
}
Err(_) => println!("{RED}Not running{NC}"),
}
println!();
if !health_ok() {
println!("{RED}Proxy is not responding on port {PORT}.{NC}");
std::process::exit(1);
}
println!("{BOLD}── Quota ──{NC}");
match curl_get("/v1/quota") {
Some(json) => jq_print(&json),
None => println!("{DIM}(no quota data){NC}"),
}
println!();
println!("{BOLD}── Usage ──{NC}");
match curl_get("/v1/usage") {
Some(json) => jq_print(&json),
None => println!("{DIM}(no usage data){NC}"),
}
println!();
println!("{BOLD}── Sessions ──{NC}");
match curl_get("/v1/sessions") {
Some(json) => jq_print(&json),
None => println!("{DIM}(no sessions){NC}"),
}
}
fn do_logs(n: &str, follow: bool) {
let mut args = vec!["--user", "-u", SERVICE, "--no-pager", "-n", n];
if follow {
args.push("-f");
}
let _ = Command::new("journalctl")
.args(&args)
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status();
}
fn do_logs_all() {
let _ = Command::new("journalctl")
.args(["--user", "-u", SERVICE, "--no-pager"])
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status();
}
fn do_test(msg: &str) {
println!("{CYAN}Testing:{NC} {msg}");
let body = format!(
r#"{{"model":"gemini-3-flash","input":"{}","stream":false,"timeout":30}}"#,
msg.replace('"', r#"\""#)
);
match curl_post("/v1/responses", &body) {
Some(json) => jq_print(&json),
None => {
eprintln!("{RED}Request failed. Is the proxy running?{NC}");
std::process::exit(1);
}
}
}
fn do_health() {
match curl_get("/health") {
Some(json) => {
jq_print(&json);
println!("{GREEN}Healthy{NC}");
}
None => {
println!("{RED}Not responding{NC}");
std::process::exit(1);
}
}
}

View File

@@ -185,7 +185,7 @@ pub fn log_base() -> String {
/// Token file path.
pub fn token_file_path() -> String {
let home = std::env::var("HOME").unwrap_or_else(|_| "/root".to_string());
format!("{home}/.config/antigravity-proxy-token")
format!("{home}/.config/zerogravity/token")
}
/// User-Agent string matching the Electron webview — computed once.

View File

@@ -1,4 +1,4 @@
//! Antigravity OpenAI Proxy — Rust edition v3 (stealth hardened).
//! ZeroGravity — Rust edition v3 (stealth hardened).
//!
//! Single-binary replacement for server.py. BoringSSL TLS impersonation,
//! byte-exact protobuf encoding, Chrome header fingerprinting, cascade
@@ -26,8 +26,8 @@ use mitm::store::MitmStore;
#[derive(Parser)]
#[command(
name = "antigravity-proxy",
about = "Antigravity OpenAI Proxy (stealth)"
name = "zerogravity",
about = "ZeroGravity — stealth LLM proxy"
)]
struct Cli {
/// Port to listen on
@@ -64,7 +64,7 @@ struct Cli {
#[arg(long, conflicts_with = "headless")]
classic: bool,
/// Disable per-call debug traces (on by default, writes JSON to ~/.config/antigravity-proxy/traces/)
/// Disable per-call debug traces (on by default, writes JSON to ~/.config/zerogravity/traces/)
#[arg(long)]
no_trace: bool,
}
@@ -236,12 +236,12 @@ async fn main() {
let backend = Arc::new(if let Some((_, port, ref csrf)) = standalone_ls {
// Build backend pointing at standalone LS
let oauth = std::env::var("ANTIGRAVITY_OAUTH_TOKEN")
let oauth = std::env::var("ZEROGRAVITY_TOKEN")
.ok()
.filter(|s| !s.is_empty())
.or_else(|| {
let home = std::env::var("HOME").unwrap_or_default();
let path = format!("{home}/.config/antigravity-proxy-token");
let path = format!("{home}/.config/zerogravity/token");
std::fs::read_to_string(&path)
.ok()
.map(|s| s.trim().to_string())
@@ -282,7 +282,7 @@ async fn main() {
let trace_collector = trace::TraceCollector::new(trace_enabled);
if trace_enabled {
trace_collector.cleanup_old_traces(7);
info!("Debug tracing enabled → ~/.config/antigravity-proxy/traces/");
info!("Debug tracing enabled → ~/.config/zerogravity/traces/");
}
let state = Arc::new(AppState {
@@ -391,7 +391,7 @@ fn print_banner(
let ver = crate::constants::antigravity_version();
println!();
println!(" \x1b[1;35m>> antigravity-proxy\x1b[0m \x1b[2mv{ver}\x1b[0m");
println!(" \x1b[1;35m>> zerogravity\x1b[0m \x1b[2mv{ver}\x1b[0m");
println!(" \x1b[2m────────────────────────────────────────────────\x1b[0m");
println!();
println!(" \x1b[1mcore\x1b[0m");
@@ -462,11 +462,11 @@ fn print_banner(
if token == "NOT SET" {
println!(" \x1b[1;33m[!]\x1b[0m no oauth token");
println!(" export ANTIGRAVITY_OAUTH_TOKEN=ya29.xxx");
println!(" export ZEROGRAVITY_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!(" echo 'ya29.xxx' > ~/.config/zerogravity/token");
println!();
}
}
@@ -522,5 +522,5 @@ 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")
.join("zerogravity")
}

View File

@@ -24,13 +24,13 @@ const LS_BINARY_PATH: &str =
const APP_ROOT: &str = "/usr/share/antigravity/resources/app";
/// Data directory for the standalone LS.
const DATA_DIR: &str = "/tmp/antigravity-standalone";
const DATA_DIR: &str = "/tmp/zerogravity-standalone";
/// System user for UID-scoped iptables isolation.
const LS_USER: &str = "antigravity-ls";
const LS_USER: &str = "zerogravity-ls";
/// Path for the compiled dns_redirect.so preload library.
const DNS_REDIRECT_SO_PATH: &str = "/tmp/antigravity-dns-redirect.so";
const DNS_REDIRECT_SO_PATH: &str = "/tmp/zerogravity-dns-redirect.so";
/// Source file for the DNS redirect preload library (relative to binary).
const DNS_REDIRECT_C_SOURCE: &str = include_str!("../mitm/dns_redirect.c");

View File

@@ -57,9 +57,9 @@ impl StandaloneLS {
1, // DETECT_AND_USE_PROXY_ENABLED
);
// Setup data dir (mode 1777 so both current user and antigravity-ls can write)
// Setup data dir (mode 1777 so both current user and zerogravity-ls can write)
let gemini_dir = format!("{DATA_DIR}/.gemini");
let app_data_dir = format!("{DATA_DIR}/.gemini/antigravity-standalone");
let app_data_dir = format!("{DATA_DIR}/.gemini/zerogravity-standalone");
let annotations_dir = format!("{app_data_dir}/annotations");
let brain_dir = format!("{app_data_dir}/brain");
for dir in [
@@ -77,7 +77,7 @@ impl StandaloneLS {
}
}
// Check if data dir is writable by writing a test file.
// Old runs as `antigravity-ls` user leave dirs owned by that user.
// Old runs as `zerogravity-ls` user leave dirs owned by that user.
let test_path = format!("{app_data_dir}/.write_test");
if std::fs::write(&test_path, b"ok").is_err() {
eprintln!(
@@ -138,12 +138,12 @@ impl StandaloneLS {
})
.unwrap_or_else(|| {
// Fall back to env var / token file
let token = std::env::var("ANTIGRAVITY_OAUTH_TOKEN")
let token = std::env::var("ZEROGRAVITY_TOKEN")
.ok()
.filter(|s| !s.is_empty())
.or_else(|| {
let home = std::env::var("HOME").unwrap_or_default();
let path = format!("{home}/.config/antigravity-proxy-token");
let path = format!("{home}/.config/zerogravity/token");
std::fs::read_to_string(&path)
.ok()
.map(|s| s.trim().to_string())
@@ -153,7 +153,7 @@ impl StandaloneLS {
if !token.is_empty() {
info!("Loaded OAuth token from env/file (no refresh token — manual refresh needed)");
} else {
eprintln!("[headless] ⚠ No OAuth token found. Login to Antigravity first, or set ANTIGRAVITY_OAUTH_TOKEN");
eprintln!("[headless] ⚠ No OAuth token found. Login to Antigravity first, or set ZEROGRAVITY_TOKEN");
}
(token, None)
});
@@ -224,7 +224,7 @@ impl StandaloneLS {
"https://daily-cloudcode-pa.googleapis.com".to_string()
},
"-app_data_dir".to_string(),
"antigravity-standalone".to_string(),
"zerogravity-standalone".to_string(),
"-gemini_dir".to_string(),
gemini_dir,
];
@@ -240,16 +240,16 @@ impl StandaloneLS {
if let Some(mitm) = mitm_config {
// Go's SSL_CERT_FILE replaces the entire system cert pool, so we
// need a combined bundle: system CAs + our MITM CA
// Write to /tmp — accessible by antigravity-ls user
// Write to /tmp — accessible by zerogravity-ls user
// (user's ~/.config/ is not traversable by other UIDs)
let combined_ca_path = "/tmp/antigravity-mitm-combined-ca.pem".to_string();
let combined_ca_path = "/tmp/zerogravity-mitm-ca.pem".to_string();
let system_ca =
std::fs::read_to_string("/etc/ssl/certs/ca-certificates.crt").unwrap_or_default();
let mitm_ca = std::fs::read_to_string(&mitm.ca_cert_path)
.map_err(|e| format!("Failed to read MITM CA cert: {e}"))?;
std::fs::write(&combined_ca_path, format!("{system_ca}\n{mitm_ca}"))
.map_err(|e| format!("Failed to write combined CA bundle: {e}"))?;
// Make readable by antigravity-ls user
// Make readable by zerogravity-ls user
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
@@ -286,18 +286,18 @@ impl StandaloneLS {
env_vars.push(("LD_PRELOAD".into(), so));
env_vars.push((
"DNS_REDIRECT_LOG".into(),
"/tmp/antigravity-dns-redirect.log".into(),
"/tmp/zerogravity-dns-redirect.log".into(),
));
}
}
}
// In headless mode, never use sudo — run as current user
// In normal mode, use sudo if 'antigravity-ls' user exists
// In normal mode, use sudo if 'zerogravity-ls' user exists
let use_sudo = !headless && has_ls_user();
let mut cmd = if use_sudo {
info!("Using UID isolation: spawning LS as 'antigravity-ls' user");
info!("Using UID isolation: spawning LS as 'zerogravity-ls' user");
let mut c = Command::new("sudo");
c.args(["-n", "-u", LS_USER, "--", "/usr/bin/env"]);
for (k, v) in &env_vars {
@@ -317,7 +317,7 @@ impl StandaloneLS {
};
// Capture stderr for debugging — logs to /tmp so we can diagnose LS failures
let stderr_file = std::fs::File::create("/tmp/antigravity-ls-debug.log")
let stderr_file = std::fs::File::create("/tmp/zerogravity-ls-debug.log")
.map_err(|e| format!("Failed to create LS debug log: {e}"))?;
cmd.stdin(Stdio::piped())
.stdout(Stdio::null())
@@ -342,7 +342,7 @@ impl StandaloneLS {
let ls_pid = if use_sudo {
// Give sudo a moment to spawn the real process
std::thread::sleep(std::time::Duration::from_millis(500));
// Find the LS process owned by antigravity-ls user
// Find the LS process owned by zerogravity-ls user
find_ls_pid_for_user(LS_USER).ok()
} else {
Some(child.id())
@@ -424,7 +424,7 @@ impl StandaloneLS {
// The child is sudo which already exited. Kill the actual LS.
if let Some(pid) = self.ls_pid {
info!(pid, "Killing LS process via sudo -u {}", LS_USER);
// Run kill AS the antigravity-ls user (same UID can signal)
// Run kill AS the zerogravity-ls user (same UID can signal)
let ok = std::process::Command::new("sudo")
.args(["-n", "-u", LS_USER, "kill", "-TERM", &pid.to_string()])
.stdout(Stdio::null())

View File

@@ -1,7 +1,7 @@
//! Per-call debug trace system.
//!
//! Every API call gets a structured JSON trace file written to
//! `~/.config/antigravity-proxy/traces/{YYYY-MM-DD}/{HH-MM-SS}_{cascade_short}.json`.
//! `~/.config/zerogravity/traces/{YYYY-MM-DD}/{HH-MM-SS}_{cascade_short}.json`.
//!
//! Designed for LLM consumption: compact, structured, no raw bodies.
@@ -23,7 +23,7 @@ impl TraceCollector {
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
let traces_dir = PathBuf::from(home)
.join(".config")
.join("antigravity-proxy")
.join("zerogravity")
.join("traces");
Self {
enabled,