Skip to content

vana-com/vana-data-app-starter

Repository files navigation

Vana Data App Starter

A small Next.js app that turns approved linkedin.profile data into a LinkedIn profile snapshot.

It gives you two different proofs:

  • Local proof: the fixture, mapper, and UI work without Vana.
  • Live proof: a registered app identity requests and reads approved data from a Personal Server.

Passing the local proof does not mean the live path is configured.

Build the app locally

Use Node.js 22 or newer. This starter runs its backend routes in the Node.js runtime; it does not support the Edge runtime.

git clone https://github.com/vana-com/vana-data-app-starter.git
cd vana-data-app-starter
pnpm install
pnpm dev

Open the Local URL printed by Next.js. This is normally http://localhost:3000. If that port is occupied and you did not set PORT or pass --port, Next.js uses the next available port. Use the exact printed origin throughout setup. The initial profile is a fictional fixture, so this works without a private key, app identity, approval, or Personal Server read.

pnpm test
pnpm typecheck

Connect it to Vana

Create a registered app identity instead of making a private key by hand:

  1. Open the Developers page for the Vana environment you are using.
  2. Select the network.
  3. Enter the exact App URL the browser will use. For this starter, use the local origin printed by pnpm dev.
  4. Create the app identity and approve the wallet signature.
  5. Copy the generated environment values immediately. The Developers page shows the private key once.
Request lane Create the identity at Open the starter at
Production / Mainnet account.vana.org/developers, Mainnet <local-origin>
Production / Moksha account.vana.org/developers, Moksha <local-origin>?network=moksha
Dev / Moksha account-dev.vana.org/developers, Moksha <local-origin>?vana_env=dev&network=moksha

Put the generated values in .env.local:

VANA_APP_PRIVATE_KEY=0x...
VANA_APP_URL=http://localhost:3000

Replace the example origin with the exact local or deployed origin the browser uses, including a non-default port. Keep the private key server-only. VANA_APP_URL is server runtime configuration: the starter uses it for the app homepage, /connect/return, and signed request/session binding. It must match the registered App URL; never use a placeholder URL.

There is no separate callback-registration step.

Deploy the server

Deploy this as one long-lived Node.js 22+ process. Set VANA_APP_PRIVATE_KEY and VANA_APP_URL in the host's project environment for every deployed environment you use; .env.local is not uploaded. Set VANA_APP_URL to the fixed public origin that users open, then redeploy after adding or changing either value.

The starter's consume-once guarantee is process-local. Within one uninterrupted Node.js process, overlapping reads share one attempt and later repeats reuse the accepted result. It retains at most 100 active request entries and refuses new distinct reads at capacity rather than evicting charge protection. A restart or cold start loses retained results. Serverless isolates, workers, multiple processes, or multiple regions therefore require a shared atomic consume-once/result store before they can safely serve paid reads.

Choose the data scopes

A scope is a named category of data that an app requests, such as a LinkedIn profile or work history. Request only what the product needs and what the selected collection path can produce.

Browse the public Scope Coverage Registry to find other sources, their exact scope IDs, Web and Desktop availability, and connector maturity. Use the linked JSON schemas to inspect each scope's payload shape; for example, see the LinkedIn scope schemas.

Scope Data Collection path
linkedin.profile Profile identity, headline, summary, and location Public profile or deep import
linkedin.experience Roles, employers, and dates Public profile or deep import
linkedin.education Schools, degrees, and dates Public profile or deep import
linkedin.skills Listed skills Public profile or deep import
linkedin.languages Listed languages Public profile or deep import
linkedin.connections Connection list Deep authenticated desktop import only

This starter requests only linkedin.profile in src/lib/vana/constants.ts. If you change or add scopes, update the capability check, fixture, mapper, and product UI together.

The local fictional fixture previews profile, Work, Education, and Skills, but the current live request reads only linkedin.profile. The local preview is not a public protocol fixture and does not prove that richer live shape. BUI-727 owns the multi-scope transport, public fixtures, mapper, and product alignment. Until then, keep local product proof distinct from live protocol proof.

The public Scope Coverage Registry is generated from the machine-readable catalog owned by vana-com/data-connectors. Use it and its schemas as the discovery surface instead of inventing starter-local public fixture rules.

Run a live request

Start the app, open the URL matching the registered identity, and select Connect LinkedIn. Vana opens in a normal new browser tab, not a popup window.

App tab:  sample -> creating -> waiting -> reading -> approved profile
Vana tab: approve -> deliver data -> wait for the app to acknowledge the read

Keep the Vana tab open while it says data is being delivered. It may close after the app confirms the read. If the browser blocks the new tab, show an Open approval fallback.

/connect/return is a verified fallback and status surface, not the main product UI. It fetches authoritative status instead of trusting browser query parameters.

If Connect LinkedIn appears to do nothing, inspect POST /api/vana/request first. Missing environment values can fail request creation before the new tab receives its Vana URL.

Mainnet funding limitation

The app operator funds Direct reads. A supported zero-crypto, self-service Mainnet path is not yet proven; BUI-703 owns that funding product and its live proof. Do not invent a manual acquisition or bridging workflow in this starter. Keep escrow balances, fee assets, funding instructions, and raw server errors out of end-user UI. Show neutral availability and recovery copy instead.

Adapt the product

Keep the transport and session handling. Replace the app-owned source boundary:

  • src/lib/vana/constants.ts: request identity, source, and scope
  • src/data/linkedin-profile.fixture.ts: credential-free sample data
  • src/lib/linkedin-profile.ts: external payload to app view model
  • src/components/ProfileSnapshot.tsx: finished product surface
  • Product copy in src/components/LinkedInProfileApp.tsx

Preserve the product UI and replace only its Direct routes, session handling, fixture, and mapper.

Make idle, waiting, reading, ready, and error inspectable as pure views without a live request. A development-only fixture browser must branch before the Vana SDK mounts and must not open a tab, poll, read data, or touch escrow. Keep app-tab states separate from /connect/return states and label fixtures clearly.

Add Direct Vana to an existing app

If you already have a TypeScript Next.js App Router project using src/app and the @/* path alias, install the released transport bundle instead of forking:

npx shadcn@latest add vana-com/vana-data-app-starter/direct-vana-linkedin-next#v0.1.0

This uses shadcn only as a file-distribution registry. It does not require Tailwind, shadcn UI components, a cn utility, design tokens, or components.json.

Review these app-owned collision paths before installing:

src/lib/vana/*.ts
src/lib/linkedin-profile.ts
src/data/linkedin-profile.fixture.ts
src/components/linkedin-profile-copy.ts
src/app/api/vana/{request,status,read}/route.ts
src/app/connect/return/page.tsx
test/contract.test.ts

The item installs @opendatalabs/vana-sdk@3.13.4, server-only, tsx, and the safe consumer-copy helper exercised by its contract test. It does not install Next.js, React, layouts, styles, product components, environment values, or a package.json test script.

npx tsx --test test/contract.test.ts test/consume-once.test.ts

Transport boundary

The reusable bundle owns validation, request-session binding, sanitized server responses, and app-local result mapping:

src/lib/vana/{app-url,binding,capability,constants,consume-once,errors,request,response,return-state,runtime,server}.ts
src/app/api/vana/{request,status,read}/route.ts
src/app/connect/return/page.tsx
useDirectVanaConnect callbacks in src/components/LinkedInProfileApp.tsx

Do not add a second payment state machine, retry wrapper, caller-selected return URL, client-side private key, status/read runtime query parameters, raw SDK payload UI contract, or one shared cookie that overwrites concurrent requests. The Vana SDK owns Personal Server retries and 402 settlement.

Maintaining the registry

registry.json is the source manifest. Rebuild public/r/*.json whenever it or a transport file changes, then run the registry parity test:

pnpm registry:build
pnpm exec tsx --test test/registry.test.ts

Publication and tagging are a fresh post-merge task. Before creating a release tag, validate from merged main and repeat the clean consumer install using the immutable ref.

About

Canonical Next.js starter for Vana data apps

Resources

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors