gitstate reads your repositories and derives project state, effort, contribution, and classification directly from git and your forge. No tickets to maintain, no numbers to invent. It runs locally: a Rust core over plain SQLite, wrapped in a Tauri desktop app — no SaaS backend, no multi-tenant server, no cloud account. What you run is what you own.
MIT OR Apache-2.0 · Rust 1.85+ · Tauri 2 · React + Vite · SQLite · local-first · P2P (CRDT) · no cloud
Quick start · What it is · How it works · Screenshots · Architecture · Classification & decentralization · Docs · Roadmap
Every project-tracking tool — Jira, Linear, ClickUp, ZenHub — is a manually-maintained fiction sitting next to git. People re-type into tickets what they already did in the repo. The result is unreliable by construction: estimates are off ~30% (and have been for 40 years), velocity is gamed the moment it's a target, and timesheets are reconstructed from memory on Friday.
Git is the real ledger. gitstate stops asking humans to invent numbers — it observes work from git and the forge — and makes whatever fiction remains explicit.
The difference from the old gitstate (and from every incumbent): there is no central server. A
Rust core over a plain SQLite file, wrapped in a Tauri desktop app. No account, no cloud, no
telemetry. It talks to GitHub/GitLab from your machine using your gh/glab login,
classifies with your local LLM (or a deterministic fallback), stores everything in a local
database, and shares saved working sets and categories peer-to-peer — never through a hub.
If a feature would force a human to invent a number, it doesn't ship.
| Discipline | What it means |
|---|---|
| Derived, not entered | State comes from git — merged = done, PR open = in progress. Nobody maintains tickets. |
| Measure work, not workers | Contribution is shown as texture across six dimensions (including review), never a single rank, never a bonus formula. |
| Evidence-based, gaps visible | Effort comes from an LLM reading the shape of a change (difficulty, not line count); what git can't see is flagged, never invented. |
Contribution is derived as six normalized dimensions — the gitstate essence — computed within the repo cohort so seniors, reviewers, and maintainers are never zeroed:
| Dimension | Derived from |
|---|---|
| Shipped | merged PRs, closed issues |
| Review | reviews performed on others' work |
| Effort | Σ judged diff-difficulty (LLM reads the change; falls back to a deterministic heuristic) |
| Quality | inverted from reverts caused, bug introductions (SZZ), and cycle time |
| Ownership | areas of the codebase authored/maintained |
| Durability | surviving lines ÷ authored lines (git blame) |
Agent identities (Claude Code, Dependabot, …) are first-class: every contribution carries an
agent_pct, so autonomous work is counted honestly rather than hidden. The composite is a texture
value, displayed as evidence — never a leaderboard.
Everything runs on your machine. The only network endpoints in the picture are ones you
configured — your forge (gh/glab or a token) and, optionally, your local LLM. A plain scan of a
local repo makes zero network calls.
flowchart LR
subgraph machine["your machine"]
direction LR
git["your git repos<br/>(git2-rs: walk, diff, blame, SZZ)"]
forge["gh / glab CLI<br/>(PRs, issues, reviews)"]
core["gitstate core + daemon<br/>(derive state · effort · contribution)"]
db[("SQLite<br/>contexts · categories · caches")]
llm["local LLM (llmux /<br/>OpenAI-compatible) — optional"]
app["Tauri desktop app<br/>(React UI over the daemon)"]
git --> core
forge --> core
core <--> db
core <-.->|"classify (opt-in)"| llm
app <-->|"HTTP :7473"| core
end
The desktop app and the headless daemon serve the same JSON API — the Tauri shell just boots the
daemon in-process on a local port and points the React UI at it. Run it headless (gitstate serve)
as an always-on peer, or as a desktop app; same core either way.
Between machines there is no hub. The only things that cross the network are saved contexts (a working set of repos, PRs, notes, tags) and categories, synced peer-to-peer as CRDTs — so two peers converge with no authority in the middle. Your commits, diffs, and code never leave your box.
flowchart TB
a["Alice's node<br/>(desktop or headless peer)"]
b["Ben's node"]
c["Chris's node"]
a <-->|"contexts + categories<br/>(CRDT ops, signed transport)"| b
b <-->|"CRDT ops"| c
a <-->|"CRDT ops"| c
All shots are the real desktop app against a deterministic synthetic demo dataset (gitstate seed --demo) — a fake org, fake pseudonymous contributors, never real git/forge history. See docs/screenshots/ for the full set (including Involvement, People, Board, Categories and light-mode shots) and web/scripts/screenshots.mjs to regenerate.
Both are local-first, like everything else. Jira and Linear each issue personal
API tokens, so gitstate calls their public HTTPS API from your machine with your own
credential — the same posture it already takes with your gh/glab login. There is
no broker, no OAuth callback, and no gitstate server in the path. The token lives in
your local SQLite file, is never returned by the API once saved (reads are redacted),
and is sent only to the vendor it belongs to.
If a token isn't an option — an air-gapped machine, a locked-down Jira Server/DC instance, or you'd simply rather not store a credential — paste or drop a Jira/Linear CSV/JSON export instead. That path performs no network requests at all.
Imported issues land as ordinary work items, so classification, effort judging and
every analytics rollup treat them exactly like native ones. Ids are derived from
(source, key), so re-importing updates in place rather than accumulating duplicates.
Status: transform in progress. gitstate is being rebuilt from a multi-tenant Go+Postgres SaaS into this standalone local-first desktop app. The Rust workspace (
crates/*), the Tauri shell (apps/desktop), and the repointed React UI (web/) are landing now; the legacy Go server underinternal/andcmd/is kept in-tree, untouched, for a staged port (see docs/MIGRATION-NOTES.md). Check PROGRESS.md for what is wired today.
- Rust stable (1.85+) and Node 20+ (for the desktop/web build).
ghand/orglabon yourPATH, logged in (gh auth login) — or a forge token in the environment (GITSTATE_GH_TOKEN/GH_TOKEN,GITSTATE_GLAB_TOKEN/GITLAB_TOKEN). No forge login is needed to scan a purely local repo.- (Optional) a local LLM endpoint for classification and effort judging
(
VULOS_LLMUX_URLorOPENAI_BASE_URL). With none configured, gitstate uses a deterministic heuristic — everything still works, offline.
git clone https://github.com/vul-os/gitstate
cd gitstate
# Core library + CLI + headless daemon (no P2P deps pulled in)
cargo build --workspace
cargo run -p gitstate-cli -- --help
# Desktop app (starts the daemon in-process, loads the React UI)
cd apps/desktop && npm install && npm run tauri devcargo build never touches the P2P sync crate — gitstate-sync is excluded from the default
workspace and lives behind an optional sync-dmtap feature, so a bare build has no network stack.
# Register a repo and derive its state (git only — no forge, no network)
gitstate repo add ~/code/my-project
gitstate repo scan <repo_id> --no-forge
gitstate state <repo_id>
# Pull PRs/issues/reviews via your gh/glab login, then derive contributions
gitstate repo scan <repo_id>
gitstate contributions <repo_id> # the six-dimension texture table
gitstate classify <repo_id> # local LLM if configured, else heuristic
# Save a working set and share it P2P (sync built with --features sync-dmtap)
gitstate context create --name "Q3 refactor" --repo <repo_id> --pr vul-os/gitstate#42 --tag refactor
gitstate context list
# Run as an always-on headless peer serving the same API + UI
gitstate serve # binds 127.0.0.1:7473 (GITSTATE_ADDR / GITSTATE_PORT)
gitstate data path # where your local database livesFull CLI reference: docs/GETTING-STARTED.md. Forge setup (gh/glab and tokens): docs/FORGE-SETUP.md.
A Rust Cargo workspace modeled on its sibling products (slipscan, ofisi): pure-domain crates in
the middle, I/O crates at the edges, one daemon that serves both the desktop shell and headless peers.
| Crate | Role |
|---|---|
| gitstate-core | Pure domain — types (Repo, Commit, Contribution, ProjectState, Context, Category, Taxonomy, …) and the four traits (ForgeClient, Classifier, Store, SyncEngine). No I/O. |
| gitstate-git | git2-rs derivation engine — walk history, diff, blame survival, SZZ bug-intro, and the six-dimension contribution math. |
| gitstate-forge | GitHub + GitLab via shelling gh/glab (REST/GraphQL fallback with a token) — PRs, issues, reviews. |
| gitstate-classify | Classifier — local LLM (llmux / any OpenAI-compatible endpoint) + a signed taxonomy + local personalization, with a deterministic heuristic fallback. |
| gitstate-store | rusqlite persistence — contexts, categories, derived caches, the CRDT op log. |
| gitstate-daemon | axum HTTP server — serves web/dist (SPA) and the JSON API. The headless always-on peer. |
| gitstate-cli | clap CLI (serve, repo, state, contributions, classify, effort, context, category, taxonomy, sync, data). |
| gitstate-sync | P2P CRDT sync of contexts + categories. Excluded from the default workspace; behind an optional sync-dmtap feature so a plain cargo build never pulls P2P deps. |
| apps/desktop | Tauri shell. Boots the daemon on an ephemeral local port and loads the same React app the headless mode serves — the UI is not forked. |
| web/ | The kept React frontend, repointed at the daemon's JSON API (§ web API contract); the old multi-tenant auth/org/billing surfaces are removed. |
Full contract: docs/ARCHITECTURE.md.
gitstate classifies work items (features, bugfixes, refactors, security, agent-authored changes, …) and judges effort. Two decisions keep this honest and decentralized:
-
Personal categorization is local-only. Classification runs against your LLM endpoint (llmux or any OpenAI-compatible URL) or a deterministic heuristic — never a gitstate-hosted model. Corrections you make train a local personalization store: each box learns its own conventions. Nothing about your work is pooled.
-
Label alignment travels as a signed data file, not a service. So that peers agree on what
feature.apiorbugfixmeans, gitstate ships a versioned, content-addressed, ed25519-signed taxonomy — verified against a pinned key, fail-closed (a bad signature falls back to local-only categories, never silently trusts). It's data you can inspect, not an endpoint you call. See docs/CLASSIFICATION-AND-TAXONOMY.md.
What is deliberately NOT built. Cross-population features — trending, "others tagged this", "similar repos" — need a view of strangers you'll never meet, so they don't belong in a git tool that runs on your machine. That surface is left as a dormant, optional coordinator seam and nothing more. There is no anti-spam/sybil tier (a tax on that unbuilt discovery layer) and no pooled fine-tuning (replaced by local personalization). The rule: only "needs a view of strangers" belongs to an optional coordinator; everything a git tool is for is local + P2P. Rationale in decisions.md.
- Getting started — install, first scan, the full CLI.
- Architecture — crates, the daemon API, the web contract, the SQLite schema.
- Classification & taxonomy — local LLM, the signed taxonomy, personalization.
- P2P contexts — saved working sets, the CRDT merge model, sharing.
- Forge setup —
gh/glaband token configuration. - Migration notes — why the legacy Go server is still in-tree, and the staged port.
- Security model · Roadmap · Decisions · Changelog
gitstate is licensed MIT OR Apache-2.0 — at your option (see LICENSE-MIT and
LICENSE-APACHE), matching every sibling in the vulos suite. The former AGPL-3.0
license and the ee/ commercial Enterprise tier were removed in the transform to a standalone
local-first app (there is no multi-tenant service to fence off).
Want to hack on it? See CONTRIBUTING.md. Found a vulnerability? See SECURITY.md.





