Repository navigation
How GitHub Works
Nibtree signs in to GitHub with a personal access token kept in the system keychain, or with the GitHub CLI's login, and uses the REST API for three actions: Share Project on GitHub, Sync Fork and Create Gist. For the user side, see GitHub.
Git cannot create a repository, sync a fork or post a gist: those are GitHub API calls, and people expect them in the Git menu. The hard part is the token: it must never land in a file, a log, the Git Console or an error, and the UI must not keep it.
The Rust side lives in src-tauri/src/github/. Everything goes through one GitHub service struct whose outside world is passed in as traits, so every path is tested against fakes:
classDiagram
class GitHub {
transport: HttpTransport
secrets: SecretStore
gh: GhCli
config_dir
host
sign_in_with_token()
sign_in_with_cli()
sign_out()
share_project()
repository()
sync_fork()
create_gist()
}
class HttpTransport {
send()
}
class SecretStore {
read()
write()
remove()
}
class GhCli {
installed()
token()
}
GitHub --> HttpTransport : UreqTransport
GitHub --> SecretStore : KeychainStore
GitHub --> GhCli : SystemGh
GitHub --> GitHubClient : REST calls
commands.rs builds the real one (with_github): UreqTransport, KeychainStore, SystemGh and ~/.nibtree, host github.com.
sequenceDiagram
participant Form as GitHubSignInForm
participant RS as github_sign_in_with_token
participant API as api.github.com
participant KC as Keychain
participant File as github.json
Form->>Form: copy token, clear the field
Form->>RS: token (sent once)
RS->>RS: validate (no spaces, at most 512)
RS->>API: GET /user, Bearer token
API-->>RS: login, name, x-oauth-scopes
RS->>KC: write(github.com, token)
RS->>File: host, login, name, source, missingScopes
RS-->>Form: GitHubAccount (no token)
-
Token.
secrets.rsstores it with thekeyringcrate: serviceio.github.shibbirweb.nibtree.github, account = the host. macOS uses the Keychain, Windows the Credential Manager; other platforms refuse rather than use a file. If savinggithub.jsonfails, the token is removed again. -
GitHub CLI.
gh.rsfindsghin the Homebrew and system folders first (GUI apps start with a shortPATH), then onPATH, and runsgh auth token --hostname github.comwithGH_PROMPT_DISABLED=1and a 15 second timeout. It is asked each time a token is needed and never stored. This runs outsidegit/cli.rs, so it never reaches the Git Console. -
Who is signed in.
account.rskeeps~/.nibtree/github.jsonwithhost,login,name,source(tokenorghCli) andmissingScopes.github_accountreads only this file: no keychain read and no network, so opening Settings is free. A missing or unreadable file means signed out. -
Scopes. Classic tokens report
x-oauth-scopes;missing_scopeslistsrepoandgistwhen absent. Fine-grained tokens report none, so nothing is flagged.
http.rs wraps a blocking ureq agent on the system TLS stack (native-tls, platform root certificates), with a 10 second connect and 60 second overall timeout, at most 5 redirects, and the Authorization header kept only on same-host redirects. Error statuses are returned, not thrown, so client.rs can read GitHub's message. HttpRequest has no Debug, because its headers carry the token. Every call sends Accept: application/vnd.github+json, X-GitHub-Api-Version: 2022-11-28 and a Nibtree/<version> user agent.
api_error turns failures into plain sentences: 401 asks to sign in again, 403 or 429 with rate limit headers says when to try again, other 403s mention the scopes, 404 says the token may not see the repository, 422 shows GitHub's detail, 5xx says GitHub is having trouble, and no response says "Could not reach GitHub".
| Action | Calls | Then |
|---|---|---|
| Share Project |
POST /user/repos (auto_init: false) |
git remote add <remote> <clone_url>, then the UI pushes with upstream |
| Sync Fork |
GET /repos/{owner}/{repo}, POST /repos/{owner}/{repo}/merge-upstream
|
Fetch All; 409 becomes a "conflict" outcome with a compare link |
| Create Gist |
POST /gists with one file |
Result dialog with the link |
Share checks everything before touching anything: a valid name (GitHub's rules, also in githubModel.ts), a free remote name, a checked-out branch and a token. Only then does an unborn repository get its first commit (git add --all and git commit -F -), and only then is the repository created. The push uses api.pushWithOptions, which is the git CLI with git's own credentials, not the app token.
Sync Fork picks the branch with syncForkBranch: the current branch when it tracks the same name on the fork's remote, else the default branch. On a conflict, forkCompareUrl builds the compare page from the upstream branch into the fork.
Create Gist reads the active editor's selection, or the whole document, from CodeMirror; it refuses empty content, slashes in the name and anything over 5 MB.
githubActions.ts holds the three menu actions. Each calls requireAccount, which opens GitHubSignInDialog with the action's name when signed out and runs the action again once signed in. Dialogs are opened through githubDialogs.ts and rendered by GitHubDialogHost.svelte. GitHubSignInForm.svelte is shared by Settings > GitHub and the dialog. githubAccount.svelte.ts holds the account and the gh status, never a token.
| File | What it does |
|---|---|
src-tauri/src/github/commands.rs |
The nine github_* commands |
src-tauri/src/github/service.rs |
GitHub: sign in and out, share, sync, gist, validation |
src-tauri/src/github/client.rs |
REST calls, response types, api_error
|
src-tauri/src/github/http.rs |
HttpTransport, UreqTransport
|
src-tauri/src/github/secrets.rs |
SecretStore, KeychainStore, an in-memory store for tests |
src-tauri/src/github/gh.rs |
GhCli, SystemGh
|
src-tauri/src/github/account.rs |
github.json, api_base_for, missing_scopes
|
src/lib/views/github/ |
Actions, dialogs, sign-in form, account store, githubModel.ts
|
src/lib/menu/menuSpec.ts, menuState.ts
|
The Git > GitHub submenu and when each item is enabled |
Keychain only. A token in settings.json would be readable by any process and easy to leak in a bug report. Refusing on platforms without a keychain backend is better than a silent file.
The token goes one way. The frontend sends it once and gets back only the account. No command returns a token, so a bug in the UI cannot show it.
git keeps its own credentials. Pushing with the API token would mean writing it into a URL or a credential helper. Leaving pushes to git keeps behavior identical to the terminal.
Ready for GitHub Enterprise. api_base_for maps other hosts to https://<host>/api/v3, and the keychain account is the host. The UI only offers github.com for now.
-
src-tauri/src/github/tests.rs: every sign-in path (the token only in the keychain, rejected or malformed tokens never stored, offline, the CLI storing nothing), sign out, signed-out calls making no request, Enterprise API roots, Share (including an existing remote refused before anything is created and the first commit of an empty repository), name rules, every Sync Fork outcome, gists, readable API errors, malformed bodies, and the realUreqTransportagainst a local server. -
src/lib/views/github/githubModel.test.ts: names, remotes, gist names, the sync branch, titles, the compare URL, initials and the scope warning.
- Update this page when anything in
src-tauri/src/github/orsrc/lib/views/github/changes. - Update GitHub for any change to Settings > GitHub or the dialogs, and retake the
github-*screenshots. - The commands are listed in Commands and Events. Link items of the GitHub submenu are in How the Git Menu Works.
The app quit on any GitHub request with a token.
- The issue: signing in with a token, Share Project, Sync Fork or Create Gist closed the whole app.
-
Why it happened:
Cargo.tomlturned on ureq'snative-tls-no-defaultfeature, but ureq checks for the exact namenative-tlsbefore every HTTPS request and panics without it; the release build'spanic = "abort"turned that into a quit. The tests used plain HTTP, so they never reached the check. -
The fix and why we chose it: the
native-tlsfeature, which passes the check (it also brings a set of root certificates; the transport still verifies against the system's roots).ureq_transport_reaches_https_without_panickingsends HTTPS to a local non-TLS listener and expects an error; it fails with the old feature.
These pages are generated from docs/wiki. To correct something, open a pull request against the source file; edits made here are overwritten.
Using Nibtree
- Getting Started
- Welcome Screen
- Workspaces
- Files Panel
- File Operations
- File Icons
- Rounded Panels
- Editor and Tabs
- Pin Reorder and Wrap Tabs
- Single Tab Title
- Split Editor
- New File and Unsaved Changes
- Unload Hidden Tabs
- Editing Code
- Code Appearance
- Markdown Editor
- Image and PDF Preview
- Search Everywhere
- Recent Files
- Navigation Bar
- Find and Replace
- Changes and Commits
- Sign In Prompts
- Commit Box Layout
- Repository Actions
- Commit Options
- Ignoring Files
- Diffs
- Inline Diffs
- Staging View
- Revert a Change
- Resolving Conflicts
- Merge Tool
- Blame
- History and Log
- Merge Commits
- Branches and Tags
- Branches Popup
- Remotes
- Stashes
- Shelf
- Git Menu
- Git Dialogs
- Interactive Rebase
- Worktrees
- AI Agents
- AI Tasks
- AI Usage
- Keep Computer Awake
- Submodules
- Git LFS
- GitHub
- Git Console
- Terminal
- Terminal Features
- Scripts
- Navigation
- Menus
- Settings
- Settings Search
- Color Themes
- Settings Terminal and Automation
- Settings Files
- Updates
- Status Bar and Help
- Notifications
- Memory Use
- Clear Cache
- MCP and CLI
- Git Mergetool
- Keyboard Shortcuts
- Keyboard Shortcuts Editor
- Troubleshooting
- Troubleshooting Git
Developers
- Developer Guide
- Architecture
- Project Layout
- Backend
- How Folder Watching Works
- Folder Watching Bugs We Fixed
- How Workspace Files Work
- How the Preview Scheme Works
- Backend Services
- How Terminal Features Work
- Terminal Bugs We Fixed
- Frontend
- How the Rich Markdown Editor Works
- Rich Markdown Bugs We Fixed
- Git Menu Links
- Menu Keys and Routing
- Frontend Modules
- Blame Bugs We Fixed
- Project Color And Title Bar
- One Diff View
- Diff Bugs We Fixed
- Merge Tool Layouts
- Merge Tool Bugs We Fixed
- Color Theme Bugs We Fixed
- How the Connected Diff Works
- Rounded Panels Bugs We Fixed
- Update Bugs We Fixed
- Status Bar Bugs We Fixed
- MCP and CLI Bugs We Fixed
- Repository Actions Bugs We Fixed
- How the Changes Tab Works
- CodeMirror Patterns
- How the Cursor Works
- How Editor Features Work
- How the Path Bar Works
- Commands and Events
- Settings Reference
- Commands Workspace and Changes
- Commands Branches and Remotes
- Commands History and Extras
- Commands App and Tools
- Testing
- Test Suites
- Debugging
- Debugging with MCP
- Measuring Setting Memory
- How Terminal Memory Is Measured
- How Markdown Diagrams Work
- Releases and CI
- Publishing the Wiki
- Versioning and Changelog
- Browser Version Packages
- Browser Release Checks
- Docker Image
- Platforms and Signing
- Windows Support
- Windows Bugs We Fixed
- Windows MCP and CLI
- Docs and Screenshots
- UI Library
- UI Library MCP
- UI Library Bugs
- UI Library Migration
- Contributing
How features work
- Welcome screen
- Workspaces and folders
- Files panel
- File operations
- File icons
- Editor and tabs
- Pin, reorder and wrap tabs
- Single tab title
- Split editor
- New File and unsaved changes
- Unload hidden tabs
- Editing code
- Code appearance
- Markdown editor
- Image and PDF preview
- Search Everywhere
- Recent Files
- Navigation Bar
- Find and replace
- Changes and commits
- Sign-in prompts
- Commit box layout
- Repository actions
- Commit options
- Ignoring files
- Diffs
- Inline diffs
- Staging view
- Revert a change
- Resolving conflicts
- Merge tool
- Blame
- History and log
- Merge commits
- Commit tabs
- Branches and tags
- Branches popup
- Remotes
- Stashes
- Shelf
- Git menu
- Git dialogs
- Interactive rebase
- Worktrees
- AI agents
- AI usage
- Keep computer awake
- AI tasks
- Submodules
- Git LFS
- GitHub account
- Git Console
- Back and Forward
- Integrated terminal
- Scripts tool window
- Menu bar
- Settings
- Search in Settings
- Color themes
- Rounded panels
- Updates and release channels
- Status bar and help links
- Notifications
- Memory readout and memory log
- Clear Cache
- MCP server and command line tool
- git mergetool mode