{"id":35423093,"url":"https://github.com/heptau/pgarachne","last_synced_at":"2026-04-01T18:48:43.935Z","repository":{"id":330908918,"uuid":"1122949280","full_name":"heptau/pgarachne","owner":"heptau","description":"🕷️ Turn PostgreSQL into a secure JSON-RPC API instantly. Zero boilerplate, high performance, native DB auth, and AI-ready schema.","archived":false,"fork":false,"pushed_at":"2026-03-19T20:13:24.000Z","size":69713,"stargazers_count":5,"open_issues_count":6,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-20T10:51:08.771Z","etag":null,"topics":["ai-friendly","api","api-gateway","backendless","database-api","database-gateway","http-api","json-rpc","json-rpc2","jwt-authentication","llm-integration","opensource","postgres","postgres-functions","postgresql","rapid-prototyping","row-level-security","static-file-server","web-server"],"latest_commit_sha":null,"homepage":"https://www.pgarachne.com","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/heptau.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","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":{"buy_me_a_coffee":"pgarachne","custom":["https://www.paypal.com/donate/?hosted_button_id=YUAEVQGPYZU8U","https://revolut.me/zbynekvanzura","https://wise.com/pay/me/zbynekv17","https://app.binance.com/uni-qr/DbrFdVHA","https://github.com/heptau/pgarachne/blob/master/docs/index.html#support"]}},"created_at":"2025-12-25T22:22:55.000Z","updated_at":"2026-03-19T20:13:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/heptau/pgarachne","commit_stats":null,"previous_names":["heptau/pgarachne"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/heptau/pgarachne","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heptau%2Fpgarachne","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heptau%2Fpgarachne/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heptau%2Fpgarachne/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heptau%2Fpgarachne/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/heptau","download_url":"https://codeload.github.com/heptau/pgarachne/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heptau%2Fpgarachne/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31290952,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"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":["ai-friendly","api","api-gateway","backendless","database-api","database-gateway","http-api","json-rpc","json-rpc2","jwt-authentication","llm-integration","opensource","postgres","postgres-functions","postgresql","rapid-prototyping","row-level-security","static-file-server","web-server"],"created_at":"2026-01-02T17:15:05.194Z","updated_at":"2026-04-01T18:48:43.929Z","avatar_url":"https://github.com/heptau.png","language":"HTML","funding_links":["https://buymeacoffee.com/pgarachne","https://www.paypal.com/donate/?hosted_button_id=YUAEVQGPYZU8U","https://revolut.me/zbynekvanzura","https://wise.com/pay/me/zbynekv17","https://app.binance.com/uni-qr/DbrFdVHA","https://github.com/heptau/pgarachne/blob/master/docs/index.html#support"],"categories":[],"sub_categories":[],"readme":"# PgArachne\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"docs-src/static/assets/pgarachne-logo.webp\" alt=\"PgArachne Logo\" width=\"200\"/\u003e\n  \u003ch1\u003ePgArachne\u003c/h1\u003e\n  \u003cp\u003e\u003cstrong\u003eTurn PostgreSQL into a secure API. Instantly.\u003c/strong\u003e\u003c/p\u003e\n  \u003cp\u003eZero boilerplate. High performance. The middleware that maps HTTP requests directly to database functions.\u003c/p\u003e\n  \u003ca href=\"#quick-start\"\u003eGet Started\u003c/a\u003e • \u003ca href=\"https://www.pgarachne.com/\"\u003eRead Full Documentation\u003c/a\u003e\n\u003c/div\u003e\n\n---\n\n**PgArachne™** is a high-performance JSON-RPC 2.0 API gateway that maps JSON-RPC methods to PostgreSQL functions (access via `schema.function`). It is optimized for AI consumption with dynamic function discovery, secure authentication, and production-ready features.\n\n## Key Features\n\n*   **🚀 Rapid Prototyping**: Stop writing boilerplate CRUD controllers. Define a SQL function, and your API endpoint is ready instantly.\n*   **🏢 Production Ready**: Handles connection pooling, graceful shutdowns, and Prometheus metrics.\n*   **🧠 AI \u0026 LLM Friendly**: Self-describing API via `capabilities` endpoint allows AI agents to construct valid calls with zero hallucinations.\n*   **🤖 MCP Support**: Native Model Context Protocol endpoint lets Claude Desktop, Cursor, and other MCP clients discover and call your PostgreSQL functions as tools — no custom glue code needed.\n*   **🔒 Secure**: Native PostgreSQL role masquerading and JWT authentication.\n\n## Quick Start\n\n### 1. Installation\n\n**Option A: Download Binaries**\nDownload the latest version directly from the project's releases page:\n👉 https://github.com/heptau/pgarachne/releases\n\n**Option B: Install via Homebrew**\n```bash\n# CLI (macOS + Linux)\nbrew install heptau/tap/pgarachne\n\n# GUI app (macOS)\nbrew install --cask heptau/tap/pgarachne-app\n```\n\n**Option C: Build from Source**\n```bash\ngit clone https://github.com/heptau/pgarachne.git\ncd pgarachne\nmake build\n```\n\n### 2. Database Setup\n\n1. Create a database (e.g., `my_database`).\n2. Run the schema script to create the necessary `pgarachne` structure.\n\n```bash\npsql -d my_database -f sql/schema.sql\n```\n\nNote: `sql/schema.sql` will try to create the `pgarachne_admin` role and grant it to `pgarachne`. If you run the script without superuser privileges, role creation is skipped. In that case, create the role and grant it manually (or run the script as a superuser).\nThe proxy user (`DB_USER`) must be a member of `pgarachne` and `pgarachne_admin` so it can verify and mint API tokens.\n\n3. Create the `pgarachne` system user (optional but recommended for production):\n\n```sql\n-- Connect to your database\nCREATE ROLE pgarachne WITH LOGIN PASSWORD 'secure_password';\nGRANT ALL PRIVILEGES ON DATABASE my_database TO pgarachne;\n-- Ensure it can use the schema\nGRANT USAGE ON SCHEMA pgarachne TO pgarachne;\nGRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA pgarachne TO pgarachne;\n```\n\n### 3. Configuration\n\n**1. Authentication Setup (.pgpass)**\n\nSince PgArachne does not store the database password in the configuration file, you should save it in your `~/.pgpass` file to allow the `pgarachne` user to connect:\n\n```bash\n# Format: hostname:port:database:username:password\necho \"localhost:5432:*:pgarachne:secure_password\" \u003e\u003e ~/.pgpass\nchmod 0600 ~/.pgpass\n```\n\n**2. Environment Configuration**\n\nCreate a configuration file (e.g., `.env`) with your database details:\n\n```ini\nDB_HOST=localhost\nDB_PORT=5432\nDB_USER=pgarachne\n# Optional: URL prefix (default: \"db\" → /db/:database/jsonrpc)\n# Set to \"api\" for backward-compatible paths.\n# API_PREFIX=db\n# Optional TLS settings (default sslmode=disable)\nDB_SSLMODE=disable\n# DB_SSLROOTCERT=/path/to/ca.pem\n# DB_SSLCERT=/path/to/client-cert.pem\n# DB_SSLKEY=/path/to/client-key.pem\n# Optional login rate limiting (default: 5 attempts per 1m, set 0 to disable)\nLOGIN_RATE_LIMIT=5\nLOGIN_RATE_WINDOW=1m\n# Optional trusted proxies for client IP resolution (comma-separated)\nTRUSTED_PROXIES=127.0.0.1,10.0.0.0/8\n# Optional request body size limit in bytes (default: 2097152)\nMAX_REQUEST_BYTES=2097152\n# Optional PID file path for daemon mode (-start / -stop)\n# Default: OS user cache dir (fallback: temp dir)\n# PID_FILE=/absolute/path/to/pgarachne.pid\n# Optional metrics listener (default enabled, local-only)\nMETRICS_ENABLED=true\nMETRICS_LISTEN_ADDR=127.0.0.1:9090\n# Optional SSE settings\nSSE_MAX_CHANNELS=8\nSSE_MAX_CLIENTS=1000\nSSE_CLIENT_BUFFER=64\nSSE_SEND_TIMEOUT=2s\nSSE_HEARTBEAT=20s\nSSE_IDLE_TIMEOUT=90s\n# Note: Password is read from .pgpass\nJWT_SECRET=change_this_to_something_secret\nHTTP_PORT=8080\n```\n\nRequired variables: `DB_HOST`, `DB_PORT`, `DB_USER`, `JWT_SECRET`.\n\nIf you run PgArachne behind a reverse proxy, set `TRUSTED_PROXIES` so client IPs are resolved correctly and rate limiting cannot be spoofed.\n\nTo mint long-lived API tokens, run `pgarachne.add_api_token(...)` as a role that is a member of `pgarachne_admin`.\n\nStart the server:\n```bash\n./pgarachne -config .env\n```\n\n### 3. Running Tests\n\nTests include optional database integration checks. The easiest way is to use the provided Docker-based runner:\n\n```bash\n./scripts/run_tests.sh\n```\n\nThis will:\n1. Start a local Postgres container.\n2. Create roles, database, and schema.\n3. Run `go test ./...`.\n\nRequirements:\n* Docker Desktop (or Docker Engine)\n* Docker Compose v2 (`docker compose`)\n\nNotes:\n* Login rate limiting is in-memory per instance. In multi-instance deployments, use a shared limiter (e.g., Redis) if you need global enforcement.\n\n### 4. Hello World Example\n\nLet's create a simple API endpoint associated with a user.\n\n**1. Create User and Function**\n\nIn your database (`my_database`):\n\n```sql\n-- 1. Create a user who will log in to the API\nCREATE ROLE app_user WITH LOGIN PASSWORD 'user_password';\nGRANT USAGE ON SCHEMA api TO app_user;\n\n-- 2. Create the Hello World function\n-- Input: empty jsonb, Output: json\nCREATE OR REPLACE FUNCTION api.hello_world(payload jsonb)\nRETURNS json\nLANGUAGE sql\nAS $$\n    SELECT '\"Hello World\"'::json;\n$$;\n\n-- 3. Grant permission to the user\nGRANT EXECUTE ON FUNCTION api.hello_world(jsonb) TO app_user;\n```\n\n**2. Login via API**\n\nUse the JSON-RPC `login` method to obtain a JWT token:\n\n```bash\ncurl -X POST http://localhost:8080/db/my_database/jsonrpc \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"method\":\"login\",\"params\":{\"login\":\"app_user\",\"password\":\"user_password\"},\"id\":1}'\n```\n\nResponse:\n```json\n{\"jsonrpc\":\"2.0\",\"result\":{\"token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"},\"id\":1}\n```\n\n**3. Call the Function**\n\nUse the token to call the `hello_world` function:\n\n```bash\nexport TOKEN=\"YOUR_JWT_TOKEN_HERE\"\n\ncurl -X POST http://localhost:8080/db/my_database/jsonrpc \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"jsonrpc\": \"2.0\", \"method\": \"api.hello_world\", \"params\": {}, \"id\": 1}'\n```\n\nResponse:\n```json\n{\"jsonrpc\": \"2.0\", \"result\": \"Hello World\", \"id\": 1}\n```\n\n### 5. Real-time Notifications (SSE)\n\nClients can subscribe to PostgreSQL `NOTIFY` channels over Server-Sent Events:\n\n```bash\ncurl -N \"http://localhost:8080/db/my_database/sse?channels=orders,users\" \\\n  -H \"Authorization: Bearer $TOKEN\"\n```\n\nEach notification is delivered as JSON:\n\n```json\n{\"channel\":\"orders\",\"data\":{\"id\":123,\"status\":\"created\"}}\n```\n\nIf the payload is plain text, it is wrapped as a string in `data`.\n\nTo send a notification from PostgreSQL:\n\n```sql\n-- From psql or any database session:\nNOTIFY orders, '{\"id\":123,\"status\":\"created\"}';\n\n-- Or via a trigger / stored procedure:\nPERFORM pg_notify('orders', json_build_object('id', NEW.id, 'status', NEW.status)::text);\n```\n\n### 6. MCP (Model Context Protocol)\n\nMCP-compatible AI clients (Claude Desktop, Cursor, etc.) can connect directly to any database and discover its functions as tools:\n\n```\nPOST http://localhost:8080/db/my_database/mcp\n```\n\nThe MCP endpoint maps automatically:\n- `tools/list` → calls `pgarachne.capabilities()` as the authenticated role\n- `tools/call` → executes the named PostgreSQL function with the provided arguments\n\nAuthentication uses the same Bearer token (JWT or API token) as the JSON-RPC endpoint.\nPostgreSQL functions require **no changes** — they remain JSON-RPC-shaped.\n\n## HTTP Endpoints\n\n| Endpoint | Method | Description |\n|---|---|---|\n| `/db/:database/jsonrpc` | POST | JSON-RPC 2.0 gateway (including `login`) |\n| `/db/:database/sse` | GET | SSE stream for PostgreSQL `NOTIFY` channels |\n| `/db/:database/mcp` | POST | MCP (Model Context Protocol) endpoint |\n| `/health` | GET | Health check |\n| `/metrics` | GET | Prometheus metrics (dedicated listener, default `127.0.0.1:9090`) |\n\n**Legacy redirects** (307 Temporary Redirect, method-preserving):\n- `POST /api/:database` → `POST /db/:database/jsonrpc`\n- `GET /sse/:database` → `GET /db/:database/sse`\n\nThe `db` prefix is the default and is configurable via `API_PREFIX`.\n\nPrometheus metrics are exposed on a dedicated listener (default: `http://127.0.0.1:9090/metrics`).\n\nSSE metrics are exported via Prometheus:\n* `pgarachne_sse_clients{database=...}`\n* `pgarachne_sse_channels{database=...}`\n* `pgarachne_sse_client_drops_total{database=...,reason=...}`\n\nAdditional Prometheus metrics:\n* `pgarachne_http_requests_total{method=...,path=...,status=...}`\n* `pgarachne_http_request_duration_seconds{method=...,path=...,status=...}`\n* `pgarachne_auth_requests_total{type=...,result=...}`\n* `pgarachne_login_attempts_total{result=...}`\n* `pgarachne_jsonrpc_requests_total{method=...,result=...}`\n\n## Documentation\n\nDocumentation sources live in [`docs-src/`](docs-src/) and are built into the static site under `docs/` (GitHub Pages).\nBuild docs with:\n```bash\nmake docs\n```\n\nBuild release artifacts and Brew files with GoReleaser:\n```bash\nmake release\n```\n\nThis generates local release assets in `dist/`:\n* CLI archives: `darwin`, `linux`, `windows` (`amd64` + `arm64`)\n* macOS GUI app archives: `pgarachne-macos-amd64-app.zip`, `pgarachne-macos-arm64-app.zip`, `pgarachne-macos-universal-app.zip`\n\nIt also generates local Homebrew files for manual copy to your tap repository:\n* `dist/homebrew-tap/Formula/pgarachne.rb`\n* `dist/homebrew-tap/Casks/pgarachne-app.rb`\n\nGenerated documentation is available in the [`docs/`](docs/index.html) directory, including:\n\n*   **Configuration**: Full list of environment variables (`DB_HOST`, `JWT_SECRET`, etc.).\n*   **Security**: How role masquerading and API Tokens work.\n*   **Deployment**: Guides for Caddy, Nginx, and Ngrok.\n*   **Architectural Decisions**: Why JSON-RPC, SSE, Go, PostgreSQL functions, the URL structure, and MCP.\n*   **Error Codes**: Reference for JSON-RPC 2.0 errors.\n\n👉 [**Read the Full Documentation**](https://www.pgarachne.com/)\n\n## Support the Development\n\nIf PgArachne saves you time, please consider replacing your \"buy me a coffee\" budget with a support membership.\n\n*   ☕ [**Support on Buy Me a Coffee**](https://buymeacoffee.com/pgarachne)\n*   For Bank Transfer (USD/EUR/CZK) and Crypto details, please see the [Support section in the documentation](https://www.pgarachne.com/#support-donate).\n\n## License\n\n**The Code (MIT)**: Free for personal and commercial use. See [LICENSE](LICENSE).\n\n**The Brand**: The \"PgArachne\" name and logo are trademarks of **Zbyněk Vanžura**. Please remove branding if forking or selling a managed service.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fheptau%2Fpgarachne","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fheptau%2Fpgarachne","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fheptau%2Fpgarachne/lists"}