Command-line client for on-chain Git on Solana. Stores repos, files, and commit history entirely on chain via @iqlabs-official/git-sdk.
# create your own
iqgit init
iqgit create my-app --public
iqgit add .
iqgit commit -m "first"
iqgit push
# publish it as a live site (on-chain hosting)
iqgit pages deploy
# read someone else's (no wallet needed)
iqgit clone <owner>/<repo>
iqgit log
npm install -g @iqlabs-official/iq-git-cliOnce installed, iqgit is available from any directory.
git clone <this-repo>
cd iq-git-cli
npm install
npm run build
npm link # registers `iqgit` against this buildThe first time you run a write command (create, push, wallet balance),
the CLI walks you through:
- Wallet: generate a new Solana keypair, or point at an existing
keypair JSON file. Stored in
~/.iq-git/wallets/default.jsonby default. - RPC URL: required for any chain interaction. Free option is
Helius. Paste the URL when prompted.
Saved to
~/.iq-git/.env.
Read-only commands (clone, log, status, registry) only need the
RPC. clone does not even prompt for a keypair, so anyone can pull a
public repo without setting up a wallet first.
iqgit config rpcUrl # show current value
iqgit config rpcUrl 'https://my-rpc.example' # set a new one
iqgit config --unset rpcUrl # reset (will re-prompt)Quote the URL — if it contains ? (e.g. an API-key query string), an
unquoted URL makes zsh try to glob it and fail with no matches found:.
You can also override per-command via environment variable:
SOLANA_RPC_ENDPOINT='https://my-rpc.example' iqgit logiqgit push forwards a speed setting to the solana-sdk
SESSION_SPEED_PROFILES (controls RPS + concurrency). Default is light,
the Helius free-tier friendly preset. You can pick a preset name or dial
raw values per-run and globally:
# preset name
iqgit config speed heavy # save as global default
iqgit push --speed extreme # one-off override
# raw dials (win over the preset; any subset works)
iqgit config rps 80 # global default maxRps
iqgit push --rps 120 --concurrency-upload 30Available presets: light | medium | heavy | extreme. Raw flags:
--rps, --concurrency, --concurrency-upload. Config keys mirror them:
rps, concurrency, concurrencyUpload.
log, registry, clone, status (when fetching base tree), and
wallet repos route reads through the IQ Gateway HTTP cache by default.
The default chain is the same 3-gateway list iq-chan uses (primary →
Akash direct → backup), with direct RPC as the final fallback. This
sidesteps RPC per-method rate limits on bulk table reads and matches
how blockchan's frontend resolves chain reads.
GATEWAY_URL value |
behavior |
|---|---|
| (unset, default) | 3-gateway chain → RPC fallback (recommended) |
https://my.example |
single override → RPC fallback |
url1,url2,url3 |
comma-separated list, tried in order → RPC |
off |
disable all gateway, raw RPC only |
Anyone can run their own gateway. See iq-gateway.
iqgit pages deploy publishes a repo as a live site — the on-chain
equivalent of enabling GitHub Pages. Commit an iqpages.json manifest at
the repo root first:
{ "name": "my-app", "version": "1.0.0", "description": "...", "entry": "index.html" }iqgit add iqpages.json && iqgit commit -m "add pages manifest" && iqgit push
iqgit pages deploy # registers the deploy + one-time fee
iqgit pages status # prints the live URL once deployedDeploy writes one marker row to the shared iqpages-root/deployed gallery
plus a one-time fee; it's a no-op if the repo is already deployed. Nothing
is re-uploaded — the site is served live from the repo's latest commit
tree at <gateway>/site/<treeTxId>/<entry>, so a later push updates
the site with no re-deploy. status is read-only and needs no wallet.
| Command | Description |
|---|---|
iqgit init |
Create local .iqgit/ directory. No chain interaction. |
iqgit create <name> [--public|--private] |
Register repo on chain. |
iqgit add <pathspec...> |
Stage paths for the next commit. |
iqgit reset [pathspec...] |
Unstage paths (no args clears the index). |
iqgit commit -m "<msg>" |
Build a snapshot of staged paths under .iqgit/pending/. No chain interaction. |
iqgit push |
Upload all pending commits to chain. Resume-safe. |
iqgit clone <owner>/<repo> [dir] |
Pull a repo's latest snapshot to disk. |
iqgit restore [commitId] |
Restore working tree to a commit (default: latest). |
iqgit log [--limit N] [--owner ... --repo ...] |
Print commit history. |
iqgit status |
4-tier diff: HEAD, pending commits, staged index, working tree. |
iqgit registry [--limit N] |
Browse the public on-chain repo gallery. |
iqgit pages deploy |
Publish this repo to iq-pages (on-chain hosting). No-op if already deployed. |
iqgit pages status |
Show whether this repo is deployed and print its live URL. Read-only. |
iqgit config [key] [value] |
Get or set global config. |
iqgit wallet new|show|balance|repos |
Manage keypair. |
Each push writes three kinds of records on chain:
- Blobs: file contents, one inscription per unique hash
- Tree: JSON map of
{ path: { txId, hash } }, one per commit - Commit row:
{ id, message, treeTxId, parentCommitId, timestamp, author }
commit builds these locally; push uploads them. Splitting the two means
multiple commits can be batched into a single push, amortizing Solana
transaction fees.
push is checkpointed:
- Each blob's
{ hash: txId }is appended to.iqgit/upload-cache.jsonon success, with synchronous flush. - The tree's txId and the commit row's signature are persisted into the
pending commit's
meta.jsonbetween steps.
If a push fails partway through (network drop, kill signal, RPC error),
the next iqgit push resumes exactly where the last one stopped. Already
uploaded blobs are reused from cache instead of being re-inscribed.
scan reads both .gitignore and .iqgitignore (if present) and merges
them. .git/ and .iqgit/ are always excluded. Use .iqgitignore for
files you want in git but not on chain (e.g. large binaries).
Files larger than 1MB trigger a confirmation prompt during commit since
on-chain inscription cost scales with size. Skip with --no-warn-large.
Per-repo state lives in .iqgit/ next to your code. Safe to peek at
when debugging:
.iqgit/
├── config.json # { owner, repo, isPublic }
├── HEAD # last successfully-pushed commitId
├── index.json # paths staged for the next commit (string[])
├── pending/
│ └── NNN-<commitId>/
│ ├── meta.json # { id, message, parentCommitId, treeTxId, committedSig, ... }
│ ├── tree.json # full snapshot of this commit's file set
│ └── blobs/<hash> # raw base64 content, one per unique hash
└── upload-cache.json # { hash: { txId, uploadedAt } } resume cache
Global config lives in ~/.iq-git/:
~/.iq-git/
├── config.json # walletPath + rpcUrl
├── .env # SOLANA_RPC_ENDPOINT, optionally GATEWAY_URL
└── wallets/default.json # generated keypair (only if you opted in)
src/
├── cli.ts # commander entry, registers each command
├── setup.ts # wallet / RPC gate; constructs GitClient
├── ui.ts # chalk + ora + inquirer wrappers
├── core/
│ ├── scan.ts # fs walk + ignore + base64 + hash
│ ├── repo.ts # all .iqgit/ disk I/O
│ ├── tree.ts # base-tree resolution shared by commit / status
│ └── gateway.ts # gateway-first reads with RPC fallback
└── commands/ # one file per CLI command
# (init, create, add, reset, commit, push, clone,
# restore, log, status, registry, pages, config, wallet)
if iqpages.json error
✖ iqpages.json missing in puter-angel. Commit it first, then deploy.
use this command to bypass the gateway and use RPC directly
GATEWAY_URL=off SOLANA_RPC_ENDPOINT="your-helius-url" iqgit -n mainnet pages deploy
Apache-2.0. See LICENSE.