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
- 最近更新
- 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
- 参考文档reference/verification.md5.7 KB
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
--payand--max-feegates 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 ... | jqis 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 receiptdelivery: "enclave"reportspaid: 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_readymeans 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.
askstops at whateverrequestreturned. 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
verify-setup-resume
Verify BUI-936 setup/resume identities, funding readiness and overlapping resumes. Use when changing app request, test-credit transport or the request journal. Runs bounded TLA+ checks and offline real-code replays without live funding or paid reads.
verify-server-supervision
Verify the local Personal Server stop and restart boundary. Use when changing the supervisor's backoff, port probing, replacement launch or Stop handling.
verify-paid-read-recovery
Verify interrupted and overlapping CLI paid reads, receipt-file writers and dead-owner recovery. Use when changing src/cli/app/read.ts, src/core/receipts.ts or src/core/receipts-lock.ts. Runs TLA+ models and offline real-code replays without live paid reads.
verify-mainnet-registration
Verify mainnet app registration and Account association recovery. Use for the registration journal, gateway confirmation, retry, or ownership check.
verify-hosted-provisioning
Verify hosted Personal Server resource ownership across create and delete. Use for Connect's server API, provisioning-row updates, or deprovisioning admission.
verify-export-settlement
Verify queued export owner binding and generation settlement. Use for result publication, pending upload receipts, or retry and sync changes.