{"id":27110412,"url":"https://github.com/emersonfelipesp/proxbox-api","last_synced_at":"2026-05-05T17:02:14.048Z","repository":{"id":286346083,"uuid":"960997724","full_name":"emersonfelipesp/proxbox-api","owner":"emersonfelipesp","description":"Backend of NetBox Proxbox Plugin using FastAPI","archived":false,"fork":false,"pushed_at":"2026-04-30T22:03:50.000Z","size":9962,"stargazers_count":3,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-30T23:09:47.800Z","etag":null,"topics":["backend","fastapi","integration","netbox","proxmox","sqlmodel"],"latest_commit_sha":null,"homepage":"https://emersonfelipesp.github.io/proxbox-api/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/emersonfelipesp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-04-05T14:34:41.000Z","updated_at":"2026-04-30T22:03:11.000Z","dependencies_parsed_at":"2025-04-09T21:51:52.045Z","dependency_job_id":"521a03dd-ed11-49a3-9995-99509fc07650","html_url":"https://github.com/emersonfelipesp/proxbox-api","commit_stats":null,"previous_names":["emersonfelipesp/proxbox-api"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/emersonfelipesp/proxbox-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emersonfelipesp%2Fproxbox-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emersonfelipesp%2Fproxbox-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emersonfelipesp%2Fproxbox-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emersonfelipesp%2Fproxbox-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emersonfelipesp","download_url":"https://codeload.github.com/emersonfelipesp/proxbox-api/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emersonfelipesp%2Fproxbox-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32658954,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-05T11:29:49.557Z","status":"ssl_error","status_checked_at":"2026-05-05T11:29:48.587Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["backend","fastapi","integration","netbox","proxmox","sqlmodel"],"created_at":"2025-04-06T23:51:24.004Z","updated_at":"2026-05-05T17:02:14.042Z","avatar_url":"https://github.com/emersonfelipesp.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Installing proxbox-api (Plugin backend made using FastAPI)\n\n## Tooling: uv + Ruff + ty\n\nThis repo uses [uv](https://docs.astral.sh/uv/) to install Python and dependencies, [Ruff](https://docs.astral.sh/ruff/) for linting and formatting, and [ty](https://github.com/astral-sh/ty) for type checking.\n\n```bash\n# Runtime only\nuv sync\n\n# Tests + Ruff (matches CI)\nuv sync --extra test --group dev\n\n# Documentation (MkDocs)\nuv sync --extra docs --group dev\n```\n\n```bash\nuv run ruff check .\nuv run ruff format .\nuv run ty check proxbox_api/types proxbox_api/utils/retry.py\nuv run pytest tests\nuv run mkdocs serve   # after syncing with --extra docs\n```\n\n## Documentation (MkDocs Material)\n\nProject documentation is available under `docs/` and built with MkDocs Material.\n\n### Local docs build\n\n```bash\nuv sync --extra docs --group dev\nuv run mkdocs serve\n```\n\n### Languages\n\n- English (default)\n- Brazilian Portuguese (`pt-BR`) as optional translation\n\n## Using docker (recommended)\n\nAll images are **Alpine-based** (smaller footprint), built from this repository with **uv** and **`uv.lock`** in a multi-stage Dockerfile. Three variants are published to Docker Hub:\n\n| Variant | Tags | Description |\n|---------|------|-------------|\n| **Raw** (default) | `latest`, `\u003cversion\u003e` | Pure uvicorn, HTTP only. Smallest image. |\n| **Nginx** | `latest-nginx`, `\u003cversion\u003e-nginx` | nginx terminates HTTPS via mkcert; proxies to uvicorn. |\n| **Granian** | `latest-granian`, `\u003cversion\u003e-granian` | [Granian](https://github.com/emmett-framework/granian) (Rust ASGI server) with native TLS via mkcert. No nginx. |\n\n\u003e **Upgrade note:** before v0.0.7, `latest` was the nginx+HTTP image. It is now the raw uvicorn image. Pull `latest-nginx` for the previous behavior.\n\n### Raw image (default)\n\nPlain uvicorn on HTTP — the simplest option for local dev or when you put your own proxy in front.\n\n```bash\ndocker pull emersonfelipesp/proxbox-api:latest\ndocker run -d -p 8000:8000 --name proxbox-api emersonfelipesp/proxbox-api:latest\n```\n\nBuild from source:\n\n```bash\ndocker build -t proxbox-api:raw .\ndocker run -d -p 8000:8000 proxbox-api:raw\n```\n\n### Nginx image (nginx + mkcert HTTPS + uvicorn)\n\n**nginx** terminates HTTPS on `PORT` (default **8000**) using certificates from [mkcert](https://github.com/FiloSottile/mkcert) and proxies to **uvicorn** on `127.0.0.1:8001`. **supervisord** manages both processes. The nginx config disables proxy buffering so chunked / SSE responses flow through unmodified.\n\n```bash\ndocker pull emersonfelipesp/proxbox-api:latest-nginx\ndocker run -d -p 8443:8000 --name proxbox-api-nginx \\\n  emersonfelipesp/proxbox-api:latest-nginx\n```\n\nBuild from source:\n\n```bash\ndocker build --target nginx -t proxbox-api:nginx .\ndocker run -d -p 8443:8000 proxbox-api:nginx\n```\n\n### Granian image (granian + mkcert HTTPS)\n\n[Granian](https://github.com/emmett-framework/granian) is a Rust-based ASGI server with native HTTP/2, WebSocket, and TLS support. This variant eliminates nginx and supervisord — a single granian process handles everything.\n\n```bash\ndocker pull emersonfelipesp/proxbox-api:latest-granian\ndocker run -d -p 8443:8000 --name proxbox-api-granian \\\n  emersonfelipesp/proxbox-api:latest-granian\n```\n\nBuild from source:\n\n```bash\ndocker build --target granian -t proxbox-api:granian .\ndocker run -d -p 8443:8000 proxbox-api:granian\n```\n\n### Docker runtime environment variables\n\nCommon to all images (`raw`, `nginx`, `granian`):\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `PORT` | `8000` | Port the server listens on |\n| `PROXBOX_BIND_HOST` | `0.0.0.0` | Bind address for the API server. Set to `::` for IPv4 + IPv6 dual-stack. Honored by the `raw` and `granian` images; the `nginx` image listens on both stacks unconditionally. |\n\nmkcert-specific (only for `nginx` and `granian`):\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `MKCERT_CERT_DIR` | `/certs` | Directory where certs are stored |\n| `MKCERT_EXTRA_NAMES` | — | Extra SANs (commas or spaces), e.g. `proxbox.lan,10.0.0.5` |\n| `CAROOT` | — | Mount a volume here to persist the local CA across container restarts |\n\n```bash\ndocker run -d -p 8443:8000 --name proxbox-api-tls \\\n  -e MKCERT_EXTRA_NAMES='myhost.local,192.168.1.10' \\\n  emersonfelipesp/proxbox-api:latest-nginx\n```\n\n### Binding to IPv6 / dual-stack\n\n```bash\ndocker run -d -p 8000:8000 -e PROXBOX_BIND_HOST=:: \\\n  emersonfelipesp/proxbox-api:latest\n```\n\nIn Docker Compose `environment:` **list-form**, the value is taken verbatim — quotes are NOT stripped — so `- PROXBOX_BIND_HOST=\"::\"` arrives in the container as the literal string `\"::\"`. The container sanitizes surrounding quotes defensively, but the recommended forms are:\n\n```yaml\nenvironment:\n  - PROXBOX_BIND_HOST=::          # list-form: NO quotes\n```\n\n```yaml\nenvironment:\n  PROXBOX_BIND_HOST: \"::\"         # map-form: YAML strips the quotes\n```\n\nTo run a shell instead of starting the server, pass a command (the entrypoint delegates to it):\n\n```bash\ndocker run --rm emersonfelipesp/proxbox-api:latest-nginx sh\n```\n\n## Installing from PyPI\n\nThe package is published to [PyPI](https://pypi.org/project/proxbox-api/) as `proxbox-api`.\n\n```bash\npip install proxbox-api\n```\n\nOr with `uv`:\n\n```bash\nuv add proxbox-api\n```\n\nStart the server after installing:\n\n```bash\npython -m uvicorn proxbox_api.main:app --host 0.0.0.0 --port 8000\n```\n\n## Using git repository\n\n### Clone the repository\n\n```\ngit clone https://github.com/emersonfelipesp/proxbox-api.git\n```\n\n### Change to project root folder\n\n```\ncd proxbox-api\n```\n\n### Install dependencies\n\nFrom the repository root (where `pyproject.toml` lives):\n\n```\nuv sync\n```\n\n### Start the FastAPI app (recommended)\n\nFrom the repository root:\n\n```\nuv run fastapi run proxbox_api.main:app --host 0.0.0.0 --port 8000\n```\n\n- `--host 0.0.0.0` will make the app available on all host network interfaces, which my not be recommended.\nJust pass your desired IP like `--host \u003cYOUR-IP\u003e` and it will also work.\n\n- `--port 8000` is the default port, but you can change it if needed. Just to remember to update it on NetBox also, at FastAPI Endpoint model.\n\n### Cache Configuration (optional)\n\nControl NetBox API request caching to optimize sync performance:\n\n```bash\n# 5-minute TTL (default is 60 seconds)\nexport PROXBOX_NETBOX_GET_CACHE_TTL=300\n\n# Disable caching entirely\nexport PROXBOX_NETBOX_GET_CACHE_TTL=0\n\n# Increase max entries (default 4096)\nexport PROXBOX_NETBOX_GET_CACHE_MAX_ENTRIES=8192\n\n# Set max cache size in bytes (default 52428800 = 50MB)\nexport PROXBOX_NETBOX_GET_CACHE_MAX_BYTES=104857600  # 100MB\n\n# Enable debug logging\nexport PROXBOX_DEBUG_CACHE=1\n\nuv run fastapi run proxbox_api.main:app --host 0.0.0.0 --port 8000\n```\n\nCache metrics are available at `GET /cache` and `GET /cache/metrics/prometheus`.\n\n### Backup Sync Throttling (optional)\n\nControl backup synchronization batch size and delay to prevent overwhelming NetBox's PostgreSQL connection pool:\n\n```bash\n# Batch size for backup sync (default 5, was 10 before fix)\nexport PROXBOX_BACKUP_BATCH_SIZE=5\n\n# Delay between batches in milliseconds (default 200ms)\nexport PROXBOX_BACKUP_BATCH_DELAY_MS=200\n\nuv run fastapi run proxbox_api.main:app --host 0.0.0.0 --port 8000\n```\n\n**Why adjust these?**\n- **Smaller batch size (3-5)**: Use when NetBox has limited PostgreSQL connections or many concurrent users\n- **Larger batch size (10-20)**: Safe if NetBox has a large PostgreSQL pool (50+ connections) and dedicated hardware\n- **Longer delay (500-1000ms)**: Helps when \"database unavailable\" errors appear during full sync\n- **Shorter delay (0-100ms)**: Faster sync when NetBox is lightly loaded\n\n**Symptoms of incorrect tuning:**\n- HTTP 500 \"database unavailable\" during backup/VM sync → decrease batch size, increase delay\n- HTTP 502 \"Response ended prematurely\" → decrease batch size, increase delay\n- Slow sync performance on powerful hardware → increase batch size, decrease delay\n\n### Alternative: pip editable install\n\n```\npip install -e .\nfastapi run proxbox_api.main:app --host 0.0.0.0 --port 8000\n```\n\nOr with uvicorn:\n\n```\nuvicorn proxbox_api.main:app --host 0.0.0.0 --port 8000\n```\n\n## HTTPS without Docker\n\n### Local development: mkcert\n\nInstall the local CA in your system trust store, generate a cert, then point uvicorn at the PEM files:\n\n```\nmkcert -install\nmkcert proxbox.backend.local localhost 127.0.0.1 ::1\n```\n\nFrom the repository root (adjust paths to the files mkcert printed):\n\n```\nuv run uvicorn proxbox_api.main:app --host 127.0.0.1 --port 8000 --reload \\\n  --ssl-keyfile=./proxbox.backend.local+3-key.pem \\\n  --ssl-certfile=./proxbox.backend.local+3.pem\n```\n\n**NetBox plugin layout** (paths differ): example with `--app-dir` and module path as in your install:\n\n```\n/opt/netbox/venv/bin/uvicorn netbox-proxbox.proxbox_api.proxbox_api.main:app \\\n  --host 127.0.0.1 --port 8000 --app-dir /opt/netbox/netbox \\\n  --ssl-keyfile=/path/to/localhost+2-key.pem \\\n  --ssl-certfile=/path/to/localhost+2.pem\n```\n\nOptional **nginx** in front of that uvicorn: copy or adapt a site config that `proxy_pass`es to `http://127.0.0.1:8000` and terminates TLS on 443 (same idea as [TLS with a real certificate](#tls-with-a-real-certificate-no-docker) below).\n\n### TLS with a real certificate (no Docker)\n\nUse **Let’s Encrypt**, a **corporate CA**, or any PEM **full chain + private key**. Prefer terminating TLS in **nginx or Caddy** and keeping the app on plain HTTP on localhost; uvicorn TLS is fine for small setups if you accept Python as the TLS endpoint.\n\n**1. Certificate files**\n\n- **Let’s Encrypt (Certbot):** typically `/etc/letsencrypt/live/\u003cyour-domain\u003e/fullchain.pem` and `privkey.pem`. Renew with `certbot renew`; reload nginx (or your proxy) after renewal.\n- **Corporate / manual:** use the PEM the CA gave you: **certificate file** must include the **full chain** (leaf + intermediates) in one file, plus the **unencrypted private key** (or use `--ssl-keyfile-password` with uvicorn if the key is encrypted).\n\n**2. Recommended: reverse proxy on the same host**\n\nRun the API on HTTP bound to loopback only, proxy from 443:\n\n```\nuv run uvicorn proxbox_api.main:app --host 127.0.0.1 --port 8000\n```\n\nExample **nginx** `server` block (replace domain and paths):\n\n```nginx\nserver {\n    listen 443 ssl http2;\n    server_name api.example.com;\n\n    ssl_certificate     /etc/letsencrypt/live/api.example.com/fullchain.pem;\n    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;\n\n    location / {\n        proxy_pass http://127.0.0.1:8000;\n        proxy_http_version 1.1;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection $connection_upgrade;\n        proxy_read_timeout 86400;\n        proxy_send_timeout 86400;\n        proxy_buffering off;\n    }\n}\n```\n\nFor `Connection $connection_upgrade`, add at `http` level:\n\n```nginx\nmap $http_upgrade $connection_upgrade {\n    default upgrade;\n    ''      close;\n}\n```\n\nReload nginx after editing. Point NetBox’s FastAPI endpoint at `https://api.example.com` (and port **443** or your chosen HTTPS port).\n\n**3. Alternative: uvicorn serves TLS directly**\n\n```\nuv run uvicorn proxbox_api.main:app --host 0.0.0.0 --port 8443 \\\n  --ssl-certfile=/etc/letsencrypt/live/api.example.com/fullchain.pem \\\n  --ssl-keyfile=/etc/letsencrypt/live/api.example.com/privkey.pem\n```\n\nEnsure the user running uvicorn can read the key (often `root` owns `/etc/letsencrypt`; use `group` ACLs, a deploy user + copied certs, or **hitch**/proxy instead). Use **`fullchain.pem`** for `--ssl-certfile` so clients receive the full chain.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femersonfelipesp%2Fproxbox-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femersonfelipesp%2Fproxbox-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femersonfelipesp%2Fproxbox-api/lists"}