@opendatalabs/connect 0.4.1 → 0.5.0-canary.148baac
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
# @opendatalabs/connect
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Let users bring their own data into your app. One SDK call to request consent, one to fetch the data.
|
|
4
|
+
|
|
5
|
+
Users connect platforms they already use — ChatGPT, Instagram, Gmail, and more — through the [Vana Desktop App](https://www.vana.com/download), which keeps them in control of what's shared. Your app receives structured, user-consented data through a cryptographically verified grant. No scraping, no OAuth token juggling, no compliance gray areas.
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Your App Vana Desktop App
|
|
11
|
+
────────────────────────────── ──────────────────────────────
|
|
12
|
+
|
|
13
|
+
1. connect({ scopes })
|
|
14
|
+
→ creates session
|
|
15
|
+
→ returns deep link ──▶ 2. User opens deep link
|
|
16
|
+
sees requested scopes
|
|
17
|
+
approves or denies
|
|
18
|
+
|
|
19
|
+
3. Poll resolves with grant ◀── Grant signed on-chain
|
|
20
|
+
|
|
21
|
+
4. getData({ grant }) ──▶ 5. Personal Server returns
|
|
22
|
+
→ structured JSON user data over TLS
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The SDK handles session creation, cryptographic request signing, polling, and data fetching. You write three function calls; the protocol handles the rest.
|
|
26
|
+
|
|
27
|
+
## Where this fits in the Vana protocol
|
|
28
|
+
|
|
29
|
+
The [Data Portability Protocol](https://docs.vana.com) defines how users collect data from platforms, store it under their control (on-device or hosted), and grant third-party apps scoped access. The protocol participants are:
|
|
30
|
+
|
|
31
|
+
- **Personal Server** — stores and serves user data, enforces grants
|
|
32
|
+
- **Data Portability Gateway** — fast API with eventual on-chain consistency
|
|
33
|
+
- **Vana L1** — on-chain source of truth for grants, builder registry, and file records
|
|
34
|
+
|
|
35
|
+
**This SDK is the builder integration layer.** It sits between your app and the protocol, abstracting the Session Relay handshake, Web3Signed authentication, and Personal Server data fetching into a clean API.
|
|
4
36
|
|
|
5
37
|
## Installation
|
|
6
38
|
|
|
@@ -8,37 +40,48 @@ SDK for integrating Vana Data Portability "Connect data" flows into builder appl
|
|
|
8
40
|
npm install @opendatalabs/connect
|
|
9
41
|
```
|
|
10
42
|
|
|
11
|
-
|
|
43
|
+
Peer dependencies (installed automatically in most setups):
|
|
12
44
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
45
|
+
```bash
|
|
46
|
+
npm install viem # required for server-side signing
|
|
47
|
+
npm install react # only if using React hooks/components
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Prerequisites
|
|
51
|
+
|
|
52
|
+
Register as a builder through the Vana Desktop App:
|
|
53
|
+
|
|
54
|
+
1. Open **Desktop App** → **Builder Registration**
|
|
55
|
+
2. Enter your app's canonical URL
|
|
56
|
+
3. Save the generated private key — set it as `VANA_PRIVATE_KEY` in your server environment
|
|
57
|
+
4. Serve a [Web App Manifest](#web-app-manifest) at your app URL so the Desktop App can verify your identity
|
|
18
58
|
|
|
19
|
-
##
|
|
59
|
+
## Quickstart
|
|
20
60
|
|
|
21
|
-
### 1.
|
|
61
|
+
### 1. Create a session (server)
|
|
22
62
|
|
|
23
63
|
```typescript
|
|
24
64
|
import { connect } from "@opendatalabs/connect/server";
|
|
25
65
|
|
|
26
66
|
const session = await connect({
|
|
27
67
|
privateKey: process.env.VANA_PRIVATE_KEY as `0x${string}`,
|
|
28
|
-
scopes: ["
|
|
68
|
+
scopes: ["chatgpt.conversations"],
|
|
69
|
+
webhookUrl: "https://yourapp.com/api/webhook", // optional
|
|
70
|
+
appUserId: "user-42", // optional
|
|
29
71
|
});
|
|
30
72
|
|
|
31
|
-
//
|
|
32
|
-
// session.
|
|
33
|
-
// session.
|
|
73
|
+
// Return to your frontend:
|
|
74
|
+
// session.sessionId — used for polling
|
|
75
|
+
// session.deepLinkUrl — opens the Vana Desktop App
|
|
76
|
+
// session.expiresAt — ISO 8601 expiration
|
|
34
77
|
```
|
|
35
78
|
|
|
36
|
-
### 2. Poll for approval (client)
|
|
79
|
+
### 2. Poll for user approval (client)
|
|
37
80
|
|
|
38
81
|
```tsx
|
|
39
82
|
import { useVanaConnect } from "@opendatalabs/connect/react";
|
|
40
83
|
|
|
41
|
-
function
|
|
84
|
+
function ConnectData({ sessionId }: { sessionId: string }) {
|
|
42
85
|
const { connect, status, grant, deepLinkUrl } = useVanaConnect();
|
|
43
86
|
|
|
44
87
|
useEffect(() => {
|
|
@@ -46,72 +89,141 @@ function ConnectPage({ sessionId }: { sessionId: string }) {
|
|
|
46
89
|
}, [sessionId]);
|
|
47
90
|
|
|
48
91
|
if (status === "waiting" && deepLinkUrl) {
|
|
49
|
-
return <a href={deepLinkUrl}>
|
|
92
|
+
return <a href={deepLinkUrl}>Connect your data</a>;
|
|
50
93
|
}
|
|
51
|
-
|
|
52
94
|
if (status === "approved" && grant) {
|
|
53
|
-
|
|
95
|
+
// grant.grantId, grant.userAddress, grant.scopes are available
|
|
96
|
+
return <p>Connected.</p>;
|
|
54
97
|
}
|
|
55
|
-
|
|
56
|
-
return <p>Status: {status}</p>;
|
|
98
|
+
return <p>{status}</p>;
|
|
57
99
|
}
|
|
58
100
|
```
|
|
59
101
|
|
|
60
|
-
|
|
102
|
+
Or use the pre-built button:
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
import { ConnectButton } from "@opendatalabs/connect/react";
|
|
106
|
+
|
|
107
|
+
<ConnectButton
|
|
108
|
+
sessionId={sessionId}
|
|
109
|
+
onComplete={(grant) => saveGrant(grant)}
|
|
110
|
+
onError={(err) => console.error(err)}
|
|
111
|
+
/>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 3. Fetch user data (server)
|
|
61
115
|
|
|
62
116
|
```typescript
|
|
63
117
|
import { getData } from "@opendatalabs/connect/server";
|
|
64
118
|
|
|
65
119
|
const data = await getData({
|
|
66
120
|
privateKey: process.env.VANA_PRIVATE_KEY as `0x${string}`,
|
|
67
|
-
grant, // GrantPayload from
|
|
121
|
+
grant, // GrantPayload from step 2
|
|
68
122
|
});
|
|
69
123
|
|
|
70
|
-
//
|
|
124
|
+
// Map<string, unknown> keyed by scope
|
|
125
|
+
const conversations = data.get("chatgpt.conversations");
|
|
71
126
|
```
|
|
72
127
|
|
|
73
|
-
###
|
|
128
|
+
### Web App Manifest
|
|
74
129
|
|
|
75
|
-
|
|
76
|
-
// connect() options
|
|
77
|
-
await connect({
|
|
78
|
-
privateKey: "0x...",
|
|
79
|
-
scopes: ["instagram.dpv1"],
|
|
80
|
-
webhookUrl: "https://...", // optional webhook for session events
|
|
81
|
-
appUserId: "user-42", // optional app-level user ID
|
|
82
|
-
});
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### 4. Sign a web app manifest (server)
|
|
130
|
+
The Desktop App verifies your identity by fetching your [W3C Web App Manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest) and checking its `vana` block signature. Use `signVanaManifest()` to generate it:
|
|
86
131
|
|
|
87
132
|
```typescript
|
|
88
133
|
import { signVanaManifest } from "@opendatalabs/connect/server";
|
|
89
134
|
|
|
135
|
+
// In your manifest route handler (e.g. Next.js /manifest.json/route.ts):
|
|
90
136
|
const vanaBlock = await signVanaManifest({
|
|
91
137
|
privateKey: process.env.VANA_PRIVATE_KEY as `0x${string}`,
|
|
92
|
-
appUrl: "https://
|
|
93
|
-
privacyPolicyUrl: "https://
|
|
94
|
-
termsUrl: "https://
|
|
95
|
-
supportUrl: "https://
|
|
96
|
-
webhookUrl: "https://
|
|
138
|
+
appUrl: "https://yourapp.com",
|
|
139
|
+
privacyPolicyUrl: "https://yourapp.com/privacy",
|
|
140
|
+
termsUrl: "https://yourapp.com/terms",
|
|
141
|
+
supportUrl: "https://yourapp.com/support",
|
|
142
|
+
webhookUrl: "https://yourapp.com/api/webhook",
|
|
97
143
|
});
|
|
98
144
|
|
|
99
|
-
|
|
100
|
-
|
|
145
|
+
const manifest = {
|
|
146
|
+
name: "Your App",
|
|
147
|
+
short_name: "YourApp",
|
|
148
|
+
start_url: "/",
|
|
149
|
+
display: "standalone",
|
|
150
|
+
vana: vanaBlock, // signed identity block
|
|
151
|
+
};
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Make sure your HTML includes `<link rel="manifest" href="/manifest.json">`.
|
|
155
|
+
|
|
156
|
+
## API Reference
|
|
157
|
+
|
|
158
|
+
### Entrypoints
|
|
159
|
+
|
|
160
|
+
| Import | Environment | Exports |
|
|
161
|
+
| ------------------------------- | ----------- | ---------------------------------------------------------------- |
|
|
162
|
+
| `@opendatalabs/connect/server` | Node.js | `connect()`, `getData()`, `signVanaManifest()`, low-level clients |
|
|
163
|
+
| `@opendatalabs/connect/react` | Browser | `useVanaConnect()`, `ConnectButton` |
|
|
164
|
+
| `@opendatalabs/connect/core` | Universal | Types, `ConnectError`, constants |
|
|
165
|
+
|
|
166
|
+
### `connect(config): Promise<SessionInitResult>`
|
|
167
|
+
|
|
168
|
+
Creates a session on the Session Relay. Returns `sessionId`, `deepLinkUrl`, and `expiresAt`.
|
|
169
|
+
|
|
170
|
+
| Param | Type | Required | Description |
|
|
171
|
+
| ------------ | -------------- | -------- | ----------------------------------- |
|
|
172
|
+
| `privateKey` | `` `0x${string}` `` | Yes | Builder private key |
|
|
173
|
+
| `scopes` | `string[]` | Yes | Data scopes to request |
|
|
174
|
+
| `webhookUrl` | `string` | No | URL for grant event notifications |
|
|
175
|
+
| `appUserId` | `string` | No | Your app's user ID for correlation |
|
|
176
|
+
|
|
177
|
+
### `getData(config): Promise<Map<string, unknown>>`
|
|
178
|
+
|
|
179
|
+
Fetches user data from their Personal Server using a signed grant.
|
|
180
|
+
|
|
181
|
+
| Param | Type | Required | Description |
|
|
182
|
+
| ------------ | -------------- | -------- | ------------------------------- |
|
|
183
|
+
| `privateKey` | `` `0x${string}` `` | Yes | Builder private key |
|
|
184
|
+
| `grant` | `GrantPayload` | Yes | Grant from the approval step |
|
|
185
|
+
|
|
186
|
+
### `useVanaConnect(config?): UseVanaConnectResult`
|
|
187
|
+
|
|
188
|
+
React hook that polls the Session Relay and manages connection state.
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
const { connect, status, grant, error, deepLinkUrl, reset } = useVanaConnect();
|
|
101
192
|
```
|
|
102
193
|
|
|
103
|
-
|
|
194
|
+
`status` transitions: `idle` → `connecting` → `waiting` → `approved` | `denied` | `expired` | `error`
|
|
104
195
|
|
|
105
|
-
|
|
196
|
+
### `GrantPayload`
|
|
197
|
+
|
|
198
|
+
Returned when a user approves access:
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
interface GrantPayload {
|
|
202
|
+
grantId: string; // on-chain permission ID
|
|
203
|
+
userAddress: string; // user's wallet address
|
|
204
|
+
builderAddress: string; // your registered address
|
|
205
|
+
scopes: string[]; // approved data scopes
|
|
206
|
+
serverAddress?: string; // user's Personal Server
|
|
207
|
+
appUserId?: string; // your app's user ID (if provided)
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Low-level clients
|
|
212
|
+
|
|
213
|
+
For full control over individual protocol interactions:
|
|
106
214
|
|
|
107
215
|
```typescript
|
|
108
216
|
import {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
217
|
+
createRequestSigner, // Web3Signed header generation
|
|
218
|
+
createSessionRelay, // Session Relay HTTP client
|
|
219
|
+
createDataClient, // Data Gateway HTTP client
|
|
112
220
|
} from "@opendatalabs/connect/server";
|
|
113
221
|
```
|
|
114
222
|
|
|
223
|
+
## Examples
|
|
224
|
+
|
|
225
|
+
See [`examples/nextjs-starter`](./examples/nextjs-starter) for a complete working integration with Next.js, including manifest signing, webhook handling, and the full connect-to-data-fetch flow.
|
|
226
|
+
|
|
115
227
|
## License
|
|
116
228
|
|
|
117
229
|
MIT
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useVanaConnect.d.ts","sourceRoot":"","sources":["../../src/react/useVanaConnect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EAEb,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,oBAAoB;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACvE,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,wBAAgB,cAAc,CAC5B,MAAM,CAAC,EAAE,oBAAoB,GAC5B,oBAAoB,
|
|
1
|
+
{"version":3,"file":"useVanaConnect.d.ts","sourceRoot":"","sources":["../../src/react/useVanaConnect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EAEb,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,oBAAoB;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACvE,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,wBAAgB,cAAc,CAC5B,MAAM,CAAC,EAAE,oBAAoB,GAC5B,oBAAoB,CAmGtB"}
|
|
@@ -32,8 +32,17 @@ export function useVanaConnect(config) {
|
|
|
32
32
|
try {
|
|
33
33
|
const res = await fetch(pollUrl);
|
|
34
34
|
if (!res.ok) {
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
// Try to extract structured error from response body
|
|
36
|
+
const body = await res.json().catch(() => null);
|
|
37
|
+
const errorCode = body?.error?.errorCode;
|
|
38
|
+
if (res.status === 410 || errorCode === "SESSION_EXPIRED") {
|
|
39
|
+
setStatus("expired");
|
|
40
|
+
setError("Session expired");
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
setStatus("error");
|
|
44
|
+
setError(`Poll failed: ${res.status}`);
|
|
45
|
+
}
|
|
37
46
|
stopPolling();
|
|
38
47
|
return;
|
|
39
48
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useVanaConnect.js","sourceRoot":"","sources":["../../src/react/useVanaConnect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAoBzD,MAAM,UAAU,cAAc,CAC5B,MAA6B;IAE7B,MAAM,OAAO,GAAG,iBAAiB,CAAC;IAClC,MAAM,QAAQ,GAAG,MAAM,EAAE,eAAe,IAAI,IAAI,CAAC;IAEjD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAmB,MAAM,CAAC,CAAC;IAC/D,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAsB,IAAI,CAAC,CAAC;IAC9D,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACpE,MAAM,QAAQ,GAAG,MAAM,CAAwC,IAAI,CAAC,CAAC;IAErE,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;QACnC,IAAI,QAAQ,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YAC9B,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAChC,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,WAAW,EAAE,CAAC;QACd,SAAS,CAAC,MAAM,CAAC,CAAC;QAClB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,cAAc,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAElB,MAAM,OAAO,GAAG,WAAW,CACzB,CAAC,MAAmD,EAAE,EAAE;QACtD,WAAW,EAAE,CAAC;QACd,SAAS,CAAC,YAAY,CAAC,CAAC;QACxB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,cAAc,CACZ,MAAM,CAAC,WAAW,IAAI,4BAA4B,MAAM,CAAC,SAAS,EAAE,CACrE,CAAC;QAEF,MAAM,OAAO,GAAG,GAAG,OAAO,eAAe,MAAM,CAAC,SAAS,OAAO,CAAC;QAEjE,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE;YACtB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC;gBACjC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;oBACZ,SAAS,CAAC,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"useVanaConnect.js","sourceRoot":"","sources":["../../src/react/useVanaConnect.ts"],"names":[],"mappings":"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"}
|