{"id":13461533,"url":"https://github.com/dosco/graphjin","last_synced_at":"2026-05-23T07:13:00.956Z","repository":{"id":37174411,"uuid":"177420214","full_name":"dosco/graphjin","owner":"dosco","description":"GraphJin - An Automagical Compiler to Connect AI to Your Databases","archived":false,"fork":false,"pushed_at":"2026-05-22T21:17:09.000Z","size":110669,"stargazers_count":3066,"open_issues_count":22,"forks_count":186,"subscribers_count":43,"default_branch":"master","last_synced_at":"2026-05-22T21:59:05.706Z","etag":null,"topics":["cloud-native","cockroachdb","database","graphql","mariadb","mongodb","mssql","mysql","oracle","postgresql","sql","sqlite","yugabyte"],"latest_commit_sha":null,"homepage":"https://graphjin.com","language":"Go","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/dosco.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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":"NOTICE","maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"dosco"}},"created_at":"2019-03-24T13:43:05.000Z","updated_at":"2026-05-22T21:17:03.000Z","dependencies_parsed_at":"2023-12-14T18:48:56.853Z","dependency_job_id":"e0ff9cb0-8681-45f0-8c81-6767c57c2a64","html_url":"https://github.com/dosco/graphjin","commit_stats":{"total_commits":929,"total_committers":57,"mean_commits":16.29824561403509,"dds":0.433799784714747,"last_synced_commit":"a81023af7947f15da32d050cd6f72c301a0eace6"},"previous_names":["dosco/super-graph"],"tags_count":1152,"template":false,"template_full_name":null,"purl":"pkg:github/dosco/graphjin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dosco%2Fgraphjin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dosco%2Fgraphjin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dosco%2Fgraphjin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dosco%2Fgraphjin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dosco","download_url":"https://codeload.github.com/dosco/graphjin/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dosco%2Fgraphjin/sbom","scorecard":{"id":352829,"data":{"date":"2025-08-11","repo":{"name":"github.com/dosco/graphjin","commit":"0968acde69cea216be443e202e22fb983df4a9e7"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.6,"checks":[{"name":"Maintained","score":5,"reason":"5 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 5","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":0,"reason":"dangerous workflow patterns detected","details":["Warn: script injection with untrusted input ' github.event.head_commit.message ': .github/workflows/auto-release.yml:37"],"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/auto-release.yml:1","Warn: no topLevel permission defined: .github/workflows/build.yml:1","Warn: no topLevel permission defined: .github/workflows/codeql-analysis.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":1,"reason":"Found 5/30 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Info: Found linked content: SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md:1","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Binary-Artifacts","score":9,"reason":"binaries present in source code","details":["Warn: binary detected: wasm/graphjin.wasm:1"],"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/build.yml:28"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Signed-Releases","score":8,"reason":"4 out of the last 4 releases have a total of 4 signed artifacts.","details":["Info: signed release artifact: checksums.txt.sig: https://github.com/dosco/graphjin/releases/tag/v3.0.31","Info: signed release artifact: checksums.txt.sig: https://github.com/dosco/graphjin/releases/tag/v3.0.30","Info: signed release artifact: checksums.txt.sig: https://github.com/dosco/graphjin/releases/tag/v3.0.29","Info: signed release artifact: checksums.txt.sig: https://github.com/dosco/graphjin/releases/tag/v3.0.27","Warn: release artifact v3.0.31 does not have provenance: https://api.github.com/repos/dosco/graphjin/releases/165277849","Warn: release artifact v3.0.30 does not have provenance: https://api.github.com/repos/dosco/graphjin/releases/164952530","Warn: release artifact v3.0.29 does not have provenance: https://api.github.com/repos/dosco/graphjin/releases/162242490","Warn: release artifact v3.0.27 does not have provenance: https://api.github.com/repos/dosco/graphjin/releases/156822323"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/auto-release.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/auto-release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/auto-release.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/auto-release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/auto-release.yml:91: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/auto-release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/auto-release.yml:135: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/auto-release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:33: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:47: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:67: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:74: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:89: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:96: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/codeql-analysis.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/dosco/graphjin/codeql-analysis.yml/master?enable=pin","Warn: containerImage not pinned by hash: .devcontainer/Dockerfile.dev:1: pin your Docker image by updating mcr.microsoft.com/vscode/devcontainers/go to mcr.microsoft.com/vscode/devcontainers/go@sha256:1202710896e06f795a709535742b184a807e696b786423e46141d932777e29cc","Warn: containerImage not pinned by hash: Dockerfile:2","Warn: containerImage not pinned by hash: Dockerfile:9","Warn: containerImage not pinned by hash: Dockerfile:22: pin your Docker image by updating alpine:latest to alpine:latest@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1","Warn: containerImage not pinned by hash: cmd/tmpl/Dockerfile:1: pin your Docker image by updating dosco/graphjin:latest to dosco/graphjin:latest@sha256:11f4bd3c102f408cfa68530790cd6df191e23052f082e10373979595f3b92c71","Warn: containerImage not pinned by hash: examples/webshop/Dockerfile:1: pin your Docker image by updating dosco/graphjin:latest to dosco/graphjin:latest@sha256:11f4bd3c102f408cfa68530790cd6df191e23052f082e10373979595f3b92c71","Warn: goCommand not pinned by hash: Dockerfile:10","Info:   0 out of  14 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned","Info:   0 out of   6 containerImage dependencies pinned","Info:   0 out of   1 goCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"SAST","score":7,"reason":"SAST tool detected but not run on all commits","details":["Info: SAST configuration detected: CodeQL","Warn: 0 commits out of 5 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"27 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-qwcr-r2fm-qrc7","Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x","Warn: Project is vulnerable to: GHSA-rv95-896h-c2vc","Warn: Project is vulnerable to: GHSA-qw6h-vgh9-j6wx","Warn: Project is vulnerable to: GHSA-mqr2-w7wj-jjgr","Warn: Project is vulnerable to: GHSA-49j4-86m8-q2jw","Warn: Project is vulnerable to: GHSA-fpw7-j2hg-69v5","Warn: Project is vulnerable to: GHSA-4rch-2fh8-94vw","Warn: Project is vulnerable to: GHSA-pmh2-wpjm-fj45","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-rhx6-c78j-4q9w","Warn: Project is vulnerable to: GHSA-m6fv-jmcg-4jfg","Warn: Project is vulnerable to: GHSA-cm22-4g7w-348p","Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-c7qv-q95q-8v27","Warn: Project is vulnerable to: GHSA-4www-5p9h-95mh","Warn: Project is vulnerable to: GHSA-9gqv-wp59-fq42","Warn: Project is vulnerable to: GHSA-mwcw-c2x4-8c55","Warn: Project is vulnerable to: GHSA-rp65-9cf3-cjxr","Warn: Project is vulnerable to: GHSA-76c9-3jph-rj3q","Warn: Project is vulnerable to: GHSA-7fh5-64p2-3v2j","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-4v9v-hfq4-rm2v","Warn: Project is vulnerable to: GHSA-9jgg-88mc-972h"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T08:45:37.281Z","repository_id":37174411,"created_at":"2025-08-18T08:45:37.281Z","updated_at":"2025-08-18T08:45:37.281Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33386211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T04:15:53.637Z","status":"ssl_error","status_checked_at":"2026-05-23T04:15:53.242Z","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":["cloud-native","cockroachdb","database","graphql","mariadb","mongodb","mssql","mysql","oracle","postgresql","sql","sqlite","yugabyte"],"created_at":"2024-07-31T11:00:42.542Z","updated_at":"2026-05-23T07:13:00.949Z","avatar_url":"https://github.com/dosco.png","language":"Go","funding_links":["https://github.com/sponsors/dosco"],"categories":["Go","Implementations","graphql"],"sub_categories":["Go"],"readme":"# GraphJin - A Compiler to Connect AI to Your Databases\n\n[![Apache 2.0](https://img.shields.io/github/license/dosco/graphjin.svg?style=for-the-badge)](https://github.com/dosco/graphjin/blob/master/LICENSE)\n[![NPM Package](https://img.shields.io/npm/v/graphjin?style=for-the-badge)](https://www.npmjs.com/package/graphjin)\n[![Docker Pulls](https://img.shields.io/docker/pulls/dosco/graphjin?style=for-the-badge)](https://hub.docker.com/r/dosco/graphjin/tags)\n[![Discord Chat](https://img.shields.io/discord/628796009539043348.svg?style=for-the-badge\u0026logo=discord)](https://discord.gg/6pSWCTZ)\n[![GoDoc](https://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge\u0026logo=go)](https://pkg.go.dev/github.com/dosco/graphjin/core/v3)\n[![GoReport](https://goreportcard.com/badge/github.com/gojp/goreportcard?style=for-the-badge)](https://goreportcard.com/report/github.com/dosco/graphjin/core/v3)\n\nPoint GraphJin at any database or source tree and AI assistants can query it instantly. Auto-discovers your schema, understands relationships, indexes code with tree-sitter, and compiles to optimized SQL. No configuration required.\n\nWorks with PostgreSQL, MySQL, MongoDB, SQLite, Oracle, MSSQL, Snowflake, S3/GCS/files, CodeSQL source indexes - and models from Claude/GPT-4 to local 7B models.\n\n## Installation\n\n**npm (all platforms)**\n```bash\nnpm install -g graphjin\n```\n\n**macOS (Homebrew)**\n```bash\nbrew install dosco/graphjin/graphjin\n```\n\n**Windows (Scoop)**\n```bash\nscoop bucket add graphjin https://github.com/dosco/graphjin-scoop\nscoop install graphjin\n```\n\n**Linux**\n\nDownload .deb/.rpm from [releases](https://github.com/dosco/graphjin/releases)\n\n**Docker**\n```bash\ndocker pull dosco/graphjin\n```\n\n## Try It Now\n\nThis is a quick way to try out GraphJin we'll use the `--demo` command which automatically\nstarts a database using docker and loads it with demo data.\n\nDownload the source which contains the `webshop` demo\n```\ngit clone https://github.com/dosco/graphjin\ncd graphjin\n```\n\nNow launch the Graphjin service that you installed using the install options above\n```bash\ngraphjin serve --demo --path examples/webshop\n```\n\nYou'll see output like this:\n```\nGraphJin started\n───────────────────────\n  Web UI:      http://localhost:8080/\n  GraphQL:     http://localhost:8080/api/v1/graphql\n  REST API:    http://localhost:8080/api/v1/rest/\n  Workflows:   http://localhost:8080/api/v1/workflows/\u003cname\u003e\n  MCP:         http://localhost:8080/api/v1/mcp\n\nClaude Desktop Configuration\n────────────────────────────\nAdd to claude_desktop_config.json:\n\n  {\n    \"mcpServers\": {\n      \"Webshop Development\": {\n        \"command\": \"/path/to/graphjin\",\n        \"args\": [\"mcp\", \"--server\", \"http://localhost:8080\"]\n      }\n    }\n  }\n```\n\nCopy the JSON config shown and add it to your Claude Desktop config file (see below for file location). You can also click `File \u003e Settings \u003e Developer` to get to it in Claude Desktop. You will also need to **Restart Claude Desktop**\n\n| OS | Possible config file locations |\n|----|---------------------|\n| macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |\n| Windows | `%APPDATA%\\Claude\\claude_desktop_config.json` |\n\n## Authenticate the CLI / MCP\n\nBefore `graphjin cli` or `graphjin mcp` can talk to a server, point them at one. There are no `--server` or `--token` flags — both come from a single saved config file (`~/.config/graphjin/client.json`, mode `0600`):\n\n```bash\ngraphjin cli setup http://localhost:8080            # local dev, no auth needed\ngraphjin cli setup https://graphjin.example.com     # signs in via the server's OIDC IdP\n```\n\nWhat `setup` does, depending on the server:\n\n- **No built-in login** (the server has `auth_login.enabled: false`): saves only the URL. CLI calls send no `Authorization` header.\n- **Built-in login enabled**: kicks off an [RFC 8628 device-code flow](https://www.rfc-editor.org/rfc/rfc8628). The CLI prints a verification URL + short code, opens your browser, you sign in with the configured identity provider (Google, Okta, Keycloak, Auth0-as-IdP, Azure AD — anything OIDC), and the server mints a 30-day JWT. Both URL and JWT are saved to `client.json`.\n\nAfter setup every `graphjin cli ...` command just works:\n\n```bash\ngraphjin cli health\ngraphjin cli query list\ngraphjin cli schema tables\ngraphjin cli setup show       # print the saved config (token redacted)\ngraphjin cli setup logout     # delete client.json\ngraphjin cli setup            # re-run sign-in against the same server (refresh token)\n```\n\nTo enable built-in login, set this on the server:\n\n```yaml\nauth:\n  jwt:\n    secret: \"long-random-shared-secret\"   # used to sign and verify local JWTs\n\nauth_login:\n  enabled: true\n  audience_graphjin: true                 # shorthand for audience: \"graphjin-cli\"\n  oidc:\n    issuer_url: \"https://accounts.google.com\"\n    client_id: \"...\"\n    client_secret: \"...\"                  # or $GJ_AUTH_LOGIN_OIDC_CLIENT_SECRET\n    allowed_domains: [\"example.com\"]      # optional allow-list\n```\n\nSuccessful authentication is recorded in structured logs with the verified `email` and `name` claims (when present), giving you a clean audit trail of who called every endpoint.\n\n### MCP install for OpenAI Codex + Claude Code\n\nGraphJin includes a guided installer that configures MCP for OpenAI Codex, Claude Code, or both. Run `graphjin mcp setup \u003cserver-url\u003e` first — `mcp install` reads the server URL from `client.json`, and the MCP-client config it writes is credential-free, so rotating tokens (re-running `mcp setup`) needs no edits to Claude / Codex.\n\n```bash\ngraphjin mcp setup https://graphjin.example.com\ngraphjin mcp install                                # guided: target client + scope\n```\n\n#### OpenAI Codex\n\n\u003cimg src=\"website/public/logos/openai-codex.svg\" alt=\"OpenAI Codex logo\" width=\"280\"\u003e\n\n```bash\ngraphjin mcp install --client codex --scope global --yes\n```\n\n#### Claude Code\n\n\u003cimg src=\"website/public/logos/claude-code.svg\" alt=\"Claude Code logo\" width=\"280\"\u003e\n\n```bash\ngraphjin mcp install --client claude --scope global --yes\n```\n\n#### Troubleshooting\n\n- `mcp install` requires a saved server URL — run `graphjin mcp setup \u003cserver-url\u003e` first if you see \"no GraphJin server configured\".\n- The generated MCP-client config is `args: [\"mcp\"]`. `graphjin mcp` reads server + token from `client.json` on its own, in proxy mode when a server is saved or local mode when it's not.\n- If Codex CLI does not support `codex mcp add --scope` (older versions), GraphJin automatically falls back to updating:\n  - global scope: `~/.codex/config.toml`\n  - local scope: `.codex/config.toml`\n\n## Getting started\n\nTo use GraphJin with your own databases you have to first create a new GraphJin app, then configure it using its config files and then launch GraphJin.\n\n**Step 1: Create New GraphJin App** \n```bash\ngraphjin new my-app\n```\n\n**Step 2: Start the GraphJin Service**\n```bash\ngraphjin serve --path ./my-app\n```\n\n**Step 3: Add to Claude Desktop config file**\n\nCopy paste the Claude Desktop Config provided by `graphjin serve` into the Claude Desktop MCP config file. How to do this has been defined clearly above in the `Try it Now` section.\n\n**Step 4: Restart Claude Desktop**\n\n**Step 5: Ask Claude questions like:**\n- \"What tables are in the database?\"\n- \"Show me all products under $50\"\n- \"List customers and their purchases\"\n- \"What's the total revenue by product?\"\n- \"Find products with 'wireless' in the name\"\n- \"Add a new product called 'USB-C Cable' for $19.99\"\n\n## How It Works\n\n1. **Connects to database** - Reads your schema automatically\n2. **Discovers relationships** - Foreign keys become navigable joins\n3. **Exposes metadata** - `gj_*` tables make discovered databases, tables, columns, relationships, functions, and indexes queryable when the GraphJin source is enabled\n4. **Indexes source code** - CodeSQL turns tree-sitter syntax trees and database references into a managed SQLite database\n5. **Exposes MCP tools** - Teach any LLM the query syntax\n6. **Runs JS workflows** - Chain multiple GraphJin MCP tools in one reusable workflow\n7. **Compiles to SQL** - Every request becomes a single optimized query\n\nNo resolvers. No ORM. No N+1 queries. Just point and query.\n\n## CodeSQL: Query Source Code Like a Database\n\nCodeSQL is a managed source kind for source trees. Configure a source folder and GraphJin creates a SQLite cache under `config/codesql/`, indexes it with tree-sitter, and updates it on restart. In development it also watches for changes while the service runs; in production live watching is disabled.\n\n```yaml\nsources:\n  - name: app\n    kind: sql\n    type: postgres\n    connection_string: postgres://app:secret@db/app\n    default: true\n\n  - name: code\n    kind: codesql\n    path: /srv/app\n    infer_db_refs: true\n\n  - name: graphjin\n    kind: graphjin\n    metadata: true\n\ntables:\n  - name: users\n    source: app\n\n  - name: gj_code\n    source: code\n    read_only: true\n```\n\nGraphJin exposes CodeSQL through one ordinary GraphQL root, `gj_code`. Use `kind` to select files, symbols, references, imports, database references, docs, parse errors, change sets, and locks:\n\n```graphql\nquery {\n  gj_code(where: { kind: { eq: \"symbol\" }, name: { iregex: \"handler|resolver\" } }, limit: 20) {\n    name\n    symbol_kind\n    language\n    start_row\n    path\n    hash\n  }\n}\n```\n\nWith a `kind: graphjin` source, GraphJin creates a read-only system graph named `graphjin` by default. Schema, catalog, entrypoint, capability, workflow, and system metadata are catalog items in `gj_catalog`; table and column metadata are selected by `kind`. When one CodeSQL source is active, GraphJin links catalog items to code references automatically:\n\n```graphql\nquery {\n  gj_catalog(where: { kind: { eq: \"column\" }, table_name: { eq: \"users\" }, column_name: { eq: \"email\" } }) {\n    database_name\n    table_name\n    column_name\n    gj_code {\n      kind\n      ref_kind\n      path\n      symbol_id\n    }\n  }\n}\n```\n\nThis is where the model gets genuinely powerful: the same agent can inspect production data systems and the code that operates them. It can ask, \"which handlers touch customer invoices?\", \"what tables do these workflows depend on?\", or \"show me the imports and call sites near this data path\" without switching tools or inventing a new backend.\n\n## What AI Can Do\n\n**Simple queries with filters:**\n```graphql\n{ products(where: { price: { gt: 50 } }, limit: 10) { id name price } }\n```\n\n**Nested relationships:**\n```graphql\n{\n  orders(limit: 5) {\n    id total\n    customer { name email }\n    items { quantity product { name category { name } } }\n  }\n}\n```\n\n**Aggregations:**\n```graphql\n{ products { count_id sum_price avg_price } }\n```\n\n**Analytics directives:**\n```graphql\n{\n  orders {\n    account_id\n    month\n    total\n    running_total: total @running(aggregate: sum, by: \"account_id\", orderBy: { month: asc })\n    moving_avg_total: total @moving(aggregate: avg, rows: 6, by: \"account_id\", orderBy: { month: asc })\n    previous_total: total @previous(by: \"account_id\", orderBy: { month: asc })\n    rank_by_total: total @rank(by: \"account_id\", order: desc)\n  }\n}\n```\nUse analytics directives when each original row should remain visible while adding report metrics such as running totals, moving averages, previous/next values, first/last values, and rank within a group. Ordinary one-row-per-group summaries still use `distinct` plus aggregate fields. Supported SQL databases validate analytics support at compile time; MongoDB and known-old database versions return clear errors.\n\n**Mutations:**\n```graphql\nmutation {\n  products(insert: { name: \"New Product\", price: 29.99 }) { id }\n}\n```\n\n**Spatial queries:**\n```graphql\n{\n  stores(where: { location: { st_dwithin: { point: [-122.4, 37.7], distance: 1000 } } }) {\n    name address\n  }\n}\n```\n\n## Real-time Subscriptions\n\nGet live updates when your data changes. GraphJin handles thousands of concurrent subscribers with a single database query - not one per subscriber.\n\n```graphql\nsubscription {\n  orders(where: { user_id: { eq: $user_id } }) {\n    id total status\n    items { product { name } }\n  }\n}\n```\n\n**Why it's efficient:**\n- Traditional approach: 1,000 subscribers = 1,000 database queries\n- GraphJin: 1,000 subscribers = 1 optimized batch query\n- Automatic change detection - updates only sent when data actually changes\n- Built-in cursor pagination for feeds and infinite scroll\n\nSubscribe over **WebSockets** (`graphql-ws` / `graphql-transport-ws` subprotocols) or **Server-Sent Events** — set `Accept: text/event-stream` on a `POST /api/v1/graphql` request and GraphJin streams `event: next` frames for each result, terminated by `event: complete`. Works from Node.js, Go, or any browser `EventSource` / WebSocket client.\n\n## Filesystem Tables (Local, S3, GCS)\n\nObject stores show up as ordinary tables in your GraphQL schema. Declare them in config and they get the same query surface as a database table — no per-storage GraphQL plumbing on your side.\n\n```yaml\nsources:\n  - name: avatars\n    kind: filesystem\n    backend: s3\n    bucket: my-bucket\n    prefix: avatars/\n    region: us-east-1\n    presign_ttl: 15m\n\n  - name: invoices\n    kind: filesystem\n    backend: gcs\n    bucket: invoices\n    prefix: 2026/\n\n  - name: uploads_local\n    kind: filesystem\n    backend: local\n    root: /var/lib/graphjin/uploads\n\ntables:\n  - name: avatars\n    source: avatars\n    read_only: true\n\n  - name: invoices\n    source: invoices\n    read_only: true\n\n  - name: uploads_local\n    source: uploads_local\n```\n\nEvery filesystem table exposes the same columns regardless of backend:\n\n```graphql\n{ avatars(\n    where: { key: { like: \"users/%\" } }\n    order_by: { key: asc }\n    limit: 50\n  ) {\n    key size content_type modified_at url\n  }\n}\n\n{ avatars(id: \"users/42.png\") {\n    key size url data    # data is base64 because the field was selected\n  }\n}\n```\n\nThe legacy `prefix`, `key`, and `inline_data` arguments remain accepted, but new callers should use the normal GraphJin read surface: `id`, `where`, `order_by`, `limit`, `offset`, `first`, `last`, `after`, and `before`.\nFor cursor pagination, request the standard root cursor field, e.g. `avatars_cursor`, and pass it back through `after: $cursor`.\n\n`url` is a presigned GET URL by default (15 min, configurable per table). Auth follows the standard credential chain: AWS env / `~/.aws` / IRSA / EC2 IMDS for S3, Application Default Credentials for GCS — never embedded in GraphJin config.\n\nSlim builds drop SDK weight: `-tags no_s3` or `-tags no_gcs` excludes either backend. Custom backends register through `core.OptionSetFilesystemBackend(name, factory)` — same SDK GraphJin uses for the built-ins.\n\n## File Uploads\n\nThe GraphQL endpoint accepts multipart bodies per the [graphql-multipart-request-spec](https://github.com/jaydenseric/graphql-multipart-request-spec). Files can be inlined as base64 (default) or streamed straight to a filesystem table:\n\n```yaml\nuploads:\n  enabled: true\n  storage: avatars               # name of a filesystems[] entry; omit to inline as base64\n  storage_key_prefix: \"{date}/\"  # {date} → YYYY/MM/DD\n  max_size: 25_000_000\n  allowed_mime: [\"image/*\", \"application/pdf\"]\n```\n\nWhen `storage` is set, the file body is written to the backend and the GraphQL variable becomes a stable reference — mutations persist this directly into a JSONB column:\n\n```json\n{ \"key\": \"2026/05/08/abc123.png\",\n  \"url\":  \"https://s3.../...?presigned\",\n  \"size\": 12345,\n  \"content_type\": \"image/png\" }\n```\n\nWhen `storage` is empty the variable carries the bytes inline as `{filename, content_type, size, data}` (base64) — useful for small uploads going straight into `bytea`.\n\n## Apollo Federation v2\n\nGraphJin can register as a federation subgraph so it composes with other services behind Apollo Router / Cosmo / Hive Gateway:\n\n```yaml\nfederation:\n  enabled: true\n  version: \"v2.5\"\n  keys:\n    users: [\"id\"]                  # auto-derived from PKs by default\n    orders: [\"id\", \"tenant_id\"]    # composite keys via override\n  shareable: [\"Tag.name\"]          # field-level @shareable\n  inaccessible: [\"Users.encrypted_password\"]\n```\n\n`_service { sdl }` returns a federation-flavoured SDL with `@link`, `@key`, `@shareable`, `@inaccessible`, `@tag`, `_Service`, and `_Entity`. Composition succeeds out of the box; `_entities` resolution is on the roadmap (the engine returns a clear error today, so gateways see the gap rather than silent failures).\n\n## HTTP API Routes\n\n`graphjin serve` exposes everything under a single host/port. All routes go through the configured auth handler unless noted.\n\n| Route | Methods | Purpose |\n|---|---|---|\n| `/api/v1/graphql` | `GET`, `POST` | GraphQL queries and mutations. Subscriptions if the request is a WebSocket upgrade or carries `Accept: text/event-stream` (SSE). |\n| `/api/v1/rest/\u003cname\u003e` | `GET`, `POST` | Run a saved/persisted query by name. Variables go in `?variables=…` (GET) or the JSON body (POST). |\n| `/api/v1/workflows/\u003cname\u003e` | `GET`, `POST` | Legacy workflow execution endpoint. In source mode it is registered only when `mcp.legacy_discovery: true`; use `gj_workflow_execution(insert)` through GraphQL otherwise. |\n| `/api/v1/openapi.json` | `GET` | OpenAPI 3 spec generated from your saved REST queries. |\n| `/api/v1/mcp` | `POST` | MCP (Model Context Protocol) HTTP transport — Streamable HTTP, stateless. |\n| `/api/v1/mcp/message` | `POST` | MCP HTTP transport for stateless message integrations. |\n| `/api/v1/discovery` | `GET` | Legacy discovery document. In source mode it is registered only when `mcp.legacy_discovery: true`; use catalog GraphQL roots otherwise. |\n| `/api/v1/discovery/\u003csection\u003e` | `GET` | Legacy discovery drill-down (e.g. `tables`, `insights`), gated the same way as `/api/v1/discovery`. |\n| `/api/v1/admin/tables` | `GET` | Admin: list known tables (Web UI). |\n| `/api/v1/admin/tables/\u003cname\u003e` | `GET` | Admin: schema for a single table. |\n| `/api/v1/admin/queries` | `GET` | Admin: list saved queries. |\n| `/api/v1/admin/queries/\u003cname\u003e` | `GET` | Admin: details for a saved query. |\n| `/api/v1/admin/fragments` | `GET` | Admin: list GraphQL fragments. |\n| `/api/v1/admin/config` | `GET` | Admin: effective runtime config. |\n| `/api/v1/admin/database` / `/api/v1/admin/databases` | `GET` | Admin: connected database info. |\n| `/api/v1/auth/device` | `POST` | OIDC device-flow start (only if `auth_login.enabled`). |\n| `/api/v1/auth/device/token` | `POST` | OIDC device-flow poll. |\n| `/api/v1/auth/login` | `GET` | OIDC login redirect. |\n| `/api/v1/auth/callback` | `GET` | OIDC callback. |\n| `/health` | `GET` | Liveness probe. **No auth.** |\n| `/` | `GET` | Built-in Web UI (only when `webui: true`). |\n\n**Mode flags that change which routes are live:**\n- `mcp.disable: true` — removes `/api/v1/mcp` and `/api/v1/mcp/message`.\n- `mcp.only: true` — keeps only `/health` and `/api/v1/mcp*`. Legacy `/api/v1/workflows/*` and `/api/v1/discovery*` remain only when `mcp.legacy_discovery: true`.\n- Source mode (`sources:` present) disables legacy `/api/v1/workflows/*` and `/api/v1/discovery*` unless `mcp.legacy_discovery: true`.\n- `webui: false` — drops `/` and the `/api/v1/admin/*` routes.\n\n## MCP Tools\n\nGraphJin exposes a catalog-first agent surface that guides AI models to discover before acting. Start with `query_catalog`, then inspect evidence with `get_catalog_card` before writing queries, choosing relationships, or using GraphJin-specific syntax. For actions, agents can use GraphJin control-plane GraphQL roots such as `gj_workflow_execution(insert)`, `gj_workflow(insert/update/delete)`, and `gj_config(id: \"current\", update: ...)`. Schema reloads, schema changes, where-clause validation, and query repair remain MCP action tools. The legacy discovery tools are migration shims and are disabled unless `mcp.legacy_discovery: true`.\n\nFor teams building MCP agents, internal copilots, workflow agents, or enterprise automation, see [AGENTIC.md](AGENTIC.md). It explains the catalog-first agent loop in detail: discover, inspect, validate, act, observe, and refine.\n\nKey discovery tools:\n- `get_catalog_entrypoints` to choose a discovery path when the task is broad\n- `query_catalog` to search schema, relationship, workflow, language, config, policy, capability, and query-pattern items. Use `search` for ranked text discovery and `where` for exact filters.\n- `get_catalog_card` to inspect evidence, examples, details, safety notes, and graph edges\n- `validate_where_clause` to validate filters before execution\n\nFor JS orchestration, use:\n- `query_catalog` with `where: { kind: { eq: \"workflow\" } }` to discover reusable workflows\n- `get_js_runtime_api` to discover exactly which globals/functions are available inside workflow scripts\n- `gj_workflow_execution(insert: { workflow_name: \"...\", variables: {...} })` to run `./workflows/\u003cname\u003e.js` through GraphQL. This is mutation-only and returns an ephemeral result row; it does not store run history. Mark the workflows source or `gj_workflow_execution` table `read_only` to block it. The `execute_workflow` MCP compatibility tool is available only when `mcp.legacy_discovery: true` and `mcp.allow_workflow_execution: true`.\n\nPrompts like `write_query` and `fix_query_error` help models construct and debug queries.\n\n## JS Workflows (GraphQL + REST)\n\nWorkflows let an LLM run multi-step logic in JavaScript while still using GraphJin MCP tools for DB-aware operations.\n\nCreate a file in `./workflows`, for example `./workflows/customer_insights.js`:\n\n```js\nfunction main(input) {\n  const tables = gj.tools.queryCatalog({\n    where: { kind: { eq: \"table\" } }\n  }).cards;\n  const top = gj.tools.executeSavedQuery({\n    name: \"top_customers\",\n    variables: { limit: input.limit || 5 }\n  });\n  return { tables, top };\n}\n```\n\n### Run via GraphQL\n\n```graphql\nmutation {\n  gj_workflow_execution(insert: {\n    workflow_name: \"customer_insights\"\n    variables: { limit: 5 }\n  }) {\n    status\n    result_json\n    error\n  }\n}\n```\n\nLegacy MCP clients can call `execute_workflow` only when `mcp.legacy_discovery: true`.\n\n### Run via REST\n\nThis legacy HTTP endpoint is available in legacy database mode, or in source mode only when `mcp.legacy_discovery: true`.\n\n- `POST /api/v1/workflows/\u003cname\u003e` with JSON body\n- `GET /api/v1/workflows/\u003cname\u003e?variables={...json...}`\n\nBoth map variables to the same workflow input object.\n\n## Chat Walkthroughs\n\nReal conversations showing how an AI assistant uses GraphJin MCP tools to help you go from zero to running queries.\n\n### \"I have databases running but don't know the details\"\n\n\u003e **You:** I have some databases running locally but I'm not sure about the ports or connection details. Can you help me set up GraphJin?\n\n\u003e **Claude:** Let me scan your system to find running databases.\n\u003e\n\u003e *calls `discover_databases`*\n\u003e\n\u003e I found 2 databases on your system:\n\u003e - **PostgreSQL** on localhost:5432 (TCP) — listening, auth: ok (user: postgres), databases: `myapp`, `myapp_test`\n\u003e - **MySQL** on localhost:3306 (TCP) — listening, auth: auth_failed — default credentials failed, provide username and password\n\u003e\n\u003e The PostgreSQL instance has databases `myapp` and `myapp_test`. Which one would you like to configure GraphJin with?\n\n\u003e **You:** Let's go with the \"myapp\" database on Postgres.\n\n\u003e **Claude:** I'll configure GraphJin to connect to your PostgreSQL database.\n\u003e\n\u003e *calls `update_current_config` with databases: {\"main\": {type: \"postgres\", host: \"localhost\", port: 5432, user: \"postgres\", dbname: \"myapp\"}}*\n\u003e\n\u003e Connected successfully! Let me discover the catalog surface.\n\u003e\n\u003e *calls `query_catalog` with `where: {kind: {eq: \"table\"}}`*\n\u003e\n\u003e Your database has these tables: `users`, `orders`, `products`, `reviews`. Want me to write a query?\n\n\u003e **You:** Show me users with their orders.\n\n\u003e **Claude:** *calls `query_catalog` with `search: \"join users orders\", where: {kind: {eq: \"relationship\"}}`, then `get_catalog_card`, then `execute_graphql`*\n\u003e ```graphql\n\u003e { users { id name email orders { id total status } } }\n\u003e ```\n\u003e Here are the results — 24 users returned, each with their associated orders.\n\n### \"I have a Docker Compose project with MySQL\"\n\n\u003e **You:** I'm running a Docker Compose stack with MySQL. Help me connect GraphJin to it.\n\n\u003e **Claude:** Let me check what database containers you have running.\n\u003e\n\u003e *calls `discover_databases`*\n\u003e\n\u003e Found a MySQL Docker container:\n\u003e - **MySQL** via Docker — container `myapp-db-1`, image `mysql:8.0`, port mapped to localhost:13306\n\u003e\n\u003e I'll configure GraphJin with the discovered connection details.\n\u003e\n\u003e *calls `update_current_config` with databases: {\"main\": {type: \"mysql\", host: \"localhost\", port: 13306, user: \"root\", dbname: \"\"}}*\n\u003e\n\u003e What's the database name and password for your MySQL instance?\n\n\u003e **You:** Database is \"shop_db\", password is \"secret123\".\n\n\u003e **Claude:** *calls `update_current_config` with the full credentials*\n\u003e\n\u003e Connected! Let me explore the catalog.\n\u003e\n\u003e *calls `query_catalog` with `where: {kind: {eq: \"table\"}}`*\n\u003e\n\u003e Tables found: `customers`, `products`, `orders`, `order_items`, `categories`. Let me check how they relate.\n\u003e\n\u003e *calls `query_catalog` with `search: \"join orders customers order_items products\", where: {kind: {eq: \"relationship\"}}`, then `get_catalog_card` for the relevant relationship items*\n\u003e\n\u003e `orders` connects to `customers` (via customer_id) and `order_items` (via order_id), and `order_items` links to `products`. Here's a nested query:\n\u003e\n\u003e ```graphql\n\u003e { orders { id total customer { name email } order_items { quantity product { name price } } } }\n\u003e ```\n\n### \"I want to query my SQLite app database\"\n\n\u003e **You:** I have a SQLite database file in my project. Can GraphJin work with it?\n\n\u003e **Claude:** Absolutely! Let me scan your project directory for SQLite files.\n\u003e\n\u003e *calls `discover_databases` with scan_dir: \".\"*\n\u003e\n\u003e Found 1 SQLite database:\n\u003e - **data/app.sqlite3** — file found\n\u003e\n\u003e I'll configure GraphJin to use it.\n\u003e\n\u003e *calls `update_current_config` with databases: {\"main\": {type: \"sqlite\", path: \"data/app.sqlite3\"}}*\n\u003e\n\u003e Connected! Let me see your tables.\n\u003e\n\u003e *calls `query_catalog` with `where: {kind: {eq: \"table\"}}`*\n\u003e\n\u003e Tables: `tasks`, `projects`, `tags`, `task_tags`. Want to query something?\n\n\u003e **You:** Show me all tasks with their project name, sorted by due date.\n\n\u003e **Claude:** *calls `query_catalog` with `search: \"join tasks projects\", where: {kind: {eq: \"relationship\"}}`, then `get_catalog_card`, then `execute_graphql`*\n\u003e ```graphql\n\u003e { tasks(order_by: {due_date: asc}) { id title due_date completed project { name } } }\n\u003e ```\n\u003e Here are your 12 tasks sorted by due date, each showing the parent project name.\n\n## Database Support\n\n| Database | Queries | Mutations | Subscriptions | Full-Text | GIS |\n|----------|---------|-----------|---------------|-----------|-----|\n| PostgreSQL | Yes | Yes | Yes | Yes | PostGIS |\n| MySQL | Yes | Yes | Yes | Yes | 8.0+ |\n| MariaDB | Yes | Yes | Yes | Yes | Yes |\n| MSSQL | Yes | Yes | Yes | No | Yes |\n| Oracle | Yes | Yes | Yes | No | Yes |\n| SQLite | Yes | Yes | Yes | FTS5 | SpatiaLite |\n| MongoDB | Yes | Yes | Yes | Yes | Yes |\n| Snowflake | Yes | Yes | No | No | No |\n| CockroachDB | Yes | Yes | Yes | Yes | No |\n\nAlso works with AWS Aurora/RDS, Google Cloud SQL, and YugabyteDB. Snowflake supports key pair (JWT) authentication.\n\n## Production Security\n\n**Query allow-lists** - In production, only saved queries can run. AI models call `execute_saved_query` with pre-approved queries. No arbitrary SQL injection possible.\n\n**Role-based access** - Different roles see different data:\n```yaml\nroles:\n  user:\n    tables:\n      - name: orders\n        query:\n          filters: [\"{ user_id: { eq: $user_id } }\"]\n```\n\n**JWT authentication** - Supports Auth0, Firebase, JWKS endpoints.\n\n**Response caching** - Redis with in-memory fallback. Automatic cache invalidation on mutations. **Stale-while-revalidate** support: serve cached responses immediately while a background worker refreshes the entry — concurrent refreshes for the same key are deduplicated via singleflight, and the worker pool is bounded so a thundering herd can't spawn unbounded goroutines.\n\n```yaml\ncaching:\n  ttl: 3600          # hard expiry in seconds\n  fresh_ttl: 300     # soft expiry — entries past this trigger SWR refresh\n```\n\n## Also a GraphQL API\n\nGraphJin works as a traditional API too - use it from Go or as a standalone service.\n\n### Go\n```bash\ngo get github.com/dosco/graphjin/core/v3\n```\n```go\ndb, _ := sql.Open(\"pgx\", \"postgres://localhost/myapp\")\ngj, _ := core.NewGraphJin(nil, db)\nres, _ := gj.GraphQL(ctx, `{ users { id email } }`, nil, nil)\n```\n\n### Standalone Service\n```bash\nbrew install dosco/graphjin/graphjin  # Mac\ngraphjin new myapp \u0026\u0026 cd myapp\ngraphjin serve\n```\n\nBuilt-in web UI at `http://localhost:8080` for query development.\n\n## Documentation\n\n\n\n- [Configuration Reference](CONFIG.md)\n- [Feature Reference](docs/FEATURES.md)\n- [Go Examples](https://pkg.go.dev/github.com/dosco/graphjin/core#pkg-examples)\n\n## Get in Touch\n\n[Twitter @dosco](https://twitter.com/dosco) | [Discord](https://discord.gg/6pSWCTZ)\n\n## License\n\n[Apache Public License 2.0](https://opensource.org/licenses/Apache-2.0)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdosco%2Fgraphjin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdosco%2Fgraphjin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdosco%2Fgraphjin/lists"}