{"id":51325614,"url":"https://github.com/andreabozzo/gitnodes","last_synced_at":"2026-07-01T18:01:28.993Z","repository":{"id":368271445,"uuid":"1278235590","full_name":"AndreaBozzo/gitnodes","owner":"AndreaBozzo","description":"Turn a GitHub repo of markdown notes into a live, editable knowledge graph. Git is the source of truth; GitNodes visualizes and edits it in-app, with read-only MCP access for AI agents. Built in Rust + Leptos.","archived":false,"fork":false,"pushed_at":"2026-06-29T20:43:14.000Z","size":17199,"stargazers_count":1,"open_issues_count":11,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-29T21:23:33.014Z","etag":null,"topics":["agents","digital-garden","github","graph-visualization","knowledge-base","knowledge-graph","leptos","leptos-rs","llm","markdown","mcp","model-context-protocol","note-taking","rust","second-brain","self-hosted","sqlite","webassembly"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AndreaBozzo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":"SUPPORT.md","governance":null,"roadmap":"docs/ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"AndreaBozzo"}},"created_at":"2026-06-23T15:41:06.000Z","updated_at":"2026-06-29T20:43:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/AndreaBozzo/gitnodes","commit_stats":null,"previous_names":["andreabozzo/gitnodes"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/AndreaBozzo/gitnodes","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndreaBozzo%2Fgitnodes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndreaBozzo%2Fgitnodes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndreaBozzo%2Fgitnodes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndreaBozzo%2Fgitnodes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AndreaBozzo","download_url":"https://codeload.github.com/AndreaBozzo/gitnodes/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndreaBozzo%2Fgitnodes/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35017091,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["agents","digital-garden","github","graph-visualization","knowledge-base","knowledge-graph","leptos","leptos-rs","llm","markdown","mcp","model-context-protocol","note-taking","rust","second-brain","self-hosted","sqlite","webassembly"],"created_at":"2026-07-01T18:00:47.924Z","updated_at":"2026-07-01T18:01:28.986Z","avatar_url":"https://github.com/AndreaBozzo.png","language":"Rust","funding_links":["https://github.com/sponsors/AndreaBozzo"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"public/brand/gitnodes-wordmark-dark.png\"\u003e\n    \u003cimg alt=\"GitNodes\" src=\"public/brand/gitnodes-wordmark-light.png\" width=\"460\"\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eTurn a GitHub repo of markdown notes into a knowledge graph you — and your AI agents — can explore, search, and edit.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/AndreaBozzo/gitnodes/actions/workflows/ci.yml\"\u003e\u003cimg alt=\"CI\" src=\"https://img.shields.io/github/actions/workflow/status/AndreaBozzo/gitnodes/ci.yml?branch=main\u0026label=CI\u0026style=flat-square\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/AndreaBozzo/gitnodes/releases/latest\"\u003e\u003cimg alt=\"Latest release\" src=\"https://img.shields.io/github/v/release/AndreaBozzo/gitnodes?style=flat-square\u0026color=blue\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg alt=\"License: AGPL-3.0 / Apache-2.0\" src=\"https://img.shields.io/badge/license-AGPL--3.0%20%2F%20Apache--2.0-blue?style=flat-square\"\u003e\u003c/a\u003e\n  \u003cimg alt=\"Rust\" src=\"https://img.shields.io/badge/rust-stable-CE412B?style=flat-square\u0026logo=rust\u0026logoColor=white\"\u003e\n  \u003cimg alt=\"Built with Leptos\" src=\"https://img.shields.io/badge/built%20with-Leptos%200.8-EF3939?style=flat-square\"\u003e\n\u003c/p\u003e\n\nGitNodes points at a GitHub repository of markdown files and makes it navigable: a\nforce-directed graph of how your notes link together, a wiki-style reader, and an\nin-app editor that commits changes straight back to GitHub. Declare your own note\ntypes and relationships in a single `.gitnodes.yml` file and GitNodes builds the\ngraph, the typed links, and a full-text search index for you.\n\nYour notes stay in Git — there is no separate database to migrate into and nothing\nto lock you in. Point a coding agent (Claude Code, Cursor, Codex, …) at the same\nrepository over MCP and it can search and walk the graph instead of grepping blind,\nthen write changes back as ordinary, reviewable commits.\n\n- **Browse** — explore the graph of your notes and their links, with ranked full-text search.\n- **Edit** — create, link, and rename notes in-app; each change lands as a direct commit or a pull request, following your GitHub permissions.\n- **Agent-native** — a read-only MCP server lets agents traverse your knowledge graph; they author back through Git, never behind your back.\n- **Git-native** — Git is the single source of truth. The local index is just a rebuildable projection: delete it and it rebuilds from `git clone` alone.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"GitNodes rendering a knowledge base as a force-directed graph\" src=\"public/screenshots/graph.png\" width=\"860\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://gitnodes-demo-production.up.railway.app\" rel=\"external\"\u003e▶ Try the live demo\u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cem\u003eThe brain above ships in \u003ca href=\"examples/demo-brain\"\u003e\u003ccode\u003eexamples/demo-brain\u003c/code\u003e\u003c/a\u003e — run \u003ccode\u003egitnodes preview examples/demo-brain\u003c/code\u003e to explore it yourself.\u003c/em\u003e\n\u003c/p\u003e\n\n## Early presentation\n\nGitNodes was presented publicly under its former working name, **Brain UI**, at\nTalent Garden Cosenza on 3 June 2026. The event recording is available on\n[LinkedIn](https://www.linkedin.com/events/7467916975424606209/).\n\n## Quickstart\n\n\u003e **Pre-release:** the install commands below go live when **GitNodes 0.1.0**\n\u003e ships on **1 July 2026**. Until then, [build from source](#build-from-source).\n\n### Install\n\n**macOS / Linux** — download, review, run:\n\n```bash\ncurl -fSLo install-gitnodes.sh https://raw.githubusercontent.com/AndreaBozzo/gitnodes/main/scripts/install.sh\nless install-gitnodes.sh        # review before running\nsh install-gitnodes.sh\n```\n\n**Windows (PowerShell)** — download, review, run:\n\n```powershell\nInvoke-WebRequest https://raw.githubusercontent.com/AndreaBozzo/gitnodes/main/scripts/install.ps1 -OutFile install-gitnodes.ps1\nGet-Content .\\install-gitnodes.ps1   # review before running\n\u0026 .\\install-gitnodes.ps1\n```\n\nBoth drop a single `gitnodes` binary on your `PATH` — no Rust toolchain, no\ncompiling. Prefer to fetch it yourself? Grab an archive from\n[Releases](https://github.com/AndreaBozzo/gitnodes/releases/latest) and put\n`gitnodes` (or `gitnodes.exe`) on `PATH`.\n\n\u003e Homebrew and WinGet packages follow shortly after the 0.1.0 release.\n\n### First run\n\n```bash\ngitnodes init my-brain      # starter notes + .gitnodes.yml + AGENTS.md\ncd my-brain\ngitnodes preview            # opens the read-only graph; no GitHub or login\n```\n\nThe same working tree is immediately available to coding agents. Configure the\nagent to launch this stdio command rather than running it manually:\n\n```bash\ngitnodes mcp .              # read-only stdio MCP server\n```\n\nWhen you want collaborative editing and pull-request workflows, publish it:\n\n```bash\ngit add . \u0026\u0026 git commit -m \"Initialize GitNodes knowledge base\"\ngh repo create my-brain --private --source=. --remote=origin --push\ngitnodes doctor             # validates notes, Git state, remote, and gh auth\ngitnodes serve              # discovers the repo, reuses `gh auth`\n```\n\nIf needed, run `gh auth login` once before the commands above. GitNodes reads the\nrepository and branch from the local Git checkout and uses the credential already\nstored by GitHub CLI; it does not copy that token into `.env` or another file.\n`GITHUB_PAT` remains available as an explicit single-user fallback.\n`gitnodes preview` keeps its SQLite projection and sessions in memory and never\nwrites runtime state into the knowledge directory. The scaffolded `AGENTS.md`\nteaches coding agents (Claude Code, Codex, Cursor, …)\nthe conventions of your brain so they can add and link notes correctly. GitNodes\nis built for humans and agents alike.\n\n\u003e This per-brain `AGENTS.md` is generated from your brain's `.gitnodes.yml` and\n\u003e describes *that knowledge base's* taxonomy. It is distinct from the `AGENTS.md`\n\u003e at the root of this repository, which guides contributors working on GitNodes\n\u003e itself.\n\nThe source boundary matters: `preview` and MCP read the local working tree,\nincluding uncommitted files; `serve` and deployments read the pushed GitHub\nbranch. See the [end-to-end getting-started\nguide](docs/guides/GETTING_STARTED.md) before switching modes.\n\n## Build from source\n\nPrefer to compile it yourself, or on a platform without a prebuilt binary?\n\n```bash\nrustup target add wasm32-unknown-unknown\ncargo install cargo-leptos --locked --version 0.3.6\nnpm ci\nnpm run build:css\ncargo leptos build --release\ncargo build --release -p gitnodes-app --bin gitnodes-app \\\n  --no-default-features --features embed-assets\n```\n\nPut `target/release/gitnodes-app` (or `.exe`) on `PATH` as `gitnodes`.\n\n## Documentation\n\n- [Getting started: local preview to GitHub-backed use](docs/guides/GETTING_STARTED.md)\n- [Configuration reference](docs/guides/CONFIGURATION.md)\n- [Deployment guide](docs/guides/DEPLOYMENT.md)\n- [Complete feature inventory and limitations](docs/FEATURES.md)\n- [Operator notes and recovery](docs/OPERATOR_NOTES.md)\n- [Roadmap](docs/ROADMAP.md)\n\n## AI agent access\n\nGitNodes includes a read-only local MCP server (`gitnodes mcp [dir]`, stdio).\nIt re-indexes the working tree before each request through the same SQLite\nprojection and FTS5 search path as the web UI, so uncommitted notes are visible\nimmediately. No PAT, GitHub login, push, or running web server is required.\n\nIt exposes five tools:\n\n- **`search_brain`** — full-text search, ranked like the UI (type/tag/path filters).\n- **`list_nodes`** — enumerate notes, filtered by type, tag, or directory.\n- **`read_node`** — one note's projected metadata plus its markdown body.\n- **`node_links`** — walk a note's incoming and outgoing graph edges (body links,\n  frontmatter links, shared tags) so the agent traverses the graph instead of grepping.\n- **`validate_brain`** — report malformed frontmatter, taxonomy mismatches,\n  invalid tags, and unresolved links without changing the working tree.\n\n### Wiring it into your agent\n\nThe launch command is identical for every client — `gitnodes mcp \u003cpath-to-your-brain\u003e`.\nOnly *where* the config lives differs, and that drifts between releases, so use the\none-line CLI commands where they exist and otherwise drop in the standard JSON. Use the\nabsolute path to your brain checkout in every example below.\n\n**CLI agents** — one command each:\n\n```bash\n# Claude Code (add --scope project to write a committable .mcp.json in the repo)\nclaude mcp add gitnodes -- gitnodes mcp /absolute/path/to/my-brain\n\n# Codex CLI (or hand-edit ~/.codex/config.toml under [mcp_servers.gitnodes])\ncodex mcp add gitnodes -- gitnodes mcp /absolute/path/to/my-brain\n```\n\n**JSON-config editors** — Cursor (`.cursor/mcp.json`), Antigravity\n(`~/.gemini/config/mcp_config.json`, or the IDE's *Manage MCP Servers → View raw config*),\nCline, Windsurf, Claude Desktop, Continue. Add the standard `mcpServers` entry; see each\nclient's MCP docs for the exact file:\n\n```json\n{\n  \"mcpServers\": {\n    \"gitnodes\": {\n      \"command\": \"gitnodes\",\n      \"args\": [\"mcp\", \"/absolute/path/to/my-brain\"]\n    }\n  }\n}\n```\n\n### 60-second test\n\nOnce the server is configured, ask your agent something that forces a graph hop,\nfor example:\n\n\u003e Use the gitnodes tools to find notes about *knowledge graphs*, then show me\n\u003e what the top result links to and summarise it.\n\nA working setup will call `search_brain`, then `node_links` on the top hit's\npath, then `read_node` to pull the full note — discovering structure you never\nhad to describe.\n\n### Letting an agent maintain the brain\n\nThe MCP server is read-only **by design**: agents discover through it, but they\nwrite through Git, which stays the single source of truth. The authoring loop:\n\n1. The agent edits markdown files directly in the checkout. The scaffolded\n   `AGENTS.md` (generated from `.gitnodes.yml`) teaches it the node types,\n   frontmatter rules, and link conventions, so its edits land on-taxonomy.\n2. Commit and push, or open a pull request — every change is an ordinary,\n   reviewable commit.\n3. `gitnodes serve` (or the deployed app) rebuilds the projection from Git on the\n   next sync; the new notes appear in the graph and in the agent's tools.\n\nBecause Git is the interface, no special write API is needed and nothing edits\nyour knowledge base behind your back.\n\n## Stack\n\n- **Rust / Leptos 0.8** — SSR + WASM hydration (`cargo leptos`)\n- **Axum 0.8** — HTTP server, session middleware, auth routes\n- **tower-sessions 0.14** + `tower-sessions-sqlx-store 0.15` — persistent sessions on SQLite\n- **reqwest 0.12** — GitHub REST API client (no octocrab)\n- **pulldown-cmark** — markdown → HTML, shared between SSR and client (live editor preview)\n- **Tailwind CSS 3 + `@tailwindcss/typography`** — styling, built via Node toolchain\n- **SQLite** — sessions, audit log, and target-scoped projection (`nodes`, `edges`, `files`, `backlinks`, `work_items`, `work_item_bindings`); content source of truth still lives in GitHub\n\n## Workspace layout\n\n```\ncrates/\n  gitnodes-domain/    # Pure domain types: BrainConfig, NodeTypeSpec, WorkItem, GithubClient\n  gitnodes-graph/     # Graph building + force-directed layout (no I/O)\n  gitnodes-storage/   # GitHub API calls: tree walk, file CRUD, asset upload, atomic Git Data commits\n  gitnodes-auth/      # GitHub OAuth token exchange + optional org membership check\n  gitnodes-app/       # Leptos app + Axum entrypoint (SSR binary + WASM bundle)\n    src/\n      main.rs                   # Axum entrypoint, session store, auth routes\n      api.rs                    # Server functions: graph/file/work-item reads, writes, rebuilds\n      mcp.rs                    # Read-only local agent tools over stdio MCP\n      markdown.rs               # pulldown-cmark wrapper + frontmatter splitter\n      server/assets.rs          # Authenticated proxy for private-repo images\n      server/projection/        # SQLite projection materialization + read model\n      server/health.rs          # /healthz and /readyz operational probes\n      server/pending_sync_job.rs # Background retry loop for provider sync outbox\n      server/webhook.rs         # GitHub webhook entrypoint (push + item sync)\n      server/sse.rs             # Per-target typed SSE event bus + stream endpoint\n      server/installation_token.rs # GitHub App JWT → installation token, cached + refreshed\n      knowledge/\n        page.rs                 # /knowledge route composition\n        graph_canvas.rs         # SVG graph view\n        filter_panel.rs         # Tag + type filters (dynamic from config)\n        editor/                 # Create/update form split into focused submodules\n        detail_bar.rs           # Bottom strip: hover/selection summary\n        detail_panel.rs         # Right-hand slide-out: rendered markdown + work-item card\n        orphan_banner.rs        # Amber advisory for unknown node types\n        config_loader.rs        # 30s TTL cache for .gitnodes.yml\n        draft.rs                # localStorage autosave (schema v2)\ndocs/\n  README.md\n  FEATURES.md\n  guides/\n    GETTING_STARTED.md\n    CONFIGURATION.md\n    DEPLOYMENT.md\n  OPERATOR_NOTES.md\n  ROADMAP.md\n```\n\n## Configuration\n\nNode types are declared in `.gitnodes.yml` at the root of the target repo.\nThe binary ships a built-in default equivalent to seven starter types\n(concept, adr, meeting, post-mortem, project, runbook, tag), so repos without\nthe file keep working unchanged. Repos created before the rename are still read\nfrom a legacy `.brain-config.yml` if `.gitnodes.yml` is absent.\n\nThe built-in default doubles as a worked example: any repo of markdown files\nwith YAML frontmatter works as a target, with or without a config file.\n\nWork items are configured the same way: node types can declare `work_item_kind`, and\nthe label taxonomy in `.gitnodes.yml` drives provider-facing labels without hardcoding\nGitHub-specific names in the app.\n\nSaved views accept an optional `weight:` (integer; lower = earlier, default 0) so a\nsingle pinned view can float to the top without re-ordering every entry. Individual\nnotes can declare an optional `cover:` in their frontmatter — a repo-relative image\npath or an absolute `https://` URL — to render a hero image at the top of the detail\npanel. Backlinks in the detail panel are grouped by node type, in the same order as\n`node_types[]`.\n\n### Typed graph edges (`link_fields`)\n\nNode types can opt into **typed edges** by declaring `link_fields:` — a map from a\nfrontmatter field name to the target node type. The graph builder resolves slug\nvalues in those fields against existing files and materializes edges tagged with\nthe source field name, alongside the body-link edges that already exist.\n\n```yaml\n- name: pokemon\n  directory: pokemon\n  link_fields:\n    trainer: trainer          # pokemon.trainer  → ownership\n    locations: route          # pokemon.locations → encounter geography\n    evolves_to: pokemon       # pokemon.evolves_to → evolution chain\n```\n\nThe canvas styles edges by their kind (`Body`, `Frontmatter(field)`, `Tag`) and\nexposes a toggle legend in the bottom-left so users can isolate ownership,\ngeography, evolution, or tag relations from narrative body citations. Slugs that\ndon't resolve to an existing file are silently ignored — useful for documenting\nfuture entities without breaking the graph. The field is optional and\nbackward-compatible (empty = no typed edges).\n\n## Environment variables\n\nFor local `gitnodes serve [dir]`, the target repository, branch, and credential\nare discovered from the Git checkout and GitHub CLI login. Explicit environment\nconfiguration always takes precedence.\n\nRequired for deployments or checkouts without an `origin` remote:\n\n| Var                        | Purpose                                   |\n| -------------------------- | ----------------------------------------- |\n| `TARGET_GITHUB_REPOSITORY` | Default repository in `owner/repo` format |\n\nChoose one authentication mode:\n\n| Var                    | Purpose |\n| ---------------------- | ------- |\n| `GITHUB_PAT`           | Single-user mode: use this PAT for every GitHub request; no OAuth App required. |\n| `GITHUB_CLIENT_ID` + `GITHUB_CLIENT_SECRET` | Multi-user mode: GitHub OAuth App credentials. |\n\n\u003e **OAuth scope note.** In multi-user OAuth mode, login requests GitHub's `repo`\n\u003e scope — an OAuth App cannot be restricted to a single repository, so the issued\n\u003e token can read and write *all* of the user's repositories. GitNodes stores it\n\u003e encrypted at rest, uses it server-side only, and still gates every target by live\n\u003e repository permissions (a user without `pull` sees nothing). For a tighter blast\n\u003e radius, prefer single-user PAT mode (`GITHUB_PAT`, whose scopes you choose) or\n\u003e install GitNodes as a GitHub App scoped to selected repositories.\n\nCommon optional settings:\n\n| Var                    | Default                     | Purpose |\n| ---------------------- | --------------------------- | ------- |\n| `TARGET_GITHUB_BRANCH` | `main`                      | Branch to read/write. |\n| `LEPTOS_SITE_ADDR` / `PORT` | `127.0.0.1:3000`       | Bind address. Hosts like Railway and Fly inject `PORT`. |\n| `SESSION_DB_URL`       | `sqlite://data/sessions.db` | SQLite path for sessions, audit log, and projection. Mount it persistently in production. |\n| `GITHUB_LOGIN_ORG`     | _(org-less)_                | Restrict login to an organization. Target access stays gated by live repository permissions. |\n| `BRAND_NAME`           | `GitNodes`                  | Brand shown in the header and page title. |\n| `RUST_LOG`             | `gitnodes_app=info,warn`    | tracing-subscriber filter. |\n\nEverything else has a safe default and is only needed for specific setups —\nwebhook-driven sync (`WEBHOOK_SECRET`, the `GITHUB_APP_*` trio, `GITHUB_TOKEN`),\nper-IP rate limits, retention and sync-job tuning, explicit session-key\nmanagement (`SESSION_ENCRYPTION_KEY*`), and the loopback escape hatches\n(`GITNODES_ALLOW_REMOTE_PAT`, `GITNODES_ALLOW_REMOTE_PREVIEW`, `GITNODES_NO_OPEN`).\nMost deployments never touch these.\n\nThe OAuth app's callback URL must be `{host}/auth/callback`.\n\nSee the [deployment guide](docs/guides/DEPLOYMENT.md) for authentication modes,\npersistence, webhooks, and the complete operator environment table.\n\nExisting deployments may keep `TARGET_GITHUB_ORG`, `TARGET_GITHUB_REPO`, and\ntheir legacy `GITHUB_*` aliases. Those split variables retain the historical\nlogin organization fallback. New deployments using\n`TARGET_GITHUB_REPOSITORY` default to org-less login.\n\nAny GitHub user can complete OAuth in the default setup, but GitNodes serves a\ntarget only when GitHub reports live `pull` permission. Write and administration\ncapabilities continue to follow `push`, `maintain`, and `admin`.\n\n## Local development\n\nPrereqs: Rust toolchain from `rust-toolchain.toml`, Node 18+, `cargo-leptos`,\n`wasm32-unknown-unknown` target, optionally [`just`](https://github.com/casey/just).\n\n```bash\njust setup        # once — installs tailwind + typography plugin\njust css-watch \u0026  # rebuild style/main.css on changes\njust dev          # cargo leptos watch\n```\n\nOr without `just`: `npm install`, `npm run watch:css \u0026`, `cargo leptos watch`.\n\nFor OAuth development, copy `.env.example` and fill its three primary values.\nFor local checkout-based use, `gitnodes serve` can instead discover the target\nand reuse `gh auth` without an `.env`.\n\n## Production build\n\n```bash\ndocker build -t gitnodes .\ndocker run -p 3000:3000 \\\n  -e GITHUB_CLIENT_ID=... \\\n  -e GITHUB_CLIENT_SECRET=... \\\n  -e TARGET_GITHUB_REPOSITORY=your-owner/your-repository \\\n  -v gitnodes_data:/app/data \\\n  gitnodes\n```\n\nMount `/app/data` on a persistent volume so sessions and the generated\nencryption key survive restarts.\n\nWebhook-driven projection rebuilds need a server-side credential — set either the `GITHUB_APP_*` trio (preferred, auto-rotating) or `GITHUB_TOKEN` (PAT fallback). On hosts that store env vars as raw strings (Railway, Fly, k8s Secrets), paste the PEM with real newlines; the `\\n` escape is only needed for `.env` files.\n\n## Status \u0026 roadmap\n\nGitNodes is built on a mature core: config-driven node types, an atomic\nmulti-file Git commit layer, a rebuildable SQLite projection, webhook + SSE live\nsync, multi-repository routing, bidirectional work-item sync, and\npermission-aware direct-write vs pull-request flows are all in place. Security,\noperational-readiness (`/healthz`, `/readyz`, rate limiting, session\nencryption), and schema-operations hardening lanes are closed.\n\nSee [`docs/ROADMAP.md`](docs/ROADMAP.md) for the overall direction.\n\n## License\n\nThis workspace is split-licensed:\n\n- The **library crates** — `gitnodes-domain`, `gitnodes-graph`, `gitnodes-auth`,\n  `gitnodes-storage` — are licensed under the\n  [Apache License, Version 2.0](LICENSE-APACHE). Reuse them freely.\n- The **deployable application** — `gitnodes-app` — is licensed under the\n  [GNU Affero General Public License v3.0 or later](LICENSE-AGPL). If you run a\n  modified GitNodes as a network service, the AGPL requires you to offer your\n  users the corresponding source.\n\n`gitnodes-app` incorporates the Apache-2.0 libraries (one-way compatible into the\nAGPL), so the combined application is distributed under the AGPL while the\nlibraries remain independently usable under Apache-2.0.\n\nCopyright (C) 2026 Andrea Bozzo.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreabozzo%2Fgitnodes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandreabozzo%2Fgitnodes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreabozzo%2Fgitnodes/lists"}