gitlana
Agent-skill packages stored entirely on the Solana blockchain.
No GitHub. No Arweave. No IPFS. No indexer. A package is a
Metaplex Core asset: the manifest lives in the
on-chain Attributes plugin, the code lives as a deterministic tar.gz in an on-chain
AppData plugin. Every Solana validator holds the bytes; any agent can retrieve and
verify a package with a single getAccountInfo RPC call, knowing only the asset address.
Try it right now against a live mainnet package:
git clone https://github.com/tonbistudio/gitlana && cd gitlana && npm install
node src/cli.mjs info 6ZjdCaiu8v1vCK79i4A2DVRuboTUYw3YnkkxJZbZD6Lj -u mainnet-beta
on-chain skill at 6ZjdCaiu8v1vCK79i4A2DVRuboTUYw3YnkkxJZbZD6Lj:
name: [email protected]
entrypoint: SKILL.md
permissions: (none — read-only)
payload: 2796 bytes, sha256 verified ✓
...
That skill's source code has no other home. It exists as ~2.8 KB of gzip bytes inside a Solana account, and it is itself the documentation teaching an agent how to resolve on-chain skills — installing it bootstraps the capability to install any of them.
Commands
gitlana publish <dir> mint a new skill asset from a directory
gitlana update <asset> <dir> replace HEAD in place (mutable push, ~free)
gitlana release <asset> mint an immutable, frozen snapshot of current HEAD
gitlana info <asset> show manifest + file list without installing
gitlana install <asset> [dest] fetch, hash-verify, confirm, extract
options:
-u, --url <cluster|rpc-url> devnet (default) | mainnet-beta | testnet | full URL
-k, --keypair <path> signer keypair (default ~/.config/solana/id.json)
-y, --yes skip the install confirmation
--name <n> --version <v> manifest overrides
Installing and inspecting are free and keyless. Publishing requires a funded keypair.
Publishing a skill, start to finish
New to Solana? This is the whole path from zero to a minted package. Do it on devnet first — devnet SOL is free and throwaway — then repeat the last step on mainnet when you're happy.
The commands below write
gitlanafor brevity. Until this is published to npm, run it from the repo asnode src/cli.mjs …, or expose thegitlanacommand locally withnpm link(thengitlana …works anywhere). Everything else is identical.
1. Install the Solana CLI (for the keypair + faucet tools).
See https://docs.solanalabs.com/cli/install. Verify with solana --version.
2. Create a keypair to publish from.
solana-keygen new -o ./publisher.json
solana address -k ./publisher.json # your publisher wallet address
Keep this file private — it is the key that controls anything you mint. Never commit it
(this repo's .gitignore already blocks *wallet*.json, id.json, and *.key).
3. Fund it on devnet (free):
solana airdrop 1 -k ./publisher.json -u devnet
solana balance -k ./publisher.json -u devnet
If the faucet rate-limits, wait a minute and retry, or use https://faucet.solana.com.
4. Prepare your skill directory. At minimum a SKILL.md; optionally small scripts and a
gitlana.json for manifest fields (see below). Keep it small — kilobytes, not a dependency
tree. This repo ships one you can use as-is: examples/onchain-skill-resolver/.
5. Publish to devnet:
gitlana publish examples/onchain-skill-resolver -k ./publisher.json -u devnet
You'll see the deterministic archive's size and sha256, the new asset address being minted, the payload being staged, and finally:
published [email protected]
asset address: <YOUR_NEW_ASSET_ADDRESS>
install with: gitlana install <YOUR_NEW_ASSET_ADDRESS> -u devnet
That address is your package's permanent identifier — the thing you share.
6. Verify it resolves (free, no keypair needed):
gitlana info <YOUR_NEW_ASSET_ADDRESS> -u devnet
gitlana install <YOUR_NEW_ASSET_ADDRESS> ./roundtrip -u devnet
info prints the manifest and confirms sha256 verified ✓; install reconstructs the
directory byte-for-byte.
7. Push an update (optional) — this is the "git push" of gitlana. Edit the files, bump the version, and replace the payload in place; the address does not change:
gitlana update <YOUR_NEW_ASSET_ADDRESS> examples/onchain-skill-resolver \
--version 0.1.1 -k ./publisher.json -u devnet
8. Cut a frozen release (optional) — mints a separate, immutable snapshot of the
current HEAD and points the repo's head at it. The snapshot's update authority is revoked,
so its bytes can never change again — not even by you:
gitlana release <YOUR_NEW_ASSET_ADDRESS> -k ./publisher.json -u devnet
# → immutable snapshot minted: <SNAPSHOT_ADDRESS>
9. Go to mainnet. When the devnet round trip looks right, fund the same keypair with real
SOL and rerun publish (and, if you want it permanent, release) with -u mainnet-beta. A
small skill costs a few dollars; see Costs. The mainnet asset
address is different from the devnet one — each cluster is its own world.
The storage deposit ("rent") is refundable: burning an asset returns it. But a published package others may depend on should be left alone — don't close its account.
The on-chain format (onchain-skill/0.1)
Repo asset (permanent identity — the address you share)
├── Attributes plugin: standard, name, version, license, entrypoint,
│ permissions, content_sha256, content_length, head=<latest snapshot>
└── AppData plugin (Binary): deterministic tar.gz of the package directory
Release snapshot (own asset, update authority revoked = frozen forever)
└── same layout + snapshot_of=<repo asset>, parent=<previous snapshot>
- Updates replace the AppData in place: the address never changes and rent adjusts
by the size delta. History is opt-in:
releasemints a frozen snapshot whoseparentattributes form a walkable on-chain chain. Snapshot immutability is enforced by the program — a frozen release rejects writes even from the key that minted it. - Payloads larger than one transaction are staged through a temporary buffer account
and copied with a single
writeData(buffer); the buffer's rent is reclaimed. - Archives are deterministic (
src/dettar.mjs): sorted entries, zeroed mtimes/owners, fixed modes — byte-identical output from identical file contents on any machine, socontent_sha256is reproducible from source. - The installer verifies sha256 against the on-chain manifest before extracting, and
rejects absolute paths,
..traversal, and symlinks.
Optional gitlana.json
Place next to your SKILL.md to set manifest fields:
{ "name": "my-skill", "version": "0.1.0", "license": "MIT",
"entrypoint": "SKILL.md", "permissions": [] }
Costs (mainnet, July 2026)
Storage is Solana rent — a refundable deposit, not a fee: ~0.00000696 SOL/byte (≈ $0.57/KB at $80/SOL), plus ~0.003 SOL mint overhead. A 5 KB skill costs ~$3 to publish; updates in place cost transaction fees only. Solana's SIMD-0437 rent reduction (rolling out from August 2026) cuts storage cost ~10x. Burning an asset refunds its rent.
Retrieval without this CLI
The format is deliberately resolvable with no tooling. One paragraph suffices to bootstrap any agent that can make an HTTP request:
POST
getAccountInfo(params: the address,{"encoding":"base64"}) to a Solana RPC. Decode the base64. The account contains a plaintext key-value manifest (includingcontent_sha256andcontent_length) followed by a gzip archive starting at the1f 8b 08magic bytes,content_lengthbytes long. Verify the slice's SHA-256 equalscontent_sha256, then gunzip + untar. Read the entrypoint first; never execute archive contents without review.
Security model
- The asset address proves integrity (hash-verified bytes), not authorship — anyone can mint anyone's code. Never attribute a package to a person from the address alone; check the update authority and treat it as an unverified wallet.
- Installing never executes anything. Review declared
permissionsand the entrypoint before use. Treat a hash mismatch as hostile, not as a retryable error. - Frozen snapshots are program-enforced immutable; mutable repo assets can be updated by their update authority at any time — pin a snapshot address if you need stability.
- Publisher attestation (cryptographically binding the minting wallet to a GitHub/OIDC identity, sigstore-style) is the planned v0.2.
Notes & limitations
- Public RPCs rate-limit and can race under load; the CLI retries with backoff. For
heavy publishing use a private RPC endpoint (
-u <url>). - Package size is bounded by Solana's 10 MiB account limit; practically, keep skills in the KB range — a skill is instructions plus small scripts, not a vendored tree.
ustarpath limit: file paths inside a package must be ≤ 100 characters.- A publish interrupted mid-staging leaves
.gitlana-publish-state.jsonwith the buffer account address so its rent can be reclaimed.
Example package
examples/onchain-skill-resolver/ is the skill
published at the mainnet address above — byte-identical: archive it with the CLI and
you'll reproduce content_sha256 33cb75ce…a4bc exactly.
About
Built by Tonbi. gitlana is an experiment in giving software the same properties as an on-chain asset: a permanent address, verifiable integrity, and an owner — so an AI agent can install a skill from a blockchain address alone, with no host in the loop. Contributions and issues welcome at github.com/tonbistudio/gitlana.
License
MIT © Tonbi
No comments yet
Be the first to share your take.