{"id":39552388,"url":"https://github.com/leenhawk/gproxy","last_synced_at":"2026-04-15T17:01:14.525Z","repository":{"id":337150136,"uuid":"1147156724","full_name":"LeenHawk/gproxy","owner":"LeenHawk","description":"gproxy is a Rust-based multi-channel LLM proxy that exposes OpenAI / Claude / Gemini-style APIs through a unified gateway, with a built-in admin console, user/key management, and request/usage auditing.","archived":false,"fork":false,"pushed_at":"2026-04-12T07:31:29.000Z","size":9541,"stargazers_count":92,"open_issues_count":1,"forks_count":13,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-12T09:31:06.392Z","etag":null,"topics":["claude","gemini","gpt","llm-proxy"],"latest_commit_sha":null,"homepage":"https://gproxy.leenhawk.com","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LeenHawk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"ko_fi":"leenhawk"}},"created_at":"2026-02-01T09:42:21.000Z","updated_at":"2026-04-12T07:21:45.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/LeenHawk/gproxy","commit_stats":null,"previous_names":["leenhawk/gproxy"],"tags_count":81,"template":false,"template_full_name":null,"purl":"pkg:github/LeenHawk/gproxy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeenHawk%2Fgproxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeenHawk%2Fgproxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeenHawk%2Fgproxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeenHawk%2Fgproxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LeenHawk","download_url":"https://codeload.github.com/LeenHawk/gproxy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeenHawk%2Fgproxy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31851057,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T15:24:51.572Z","status":"ssl_error","status_checked_at":"2026-04-15T15:24:39.138Z","response_time":63,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["claude","gemini","gpt","llm-proxy"],"created_at":"2026-01-18T06:56:15.900Z","updated_at":"2026-04-15T17:01:14.507Z","avatar_url":"https://github.com/LeenHawk.png","language":"Rust","funding_links":["https://ko-fi.com/leenhawk"],"categories":[],"sub_categories":[],"readme":"# Deployment Guide\n\n### Build\n\nSingle-instance release build:\n\n```bash\ncargo build -p gproxy --release\n```\n\nIf you changed the embedded console frontend, build it before packaging or running the binary:\n\n```bash\ncd frontend/console\npnpm install\npnpm build\n```\n\nThe output binary is located at `target/release/gproxy`.\n\n### Embedded Console\n\nThe current binary includes an embedded browser console mounted at `/console`.\n\n- Console URL: `http://127.0.0.1:8787/console`\n- Browser login: `POST /login`\n- Browser auth header: `Authorization: Bearer \u003csession_token\u003e`\n\nTypical local workflow:\n\n```bash\ncd frontend/console\npnpm install\npnpm build\n\ncargo run -p gproxy\n```\n\nThen open `/console` in a browser and log in with a current v1 username and password.\n\n### Environment Variables\n\nThe full set of startup parameters and their corresponding environment variables are defined in `apps/gproxy/src/main.rs`:\n\n| Environment Variable | Default | Required | Description |\n| --- | --- | --- | --- |\n| `GPROXY_HOST` | `127.0.0.1` | No | Listen address. |\n| `GPROXY_PORT` | `8787` | No | Listen port. |\n| `GPROXY_ADMIN_USER` | `admin` | No | Bootstrap admin username used when creating or reconciling the admin account. |\n| `GPROXY_ADMIN_PASSWORD` | None | No | Bootstrap admin password. On first startup, if an admin account must be created and no password is provided, one is generated and logged once. |\n| `GPROXY_ADMIN_API_KEY` | None | No | Bootstrap admin API key. On first startup, if an admin account must be created and no API key is provided, one is generated and logged once. |\n| `GPROXY_DSN` | If unset, `sqlite://\u003cdata_dir\u003e/gproxy.db?mode=rwc` is generated automatically. | No | Database DSN. |\n| `GPROXY_PROXY` | None | No | Upstream HTTP proxy. |\n| `GPROXY_SPOOF` | `chrome_136` | No | TLS fingerprint emulation name. |\n| `DATABASE_SECRET_KEY` | None | No | Database-at-rest encryption key; when set, credentials, passwords, and API keys are encrypted at rest with XChaCha20Poly1305. |\n| `GPROXY_REDIS_URL` | None | No | Redis DSN; the Redis backend is enabled only when the binary is built with the `redis` feature. |\n| `GPROXY_CONFIG` | `gproxy.toml` | No | TOML config path used as the seed file during first-time initialization. |\n| `GPROXY_DATA_DIR` | `./data` | No | Data directory; the default SQLite file and runtime data are based on this directory. |\n\nAdditional Notes:\n\n- CLI arguments and environment variables are both parsed by `clap`; explicit CLI values take priority over defaults.\n- If the database already contains `global_settings` and `GPROXY_DSN` / `GPROXY_DATA_DIR` were not passed explicitly at startup, the process will reconnect to the database using the persisted configuration.\n\n### TOML Config Format\n\nThe TOML file pointed to by `GPROXY_CONFIG` is only used during initialization when the database does not already contain data. The corresponding structure is defined in `crates/gproxy-api/src/admin/config_toml.rs`.\n\n```toml\n[global]\nhost = \"0.0.0.0\"\nport = 8787\nproxy = \"http://127.0.0.1:7890\"\nspoof_emulation = \"chrome_136\"\nupdate_source = \"github\"\nenable_usage = true\nenable_upstream_log = false\nenable_upstream_log_body = false\nenable_downstream_log = false\nenable_downstream_log_body = false\ndsn = \"sqlite://./data/gproxy.db?mode=rwc\"\ndata_dir = \"./data\"\n\n[[providers]]\nname = \"openai-main\"\nchannel = \"openai\"\nsettings = { base_url = \"https://api.openai.com/v1\" }\ncredentials = [\n  { api_key = \"sk-provider-1\" }\n]\n\n[[models]]\nprovider_name = \"openai-main\"\nmodel_id = \"gpt-4.1-mini\"\ndisplay_name = \"GPT-4.1 mini\"\nenabled = true\nprice_each_call = 0.0\n\n[[model_aliases]]\nalias = \"chat-default\"\nprovider_name = \"openai-main\"\nmodel_id = \"gpt-4.1-mini\"\nenabled = true\n\n[[users]]\nname = \"alice\"\npassword = \"plain-text-or-argon2-phc\"\nenabled = true\n\n[[users.keys]]\napi_key = \"sk-user-1\"\nlabel = \"default\"\nenabled = true\n\n[[permissions]]\nuser_name = \"alice\"\nprovider_name = \"openai-main\"\nmodel_pattern = \"gpt-*\"\n\n[[file_permissions]]\nuser_name = \"alice\"\nprovider_name = \"openai-main\"\n\n[[rate_limits]]\nuser_name = \"alice\"\nmodel_pattern = \"gpt-*\"\nrpm = 60\nrpd = 10000\ntotal_tokens = 200000\n\n[[quotas]]\nuser_name = \"alice\"\nquota = 100.0\ncost_used = 0.0\n```\n\nField Descriptions:\n\n- `[global]` covers global listen address, logging, update source, DSN, and data directory configuration.\n- `[[providers]]` defines a provider; `settings` and `credentials` are both JSON values read via `serde_json::Value`.\n- `[[models]]` / `[[model_aliases]]` define forwardable models and their aliases.\n- Admin access is represented by `[[users]]` entries with `is_admin = true` and at least one enabled `[[users.keys]]` entry. If the seed config does not define such an admin, startup can bootstrap one from `GPROXY_ADMIN_USER`, `GPROXY_ADMIN_PASSWORD`, and `GPROXY_ADMIN_API_KEY`.\n- The `password` field under `[[users]]` can be either plaintext or a direct Argon2 PHC hash.\n- `[[users.keys]]` is a nested array table representing the user's API key list.\n- `[[permissions]]`, `[[file_permissions]]`, `[[rate_limits]]`, and `[[quotas]]` correspond to model permissions, file permissions, rate limiting, and cost quotas respectively.\n\n### Database Support\n\n`gproxy-storage` compiles in three database backends via SeaORM / SQLx:\n\n| Database | DSN Prefix | Description |\n| --- | --- | --- |\n| SQLite | `sqlite:` | Default mode; if `GPROXY_DSN` is not set explicitly, startup generates a SQLite file DSN automatically. |\n| PostgreSQL | `postgres:` | Provided by `sqlx-postgres` and the SeaORM Postgres feature. |\n| MySQL | `mysql:` | Provided by `sqlx-mysql` and the SeaORM MySQL feature. |\n\nCommon DSN examples:\n\n```text\nsqlite://./data/gproxy.db?mode=rwc\npostgres://gproxy:secret@127.0.0.1:5432/gproxy\nmysql://gproxy:secret@127.0.0.1:3306/gproxy\n```\n\nAfter establishing the connection, `SeaOrmStorage::connect()` will:\n\n1. Optionally load the database encryptor corresponding to `DATABASE_SECRET_KEY`.\n2. Apply per-database connection tuning parameters.\n3. Connect to the database and run `sync()` to synchronize the schema.\n\n### Graceful Shutdown\n\nGraceful shutdown behavior is jointly implemented by `apps/gproxy/src/main.rs` and `apps/gproxy/src/workers/mod.rs`:\n\n1. The process listens for `Ctrl+C`; on Unix it also listens for `SIGTERM`.\n2. Once shutdown is triggered, the Axum server enters the `with_graceful_shutdown` flow and stops accepting new requests.\n3. The main thread then calls `worker_set.shutdown()`, broadcasting the shutdown signal to all workers.\n4. `WorkerSet` waits up to 5 seconds for workers to drain.\n5. `UsageSink` closes its receiver, drains remaining usage messages, and performs a final batch write.\n6. `HealthBroadcaster` flushes any health states still in its debounce window to the database.\n7. `QuotaReconciler` and `RateLimitGC` exit their next loop iteration upon receiving the signal.\n8. If any workers have not finished within 5 seconds, the process logs a warning but does not block indefinitely.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleenhawk%2Fgproxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleenhawk%2Fgproxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleenhawk%2Fgproxy/lists"}