AGENT SKILL

builder

Get permission for an app or agent to use a person's Vana data, read it, and verify the requested result. Use for consuming-app integration, including the developer's own collected data, grants, escrow, fees, saved app delivery and connector iteration. Source collection belongs to connect-data.

License 许可
MIT
所属仓库
vana-com/vana-cli
最近更新
2026-11-13
Stars
★ 2
Forks
0

01 前言 Front Matter

name
builder
description
Get permission for an app or agent to use a person's Vana data, read it, and verify the requested result. Use for consuming-app integration, including the developer's own collected data, grants, escrow, fees, saved app delivery and connector iteration. Source collection belongs to connect-data.

02 资源文件 1

03 SKILL.md 正文

Builder

Read workflows with vana skills show <id> --json. The response includes full instructions and required reference content; installation into the agent's harness is optional. Use connect-data, create-connector or builder as the requested goal needs them.

The vana app command group is the builder side of Vana: register once, ask a person for access, read what they approved, pay the fee from escrow. Every command takes --json and returns one outcome object; branch on the exit code, read code for the reason and remedy for the next command.

Start from the person's intended result and continue through the app handoff and verification they requested. Select the commands yourself; the person handles account choices, sign-in, consent and any spending decision. Use the intended app's existing backend key, owner, Account deployment and network. An app name does not select its credentials. Keep keys server-side and out of chat, browser code and saved reports.

For an app integration, read delivery and verification before requesting access. It covers readiness, real approval, bounded reads, durable storage, display, recovery and same-grant iteration for any consumer. The instructions and required references are public. The user's app supplies its own storage and UI; no reference app or observability service is required.

The loop

vana app register --network moksha --app-url http://localhost:3000 --app-name "<your name>" # create and test first
vana app request --network moksha --scopes spotify.savedTracks          # ask a person
vana app read --network moksha spotify.savedTracks --grant <id> --pay   # read and pay

Check vana app whoami --json first and reuse an appropriate registration. Register with the name the person should see on the approval page, such as your agent's name. The CLI remembers the name for this app key and falls back to "Vana CLI" when a request has no name. Use your app's HTTP(S) URL. A localhost URL works for testing. Vana Account needs the URL to claim the app, and the gateway registration keeps its original URL.

Or ask a question instead of reading raw data, in one command:

vana app ask "Which genres fill their saved tracks?" --network moksha \
  --sources spotify.savedTracks --derived myapp.genres --pay

request prints an approval URL and waits. The person opens it, approves, and the grant id comes back to the terminal. The read command returns data; an app integration must also verify its own save and display. The --pay examples require an agreed fee-asset limit before execution.

Exit codes

Branch on these instead of parsing text.

Code Meaning What to do
0 done continue
1 failed read message, do not retry blindly
2 bad usage fix the command
3 no grant, or it does not cover this vana app request again
4 payment required inspect the quote and obtain a spending limit before paying or funding
5 no server answered the owner's Personal Server is offline; not your fault, retry later
6 not ready yet wait and repeat, using any interval the payload names
7 a person has to act surface the URL to a human

Asking for access

vana app request --network moksha --scopes spotify.savedTracks,spotify.playlists --json

Waits up to ten minutes by default (--timeout <seconds>). On approval the outcome carries grantId and the exact read command in remedy.

One grant per owner and app: an approval replaces the grant's scopes, it does not add a second grant. So when this machine already holds an approval for the app key, request reads that live grant and asks for the union, and prints what it keeps, adds and removes before creating the request. Give up a scope with --remove-scopes a,b; pick whose grant to extend with --owner <address> when more than one person approved; send --scopes verbatim with --no-merge-grant. --json carries kept, added, removed, removeScopes and grantUnion.status (merged, no_live_grant, owner_unknown, owner_ambiguous, disabled, unavailable). With no earlier approval the person is unknown until they approve, and the approval page keeps what they already granted. --remove-scopes is always sent, even when the live grant could not be read; then it prints as "Removing (if shared)" and the approval page drops those entries if the grant holds them.

When no human is watching the terminal, do not block:

vana app request --network moksha --scopes spotify.savedTracks --no-input --json

That exits 7 immediately with approvalUrl in the payload. Hand the URL to the person however you can, then come back later:

vana app requests list --json            # find the id you did not keep
vana app requests show dcr_... --json    # did they approve yet?

show refreshes from the service and writes the result down, so once it reports a grantId that id stays available offline. Both commands check each grant at the gateway: data.grant.state is revoked or replaced (one grant per owner and app, so a later approval replaces its scopes) when it no longer holds, and unverified when the gateway did not answer.

If creation was interrupted, use the saved recovery command instead of creating another request. Builds with request --resume expose the saved setup ID through vana app requests list --json. Check vana app request --help before using the option on an older installed CLI. Resume preserves the app, environment, network and original payload; an unavailable service does not authorize a replacement request or another payment.

Asking a question instead of reading raw data

Prefer this when you want an answer rather than a dataset. The question is computed on the person's own server over sources your app never reads; you end up holding a grant on the answer only.

vana app ask "Which genres fill their saved tracks?" --network moksha \
  --sources spotify.savedTracks \
  --derived myapp.genres \
  --pay --json

ask runs the whole path for you: it asks the person (same approval URL and --no-input behavior as request), waits for the answer to settle, then reads it. The derived scope must be in your own namespace, never sharing a first segment with a source. Both --sources and --derived are required. The request grants the derived scope only; the sources go to the person's server as the question's inputs, so app request refuses a question whose source is also listed in --scopes.

Two commands let you drive the same path by hand:

vana app status myapp.genres --json     # is the answer coming, and when
vana app lineage myapp.genres --json    # what it was computed from

status finds the grant and server from your own request history, so you do not have to keep ids around. Its states map onto the exit codes: exit 6 means pending or recomputing, and the payload names the interval the server wants you to wait; exit 0 means ready and the remedy is the read command.

Do not poll status in a tight loop. It is free to you and it can trigger a recompute, which spends inference budget without reading anything. Use the interval the server names.

What it costs

Two separate costs, reported separately:

  • the compute itself reports computeCost: null, which means unpriced, not free; treat a number appearing there as a normal change
  • reading the derived scope is an ordinary billable read, with the same --pay and --max-fee gates as any other read

Reading

vana app read spotify.savedTracks --grant 0x... --json

Without --pay a priced read stops at exit 4 and tells you the cost, so you always see the price before spending:

{
  "code": "payment_required",
  "message": "This read costs 0.01 USDC.e and --pay is not set."
}

Add --pay to settle it from escrow, and --max-fee to refuse anything above a limit. The limit is in the fee's own asset, which is not always VANA: on mainnet a data-access fee is quoted in USDC.e.

vana app read spotify.savedTracks --grant 0x... --pay --max-fee 0.05 --json

Two things worth knowing:

  • The fee settles before data is served, so a blind retry after a failure can pay twice. The CLI stores the signed payment header and replays it, so re-running a failed read does not settle a second fee. Never work around this by looping.
  • In human mode the data goes to stdout alone and the summary to stderr, so vana app read ... | jq is clean.

Two delivery paths, one command

Some owners serve their data from a Personal Server they run; others from a TEE sandbox reached through the gateway's job queue. You never choose. read takes the path the access request reports and handles both.

What differs, if you see it in the output:

  • delivery: "personal_server" can cost a fee and produces a receipt
  • delivery: "enclave" reports paid: false, because the gateway currently admits those jobs at zero price; that is a protocol state, not a promise that enclave reads stay free
  • an enclave read can return exit 6 while the owner's sandbox wakes, which takes seconds. Run the same command again rather than treating it as a failure

Paying

vana app escrow balance --json
vana app escrow fund --amount 1 --json                      # native VANA
vana app escrow fund --amount 5 --asset 0xF1815... --json    # an ERC20, no gas

Fund the asset the reads are actually priced in, which the exit-4 payload names as asset. On mainnet, funding requires --yes and spends real money.

An ERC20 deposit (USDC.e) goes through the gateway relayer, which pays the gas, so the app wallet needs only the token, not VANA. --self-pay-gas sends it from the app wallet instead. A native VANA deposit always pays its own gas.

To link the app to the signed-in person's Account, run vana app claim --network <network> --json. If needed, use vana login first. Exit 7 returns data.claimUrl for the person to open. After they complete the existing Account page, run vana app claim --network <network> --resume --json. Only exit 0 with data.ownership: "owned" confirms the link. Pending or unavailable confirmation is not completion. The Account page also provides funding; claiming itself does not move funds.

Checking your work

vana app whoami --json                                   # who am I, registered?
vana app onchain spotify.savedTracks --owner 0x... --json    # version, hashes, deletion

Match the result to the user's goal using the verification procedure. A grant proves permission; a read proves receipt; neither proves the consuming app saved or displayed it. Verify those outcomes at the actual app. Keep collection, consent, reading, storage and display as separate observed results.

Networks

Everything defaults to mainnet, where fees are real USDC.e. A read never pays by itself: it stops at exit 4 with the price until you pass --pay, and --max-fee caps it. Pass --network moksha to try a flow on the testnet first, where fees are play money.

Failure notes an agent gets wrong

  • Exit 5 with owner_not_ready means the person never finished setting up a Personal Server. Retrying cannot fix it; they have to act.
  • Exit 5 otherwise means their server is offline right now. The CLI already tried every server they registered.
  • Exit 6 is normal, not a failure. Wait and repeat.
  • A denied request is exit 3 and is final. Do not re-ask in a loop.
  • ask stops at whatever request returned. Exit 7 there means the person has not approved yet, not that anything failed.
  • A derivative that fails with source_missing (exit 5) means the person has not connected a source yet. Retrying will not change that.

04 同仓库更多 Skills