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 gitlana for brevity. Until this is published to npm, run it from the repo as node src/cli.mjs …, or expose the gitlana command locally with npm link (then gitlana … 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: release mints a frozen snapshot whose parent attributes 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, so content_sha256 is 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 (including content_sha256 and content_length) followed by a gzip archive starting at the 1f 8b 08 magic bytes, content_length bytes long. Verify the slice's SHA-256 equals content_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 permissions and 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.
  • ustar path limit: file paths inside a package must be ≤ 100 characters.
  • A publish interrupted mid-staging leaves .gitlana-publish-state.json with 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