@opendatalabs/personal-server-ts-server 0.2.0-canary.6e65a3c → 1.0.1

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.
Files changed (177) hide show
  1. package/dist/api-auth.d.ts +16 -0
  2. package/dist/api-auth.d.ts.map +1 -0
  3. package/dist/api-auth.js +97 -0
  4. package/dist/api-auth.js.map +1 -0
  5. package/dist/api.d.ts +4 -0
  6. package/dist/api.d.ts.map +1 -1
  7. package/dist/api.js +3 -0
  8. package/dist/api.js.map +1 -1
  9. package/dist/app.d.ts +46 -1
  10. package/dist/app.d.ts.map +1 -1
  11. package/dist/app.js +102 -30
  12. package/dist/app.js.map +1 -1
  13. package/dist/bootstrap.d.ts +11 -3
  14. package/dist/bootstrap.d.ts.map +1 -1
  15. package/dist/bootstrap.js +266 -33
  16. package/dist/bootstrap.js.map +1 -1
  17. package/dist/client.d.ts +17 -0
  18. package/dist/client.d.ts.map +1 -0
  19. package/dist/client.js +368 -0
  20. package/dist/client.js.map +1 -0
  21. package/dist/config/defaults.d.ts +12 -0
  22. package/dist/config/defaults.d.ts.map +1 -0
  23. package/dist/config/defaults.js +14 -0
  24. package/dist/config/defaults.js.map +1 -0
  25. package/dist/config/index.d.ts +4 -0
  26. package/dist/config/index.d.ts.map +1 -0
  27. package/dist/config/index.js +4 -0
  28. package/dist/config/index.js.map +1 -0
  29. package/dist/config/loader.d.ts +8 -0
  30. package/dist/config/loader.d.ts.map +1 -0
  31. package/dist/config/loader.js +74 -0
  32. package/dist/config/loader.js.map +1 -0
  33. package/dist/config/paths.d.ts +9 -0
  34. package/dist/config/paths.d.ts.map +1 -0
  35. package/dist/config/paths.js +22 -0
  36. package/dist/config/paths.js.map +1 -0
  37. package/dist/hono-context.d.ts +14 -0
  38. package/dist/hono-context.d.ts.map +1 -0
  39. package/dist/hono-context.js +2 -0
  40. package/dist/hono-context.js.map +1 -0
  41. package/dist/index.js +64 -12
  42. package/dist/index.js.map +1 -1
  43. package/dist/keys/server-account.d.ts +28 -0
  44. package/dist/keys/server-account.d.ts.map +1 -0
  45. package/dist/keys/server-account.js +48 -0
  46. package/dist/keys/server-account.js.map +1 -0
  47. package/dist/logger/index.d.ts +5 -0
  48. package/dist/logger/index.d.ts.map +1 -0
  49. package/dist/logger/index.js +9 -0
  50. package/dist/logger/index.js.map +1 -0
  51. package/dist/logging/access-log.d.ts +15 -0
  52. package/dist/logging/access-log.d.ts.map +1 -0
  53. package/dist/logging/access-log.js +25 -0
  54. package/dist/logging/access-log.js.map +1 -0
  55. package/dist/logging/access-reader.d.ts +19 -0
  56. package/dist/logging/access-reader.d.ts.map +1 -0
  57. package/dist/logging/access-reader.js +50 -0
  58. package/dist/logging/access-reader.js.map +1 -0
  59. package/dist/middleware/access-log.d.ts.map +1 -1
  60. package/dist/middleware/access-log.js +8 -2
  61. package/dist/middleware/access-log.js.map +1 -1
  62. package/dist/middleware/builder-check.d.ts +2 -2
  63. package/dist/middleware/builder-check.d.ts.map +1 -1
  64. package/dist/middleware/builder-check.js +7 -2
  65. package/dist/middleware/builder-check.js.map +1 -1
  66. package/dist/middleware/owner-check.js +1 -1
  67. package/dist/middleware/owner-check.js.map +1 -1
  68. package/dist/middleware/web3-auth.d.ts +12 -0
  69. package/dist/middleware/web3-auth.d.ts.map +1 -1
  70. package/dist/middleware/web3-auth.js +27 -31
  71. package/dist/middleware/web3-auth.js.map +1 -1
  72. package/dist/migrations/local-state.d.ts +21 -0
  73. package/dist/migrations/local-state.d.ts.map +1 -0
  74. package/dist/migrations/local-state.js +106 -0
  75. package/dist/migrations/local-state.js.map +1 -0
  76. package/dist/routes/access-logs.d.ts +7 -0
  77. package/dist/routes/access-logs.d.ts.map +1 -1
  78. package/dist/routes/access-logs.js +11 -17
  79. package/dist/routes/access-logs.js.map +1 -1
  80. package/dist/routes/auth-device.d.ts +49 -0
  81. package/dist/routes/auth-device.d.ts.map +1 -0
  82. package/dist/routes/auth-device.js +581 -0
  83. package/dist/routes/auth-device.js.map +1 -0
  84. package/dist/routes/data.d.ts +42 -1
  85. package/dist/routes/data.d.ts.map +1 -1
  86. package/dist/routes/data.js +36 -201
  87. package/dist/routes/data.js.map +1 -1
  88. package/dist/routes/grants.d.ts +9 -2
  89. package/dist/routes/grants.d.ts.map +1 -1
  90. package/dist/routes/grants.js +18 -196
  91. package/dist/routes/grants.js.map +1 -1
  92. package/dist/routes/health.d.ts +7 -1
  93. package/dist/routes/health.d.ts.map +1 -1
  94. package/dist/routes/health.js +39 -2
  95. package/dist/routes/health.js.map +1 -1
  96. package/dist/routes/mcp.d.ts +93 -6
  97. package/dist/routes/mcp.d.ts.map +1 -1
  98. package/dist/routes/mcp.js +526 -53
  99. package/dist/routes/mcp.js.map +1 -1
  100. package/dist/routes/oauth-token.d.ts +56 -0
  101. package/dist/routes/oauth-token.d.ts.map +1 -0
  102. package/dist/routes/oauth-token.js +49 -0
  103. package/dist/routes/oauth-token.js.map +1 -0
  104. package/dist/routes/sync.d.ts +7 -0
  105. package/dist/routes/sync.d.ts.map +1 -1
  106. package/dist/routes/sync.js +12 -39
  107. package/dist/routes/sync.js.map +1 -1
  108. package/dist/routes/ui-config.d.ts.map +1 -1
  109. package/dist/routes/ui-config.js +17 -82
  110. package/dist/routes/ui-config.js.map +1 -1
  111. package/dist/routes/ui-registration.d.ts +9 -0
  112. package/dist/routes/ui-registration.d.ts.map +1 -0
  113. package/dist/routes/ui-registration.js +217 -0
  114. package/dist/routes/ui-registration.js.map +1 -0
  115. package/dist/routes/ui.d.ts +1 -0
  116. package/dist/routes/ui.d.ts.map +1 -1
  117. package/dist/routes/ui.js +46 -4
  118. package/dist/routes/ui.js.map +1 -1
  119. package/dist/storage/hierarchy.d.ts +29 -0
  120. package/dist/storage/hierarchy.d.ts.map +1 -0
  121. package/dist/storage/hierarchy.js +241 -0
  122. package/dist/storage/hierarchy.js.map +1 -0
  123. package/dist/storage/index-manager.d.ts +53 -0
  124. package/dist/storage/index-manager.d.ts.map +1 -0
  125. package/dist/storage/index-manager.js +167 -0
  126. package/dist/storage/index-manager.js.map +1 -0
  127. package/dist/storage/index-schema.d.ts +5 -0
  128. package/dist/storage/index-schema.d.ts.map +1 -0
  129. package/dist/storage/index-schema.js +62 -0
  130. package/dist/storage/index-schema.js.map +1 -0
  131. package/dist/storage/node-data-storage.d.ts +9 -0
  132. package/dist/storage/node-data-storage.d.ts.map +1 -0
  133. package/dist/storage/node-data-storage.js +84 -0
  134. package/dist/storage/node-data-storage.js.map +1 -0
  135. package/dist/sync-cursor.d.ts +15 -0
  136. package/dist/sync-cursor.d.ts.map +1 -0
  137. package/dist/sync-cursor.js +110 -0
  138. package/dist/sync-cursor.js.map +1 -0
  139. package/dist/token-store.d.ts +22 -0
  140. package/dist/token-store.d.ts.map +1 -0
  141. package/dist/token-store.js +120 -0
  142. package/dist/token-store.js.map +1 -0
  143. package/dist/tunnel/auth.d.ts +4 -0
  144. package/dist/tunnel/auth.d.ts.map +1 -1
  145. package/dist/tunnel/auth.js +2 -1
  146. package/dist/tunnel/auth.js.map +1 -1
  147. package/dist/tunnel/config.d.ts +1 -0
  148. package/dist/tunnel/config.d.ts.map +1 -1
  149. package/dist/tunnel/config.js +1 -1
  150. package/dist/tunnel/config.js.map +1 -1
  151. package/dist/tunnel/manager.d.ts +21 -1
  152. package/dist/tunnel/manager.d.ts.map +1 -1
  153. package/dist/tunnel/manager.js +78 -23
  154. package/dist/tunnel/manager.js.map +1 -1
  155. package/dist/tunnel/verify.d.ts +1 -1
  156. package/dist/tunnel/verify.d.ts.map +1 -1
  157. package/dist/tunnel/verify.js +8 -2
  158. package/dist/tunnel/verify.js.map +1 -1
  159. package/dist/ui/browser_tls_rustls_bg.wasm +0 -0
  160. package/dist/ui/index.html +2192 -0
  161. package/dist/ui/ps-lite-debug.d.ts +93 -0
  162. package/dist/ui/ps-lite-debug.d.ts.map +1 -0
  163. package/dist/ui/ps-lite-debug.js +129415 -0
  164. package/dist/ui/ps-lite-debug.js.map +1 -0
  165. package/package.json +20 -8
  166. package/dist/middleware/grant-check.d.ts +0 -12
  167. package/dist/middleware/grant-check.d.ts.map +0 -1
  168. package/dist/middleware/grant-check.js +0 -90
  169. package/dist/middleware/grant-check.js.map +0 -1
  170. package/dist/oauth/provider.d.ts +0 -55
  171. package/dist/oauth/provider.d.ts.map +0 -1
  172. package/dist/oauth/provider.js +0 -160
  173. package/dist/oauth/provider.js.map +0 -1
  174. package/dist/oauth/routes.d.ts +0 -10
  175. package/dist/oauth/routes.d.ts.map +0 -1
  176. package/dist/oauth/routes.js +0 -206
  177. package/dist/oauth/routes.js.map +0 -1
@@ -0,0 +1,581 @@
1
+ /**
2
+ * Login Flow v2 — Nextcloud-style device login for self-hosted CLI authentication.
3
+ *
4
+ * POST /auth/device — Initiate a login session
5
+ * GET /auth/device/poll — Poll for completion (token= query param)
6
+ * GET /auth/device/approve — HTML approval page (session= query param)
7
+ * POST /auth/device/approve — Approve the session (session= query param)
8
+ */
9
+ // Load the Hono ContextVariableMap augmentation (auth/authMechanism/etc.) so
10
+ // c.get("authMechanism") is typed in this route's compilation.
11
+ import "../hono-context.js";
12
+ import { randomBytes } from "node:crypto";
13
+ import { Hono } from "hono";
14
+ import { verifyWeb3Signed, } from "@opendatalabs/vana-sdk/node";
15
+ import { approveDeviceSessionContract, createMemoryDeviceSessionStore, DEVICE_POLL_INTERVAL_MS, DEVICE_SESSION_TTL_MS, initiateDeviceSessionContract, pollDeviceSessionContract, provisionDeviceTokenContract, revokeDeviceTokenContract, } from "@opendatalabs/personal-server-ts-core/contracts";
16
+ import { NotOwnerError, ProtocolError, } from "@opendatalabs/personal-server-ts-core/errors";
17
+ import { createWeb3AuthMiddleware, mapSdkAuthError, } from "../middleware/web3-auth.js";
18
+ import { createOwnerCheckMiddleware } from "../middleware/owner-check.js";
19
+ function createInspectableDeviceSessionStore() {
20
+ const store = createMemoryDeviceSessionStore();
21
+ const sessionIds = new Set();
22
+ return {
23
+ create(input) {
24
+ const session = store.create(input);
25
+ sessionIds.add(session.sessionId);
26
+ return session;
27
+ },
28
+ get(sessionId) {
29
+ return store.get(sessionId);
30
+ },
31
+ findByPollToken(pollToken) {
32
+ return store.findByPollToken(pollToken);
33
+ },
34
+ delete(sessionId) {
35
+ store.delete(sessionId);
36
+ sessionIds.delete(sessionId);
37
+ },
38
+ purgeExpired(now) {
39
+ store.purgeExpired(now);
40
+ for (const sessionId of Array.from(sessionIds)) {
41
+ if (!store.get(sessionId))
42
+ sessionIds.delete(sessionId);
43
+ }
44
+ },
45
+ clear() {
46
+ for (const sessionId of Array.from(sessionIds)) {
47
+ store.delete(sessionId);
48
+ }
49
+ sessionIds.clear();
50
+ },
51
+ get size() {
52
+ return sessionIds.size;
53
+ },
54
+ has(sessionId) {
55
+ return store.get(sessionId) !== undefined;
56
+ },
57
+ };
58
+ }
59
+ /** In-memory session store (short-lived device authorization state). */
60
+ const sessions = createInspectableDeviceSessionStore();
61
+ function getRequestOrigin(c) {
62
+ return new URL(c.req.url).origin;
63
+ }
64
+ function getRequestPath(c) {
65
+ return new URL(c.req.url).pathname;
66
+ }
67
+ function resolveOrigin(origin) {
68
+ return typeof origin === "function" ? origin() : origin;
69
+ }
70
+ function isLocalhostRequest(remoteAddr) {
71
+ if (!remoteAddr)
72
+ return false;
73
+ return (remoteAddr === "127.0.0.1" ||
74
+ remoteAddr === "::1" ||
75
+ remoteAddr === "localhost" ||
76
+ remoteAddr.startsWith("127.") ||
77
+ remoteAddr === "::ffff:127.0.0.1");
78
+ }
79
+ function isLoopbackHost(hostname) {
80
+ return (hostname === "localhost" ||
81
+ hostname === "::1" ||
82
+ hostname === "127.0.0.1" ||
83
+ hostname.startsWith("127."));
84
+ }
85
+ function isLocalhostOrigin(origin) {
86
+ try {
87
+ return isLoopbackHost(new URL(origin).hostname);
88
+ }
89
+ catch {
90
+ return false;
91
+ }
92
+ }
93
+ function getSocketRemoteAddress(c) {
94
+ // Access Node.js socket via Hono's env bindings when running on the real server.
95
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
96
+ return c.env?.incoming?.socket?.remoteAddress;
97
+ }
98
+ function usesLocalApprovalChannel(c, deps) {
99
+ const localApprovalOrigin = resolveOrigin(deps.localApprovalOrigin);
100
+ if (!localApprovalOrigin) {
101
+ return false;
102
+ }
103
+ return getRequestOrigin(c) === localApprovalOrigin;
104
+ }
105
+ function getApprovalOrigin(c, deps) {
106
+ const requestOrigin = getRequestOrigin(c);
107
+ const localApprovalOrigin = resolveOrigin(deps.localApprovalOrigin);
108
+ if (localApprovalOrigin && isLocalhostOrigin(requestOrigin)) {
109
+ return localApprovalOrigin;
110
+ }
111
+ return requestOrigin;
112
+ }
113
+ async function verifyRemoteOwnerApproval(c, serverOwner) {
114
+ if (!serverOwner) {
115
+ return c.json({
116
+ error: {
117
+ code: 500,
118
+ errorCode: "SERVER_NOT_CONFIGURED",
119
+ message: "Server owner address not configured. Set VANA_MASTER_KEY_SIGNATURE environment variable.",
120
+ },
121
+ }, 500);
122
+ }
123
+ const authHeader = c.req.header("authorization");
124
+ if (!authHeader) {
125
+ return c.json({
126
+ error: {
127
+ code: 403,
128
+ message: "Remote approval requires owner wallet authentication. " +
129
+ "Connect the server owner wallet and try again.",
130
+ },
131
+ }, 403);
132
+ }
133
+ try {
134
+ const auth = await verifyWeb3Signed({
135
+ headerValue: authHeader,
136
+ expectedOrigin: getRequestOrigin(c),
137
+ expectedMethod: c.req.method,
138
+ expectedPath: getRequestPath(c),
139
+ bodyBytes: c.req.method === "GET" || c.req.method === "HEAD"
140
+ ? undefined
141
+ : new Uint8Array(await c.req.raw.clone().arrayBuffer()),
142
+ });
143
+ if (auth.signer.toLowerCase() !== serverOwner.toLowerCase()) {
144
+ const err = new NotOwnerError({
145
+ signer: auth.signer,
146
+ expected: serverOwner,
147
+ });
148
+ return c.json(err.toJSON(), 401);
149
+ }
150
+ return auth;
151
+ }
152
+ catch (err) {
153
+ const authError = mapSdkAuthError(err);
154
+ if (authError)
155
+ return c.json(authError.toJSON(), authError.code);
156
+ if (err instanceof ProtocolError) {
157
+ return c.json(err.toJSON(), err.code);
158
+ }
159
+ throw err;
160
+ }
161
+ }
162
+ export function authDeviceRoutes(deps) {
163
+ const app = new Hono();
164
+ const allowInteractiveLogin = deps.allowInteractiveLogin !== false;
165
+ const web3Auth = createWeb3AuthMiddleware({
166
+ serverOrigin: deps.serverOrigin,
167
+ devToken: deps.devToken,
168
+ accessToken: deps.accessToken,
169
+ tokenStore: deps.tokenStore,
170
+ serverOwner: deps.serverOwner,
171
+ });
172
+ const ownerCheck = createOwnerCheckMiddleware(deps.serverOwner);
173
+ if (allowInteractiveLogin) {
174
+ // POST / — Initiate login flow (no auth required)
175
+ app.post("/", (c) => {
176
+ const sessionId = randomBytes(32).toString("hex");
177
+ const pollToken = randomBytes(32).toString("hex");
178
+ const result = initiateDeviceSessionContract({
179
+ sessionStore: sessions,
180
+ serverOwner: deps.serverOwner,
181
+ requestOrigin: getRequestOrigin(c),
182
+ approvalOrigin: getApprovalOrigin(c, deps),
183
+ sessionId,
184
+ pollToken,
185
+ now: Date.now(),
186
+ });
187
+ deps.logger.info({ sessionId }, "Login flow initiated");
188
+ return c.json(result.body, result.status);
189
+ });
190
+ // GET /poll — Poll for completion
191
+ app.get("/poll", (c) => {
192
+ const result = pollDeviceSessionContract({
193
+ sessionStore: sessions,
194
+ pollToken: c.req.query("token") ?? null,
195
+ serverOwner: deps.serverOwner,
196
+ now: Date.now(),
197
+ });
198
+ if (result.status === 200) {
199
+ const body = result.body;
200
+ deps.logger.info({ server: body.server }, "Login flow completed — token delivered");
201
+ }
202
+ return c.json(result.body, result.status);
203
+ });
204
+ // GET /approve — HTML approval page
205
+ app.get("/approve", (c) => {
206
+ sessions.purgeExpired(Date.now());
207
+ const sessionId = c.req.query("session");
208
+ if (!sessionId) {
209
+ return c.html(errorPage("Missing session parameter"), 400);
210
+ }
211
+ const session = sessions.get(sessionId);
212
+ if (!session) {
213
+ return c.html(errorPage("Session expired or invalid"), 404);
214
+ }
215
+ if (session.status === "approved") {
216
+ return c.html(successPage());
217
+ }
218
+ const owner = deps.serverOwner ?? "unknown";
219
+ const localApprovalShortcutEnabled = usesLocalApprovalChannel(c, deps);
220
+ return c.html(approvePage(session.requestedServerOrigin, owner, sessionId, localApprovalShortcutEnabled));
221
+ });
222
+ // POST /approve — Approve the session
223
+ app.post("/approve", async (c) => {
224
+ sessions.purgeExpired(Date.now());
225
+ const sessionId = c.req.query("session");
226
+ if (!sessionId) {
227
+ return c.json({ error: { code: 400, message: "Missing session parameter" } }, 400);
228
+ }
229
+ const session = sessions.get(sessionId);
230
+ if (!session) {
231
+ return c.json({ error: { code: 404, message: "Session expired or invalid" } }, 404);
232
+ }
233
+ if (session.status === "approved") {
234
+ return c.json({ status: "already_approved" });
235
+ }
236
+ // v1: localhost auto-approve — user is physically at the server.
237
+ // SECURITY: Use the direct TCP peer address, NOT X-Forwarded-For
238
+ // or X-Real-IP headers which are trivially spoofable by remote attackers.
239
+ const remoteAddr = deps.getRemoteAddress?.(c) ?? getSocketRemoteAddress(c);
240
+ const allowLocalShortcut = usesLocalApprovalChannel(c, deps) &&
241
+ isLocalhostRequest(remoteAddr);
242
+ if (!allowLocalShortcut) {
243
+ const approvalAuth = await verifyRemoteOwnerApproval(c, deps.serverOwner);
244
+ if (approvalAuth instanceof Response) {
245
+ deps.logger.warn({ remoteAddr, sessionId }, "Remote approval rejected");
246
+ return approvalAuth;
247
+ }
248
+ deps.logger.info({ remoteAddr, sessionId, signer: approvalAuth.signer }, "Remote approval authenticated with owner wallet");
249
+ }
250
+ const accessToken = `vana_ps_${randomBytes(32).toString("hex")}`;
251
+ const result = await approveDeviceSessionContract({
252
+ sessionStore: sessions,
253
+ tokenStore: deps.tokenStore,
254
+ sessionId,
255
+ serverOwner: deps.serverOwner,
256
+ accessToken,
257
+ now: Date.now(),
258
+ });
259
+ deps.logger.info({ sessionId }, "Login flow approved — token generated");
260
+ return c.json(result.body, result.status);
261
+ });
262
+ }
263
+ // POST /token — add a CLI token to the store (control-plane authenticated)
264
+ app.post("/token", web3Auth, ownerCheck, async (c) => {
265
+ if (c.get("authMechanism") !== "control-plane-token") {
266
+ return c.json({
267
+ error: {
268
+ code: 403,
269
+ message: "Only control-plane tokens can provision Personal Server session tokens",
270
+ },
271
+ }, 403);
272
+ }
273
+ let body;
274
+ try {
275
+ body = await c.req.json();
276
+ }
277
+ catch {
278
+ return c.json({ error: { code: 400, message: "Request body must be valid JSON" } }, 400);
279
+ }
280
+ const result = await provisionDeviceTokenContract({
281
+ tokenStore: deps.tokenStore,
282
+ body,
283
+ now: Date.now(),
284
+ });
285
+ if (result.status !== 201) {
286
+ return c.json(result.body, result.status);
287
+ }
288
+ deps.logger.info("CLI token added via POST /token");
289
+ return c.json(result.body, 201);
290
+ });
291
+ // ── Token revocation ──────────────────────────────────────────────
292
+ // Called by `vana logout` to invalidate the token server-side.
293
+ app.delete("/token", async (c) => {
294
+ const authHeader = c.req.header("authorization");
295
+ const result = await revokeDeviceTokenContract({
296
+ tokenStore: deps.tokenStore,
297
+ bearerToken: authHeader?.startsWith("Bearer ")
298
+ ? authHeader.slice(7)
299
+ : null,
300
+ });
301
+ if (result.status !== 200) {
302
+ return c.json(result.body, result.status);
303
+ }
304
+ deps.logger.info("Token revoked via DELETE /token");
305
+ return c.json(result.body);
306
+ });
307
+ return app;
308
+ }
309
+ // ── HTML pages ──────────────────────────────────────────────────────
310
+ function approvePage(origin, owner, sessionId, localApprovalShortcutEnabled) {
311
+ return `<!DOCTYPE html>
312
+ <html lang="en">
313
+ <head>
314
+ <meta charset="UTF-8">
315
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
316
+ <title>Authorize CLI — Personal Server</title>
317
+ <style>
318
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
319
+ body {
320
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
321
+ background: #f5f5f5; color: #1a1a1a;
322
+ display: flex; justify-content: center; align-items: center;
323
+ min-height: 100vh; padding: 1rem;
324
+ }
325
+ .card {
326
+ background: #fff; border-radius: 12px; padding: 2rem;
327
+ max-width: 420px; width: 100%;
328
+ box-shadow: 0 2px 8px rgba(0,0,0,.08);
329
+ }
330
+ h1 { font-size: 1.25rem; margin-bottom: 1.5rem; }
331
+ .info { background: #f8f9fa; border-radius: 8px; padding: 1rem; margin-bottom: 1.5rem; font-size: 0.875rem; }
332
+ .info dt { color: #666; margin-bottom: 0.25rem; }
333
+ .info dd { font-family: monospace; word-break: break-all; margin-bottom: 0.75rem; }
334
+ .info dd:last-child { margin-bottom: 0; }
335
+ .btn {
336
+ display: block; width: 100%; padding: 0.75rem 1.5rem;
337
+ background: #2563eb; color: #fff; border: none; border-radius: 8px;
338
+ font-size: 1rem; font-weight: 500; cursor: pointer;
339
+ transition: background 0.15s;
340
+ }
341
+ .btn:hover { background: #1d4ed8; }
342
+ .btn:disabled { background: #94a3b8; cursor: not-allowed; }
343
+ #status { text-align: center; margin-top: 1rem; font-size: 0.875rem; color: #666; }
344
+ .hint { margin-top: 1rem; font-size: 0.875rem; color: #666; line-height: 1.5; }
345
+ </style>
346
+ </head>
347
+ <body>
348
+ <div class="card">
349
+ <h1>A CLI tool is requesting access to your Personal Server</h1>
350
+ <dl class="info">
351
+ <dt>Server</dt>
352
+ <dd>${escapeHtml(origin)}</dd>
353
+ <dt>Owner</dt>
354
+ <dd>${escapeHtml(owner)}</dd>
355
+ </dl>
356
+ <button class="btn" id="approveBtn" onclick="approve()">Approve</button>
357
+ <div id="status"></div>
358
+ <p class="hint" id="hint"></p>
359
+ </div>
360
+ <script>
361
+ const SESSION_ID = ${JSON.stringify(sessionId)};
362
+ const OWNER_ADDRESS = ${JSON.stringify(owner)};
363
+ const LOCAL_APPROVAL_SHORTCUT_ENABLED = ${JSON.stringify(localApprovalShortcutEnabled)};
364
+ const EMPTY_BODY_SHA256 =
365
+ "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
366
+
367
+ function base64urlEncodeUtf8(input) {
368
+ const bytes = new TextEncoder().encode(input);
369
+ let binary = "";
370
+ for (const byte of bytes) binary += String.fromCharCode(byte);
371
+ return btoa(binary).replace(/\\+/g, "-").replace(/\\//g, "_").replace(/=+$/, "");
372
+ }
373
+
374
+ function utf8ToHex(input) {
375
+ const bytes = new TextEncoder().encode(input);
376
+ return (
377
+ "0x" +
378
+ Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("")
379
+ );
380
+ }
381
+
382
+ async function signPersonalMessage(message, address) {
383
+ const hexMessage = utf8ToHex(message);
384
+ try {
385
+ return await window.ethereum.request({
386
+ method: "personal_sign",
387
+ params: [hexMessage, address],
388
+ });
389
+ } catch (err) {
390
+ return window.ethereum.request({
391
+ method: "personal_sign",
392
+ params: [address, hexMessage],
393
+ });
394
+ }
395
+ }
396
+
397
+ async function buildRemoteApprovalHeader() {
398
+ if (!window.ethereum) {
399
+ throw new Error("No wallet found in this browser. Open this page in a wallet-enabled browser.");
400
+ }
401
+
402
+ const accounts = await window.ethereum.request({
403
+ method: "eth_requestAccounts",
404
+ });
405
+ const signer = accounts && accounts[0];
406
+ if (!signer) {
407
+ throw new Error("No wallet account available.");
408
+ }
409
+ if (signer.toLowerCase() !== OWNER_ADDRESS.toLowerCase()) {
410
+ throw new Error(
411
+ "Connected wallet does not match the Personal Server owner."
412
+ );
413
+ }
414
+
415
+ const now = Math.floor(Date.now() / 1000);
416
+ const payload = {
417
+ aud: window.location.origin,
418
+ bodyHash: EMPTY_BODY_SHA256,
419
+ exp: now + 300,
420
+ iat: now,
421
+ method: "POST",
422
+ uri: window.location.pathname,
423
+ };
424
+ const orderedPayload = {};
425
+ for (const key of Object.keys(payload).sort()) {
426
+ orderedPayload[key] = payload[key];
427
+ }
428
+ const payloadBase64 = base64urlEncodeUtf8(JSON.stringify(orderedPayload));
429
+ const signature = await signPersonalMessage(payloadBase64, signer);
430
+ return "Web3Signed " + payloadBase64 + "." + signature;
431
+ }
432
+
433
+ const remoteApproval = !LOCAL_APPROVAL_SHORTCUT_ENABLED;
434
+ const btn = document.getElementById("approveBtn");
435
+ const status = document.getElementById("status");
436
+ const hint = document.getElementById("hint");
437
+
438
+ if (remoteApproval) {
439
+ btn.textContent = "Connect wallet and approve";
440
+ hint.textContent =
441
+ "Remote approvals require a signature from the Personal Server owner wallet.";
442
+ } else {
443
+ hint.textContent =
444
+ "This browser is on the same machine as the Personal Server, so approval stays local.";
445
+ }
446
+
447
+ async function approve() {
448
+ btn.disabled = true;
449
+ btn.textContent = remoteApproval ? 'Signing...' : 'Authorizing...';
450
+ status.textContent = "";
451
+ try {
452
+ const headers = {};
453
+ if (remoteApproval) {
454
+ headers["Authorization"] = await buildRemoteApprovalHeader();
455
+ btn.textContent = 'Authorizing...';
456
+ }
457
+
458
+ const res = await fetch('/auth/device/approve?session=' + encodeURIComponent(SESSION_ID), {
459
+ method: 'POST',
460
+ headers,
461
+ });
462
+ if (res.ok) {
463
+ document.querySelector('.card').innerHTML =
464
+ '<h1 style="color:#16a34a">Device authorized!</h1>' +
465
+ '<p style="margin-top:1rem;color:#666">You can close this tab.</p>';
466
+ } else {
467
+ const data = await res.json();
468
+ status.textContent = data.error?.message || 'Authorization failed';
469
+ status.style.color = '#dc2626';
470
+ btn.disabled = false;
471
+ btn.textContent = 'Approve';
472
+ }
473
+ } catch (err) {
474
+ status.textContent = 'Network error — please try again';
475
+ status.style.color = '#dc2626';
476
+ btn.disabled = false;
477
+ btn.textContent = 'Approve';
478
+ }
479
+ }
480
+ </script>
481
+ </body>
482
+ </html>`;
483
+ }
484
+ function successPage() {
485
+ return `<!DOCTYPE html>
486
+ <html lang="en">
487
+ <head>
488
+ <meta charset="UTF-8">
489
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
490
+ <title>Authorized — Personal Server</title>
491
+ <style>
492
+ body {
493
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
494
+ background: #f5f5f5; color: #1a1a1a;
495
+ display: flex; justify-content: center; align-items: center;
496
+ min-height: 100vh;
497
+ }
498
+ .card {
499
+ background: #fff; border-radius: 12px; padding: 2rem;
500
+ max-width: 420px; text-align: center;
501
+ box-shadow: 0 2px 8px rgba(0,0,0,.08);
502
+ }
503
+ h1 { color: #16a34a; font-size: 1.25rem; }
504
+ p { margin-top: 1rem; color: #666; }
505
+ </style>
506
+ </head>
507
+ <body>
508
+ <div class="card">
509
+ <h1>Device authorized!</h1>
510
+ <p>You can close this tab.</p>
511
+ </div>
512
+ </body>
513
+ </html>`;
514
+ }
515
+ function errorPage(message) {
516
+ return `<!DOCTYPE html>
517
+ <html lang="en">
518
+ <head>
519
+ <meta charset="UTF-8">
520
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
521
+ <title>Error — Personal Server</title>
522
+ <style>
523
+ body {
524
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
525
+ background: #f5f5f5; color: #1a1a1a;
526
+ display: flex; justify-content: center; align-items: center;
527
+ min-height: 100vh;
528
+ }
529
+ .card {
530
+ background: #fff; border-radius: 12px; padding: 2rem;
531
+ max-width: 420px; text-align: center;
532
+ box-shadow: 0 2px 8px rgba(0,0,0,.08);
533
+ }
534
+ h1 { color: #dc2626; font-size: 1.25rem; }
535
+ p { margin-top: 1rem; color: #666; }
536
+ </style>
537
+ </head>
538
+ <body>
539
+ <div class="card">
540
+ <h1>Error</h1>
541
+ <p>${escapeHtml(message)}</p>
542
+ </div>
543
+ </body>
544
+ </html>`;
545
+ }
546
+ function escapeHtml(str) {
547
+ return str
548
+ .replace(/&/g, "&amp;")
549
+ .replace(/</g, "&lt;")
550
+ .replace(/>/g, "&gt;")
551
+ .replace(/"/g, "&quot;")
552
+ .replace(/'/g, "&#39;");
553
+ }
554
+ // Export for testing
555
+ export { sessions, DEVICE_SESSION_TTL_MS as SESSION_TTL_MS, DEVICE_POLL_INTERVAL_MS as POLL_INTERVAL_MS, };
556
+ /**
557
+ * Read-only adapter exposing the device-flow session map to the standard
558
+ * RFC 8628 token endpoint (`POST /oauth/token` with
559
+ * `grant_type=urn:ietf:params:oauth:grant-type:device_code`). The
560
+ * `pollToken` issued at `POST /auth/device` is the OAuth2 `device_code`.
561
+ */
562
+ export function createDeviceSessionLookup() {
563
+ return {
564
+ findByDeviceCode(deviceCode) {
565
+ sessions.purgeExpired(Date.now());
566
+ const session = sessions.findByPollToken(deviceCode);
567
+ if (!session)
568
+ return null;
569
+ return {
570
+ status: session.status,
571
+ accessToken: session.accessToken,
572
+ accessTokenExpiresAt: session.accessTokenExpiresAt,
573
+ sessionId: session.sessionId,
574
+ };
575
+ },
576
+ consume(sessionId) {
577
+ sessions.delete(sessionId);
578
+ },
579
+ };
580
+ }
581
+ //# sourceMappingURL=auth-device.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-device.js","sourceRoot":"","sources":["../../src/routes/auth-device.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,13 @@
1
1
  import { Hono } from "hono";
2
+ import { type PersonalServerApiDispatchOptions, type PersonalServerReadFulfillmentReporter } from "@opendatalabs/personal-server-ts-core/api";
2
3
  import type { HierarchyManagerOptions } from "@opendatalabs/personal-server-ts-core/storage/hierarchy";
3
4
  import type { IndexManager } from "@opendatalabs/personal-server-ts-core/storage/index";
4
- import type { GatewayClient } from "@opendatalabs/personal-server-ts-core/gateway";
5
+ import type { DataPortabilityGatewayConfig, GatewayClient } from "@opendatalabs/vana-sdk/node";
5
6
  import type { AccessLogWriter } from "@opendatalabs/personal-server-ts-core/logging/access-log";
6
7
  import type { SyncManager } from "@opendatalabs/personal-server-ts-core/sync";
8
+ import type { DataStoragePort, RuntimeAvailabilityPort } from "@opendatalabs/personal-server-ts-core/ports";
9
+ import type { ServerSigner } from "@opendatalabs/personal-server-ts-core/signing";
10
+ import type { TokenStore } from "../token-store.js";
7
11
  import type { Logger } from "pino";
8
12
  export interface DataRouteDeps {
9
13
  indexManager: IndexManager;
@@ -12,9 +16,46 @@ export interface DataRouteDeps {
12
16
  serverOrigin: string | (() => string);
13
17
  serverOwner?: `0x${string}`;
14
18
  gateway: GatewayClient;
19
+ /**
20
+ * Required for the X402 flow on GET /v1/data/:scope. Provides the EIP-712
21
+ * domain (escrowPaymentDomain + dataRegistryDomain) the server uses to
22
+ * recover X-PAYMENT signatures and the embedded accessRecord signatures.
23
+ */
24
+ gatewayConfig?: DataPortabilityGatewayConfig;
25
+ /**
26
+ * Gateway base URL. Used by the X402 forward path — the handler `fetch`es
27
+ * POST /v1/escrow/pay directly so it can inspect the gateway's structured
28
+ * error body (the SDK's gateway.payForOperation discards it).
29
+ */
30
+ gatewayUrl?: string;
31
+ /**
32
+ * When true, GET /v1/data/:scope enforces the X402 challenge / X-PAYMENT
33
+ * cycle on every read. Off-by-default so dev / test setups don't require
34
+ * builder-side payment signing.
35
+ */
36
+ paymentEnabled?: boolean;
15
37
  accessLogWriter: AccessLogWriter;
38
+ readFulfillmentReporter?: PersonalServerReadFulfillmentReporter;
16
39
  syncManager?: SyncManager | null;
17
40
  devToken?: string;
41
+ accessToken?: string;
42
+ tokenStore?: TokenStore;
43
+ dataStorage?: DataStoragePort;
44
+ runtimeAvailability?: RuntimeAvailabilityPort;
45
+ /**
46
+ * Powers the RECORD_DATA_ACCESS attestation embedded in 402 challenges.
47
+ * When supplied alongside serverOwner + paymentEnabled, every challenge
48
+ * carries a server-signed accessRecord. Required for the on-chain
49
+ * recordDataAccess to be scheduled by gateway.settle later.
50
+ */
51
+ serverSigner?: ServerSigner;
52
+ /**
53
+ * Personal server's own EOA address. Needed by the X402 verifier so it
54
+ * can confirm that the accessRecord echoed back in X-PAYMENT was signed
55
+ * by this server (not forged by a malicious builder).
56
+ */
57
+ serverAddress?: `0x${string}`;
58
+ mountPath?: PersonalServerApiDispatchOptions["basePath"];
18
59
  }
19
60
  export declare function dataRoutes(deps: DataRouteDeps): Hono;
20
61
  //# sourceMappingURL=data.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/routes/data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAS5B,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yDAAyD,CAAC;AACvG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qDAAqD,CAAC;AACxF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+CAA+C,CAAC;AACnF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0DAA0D,CAAC;AAChG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4CAA4C,CAAC;AAC9E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAWnC,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,EAAE,uBAAuB,CAAC;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC;IACtC,WAAW,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;IAC5B,OAAO,EAAE,aAAa,CAAC;IACvB,eAAe,EAAE,eAAe,CAAC;IACjC,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAoRpD"}
1
+ {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/routes/data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAEL,KAAK,gCAAgC,EACrC,KAAK,qCAAqC,EAC3C,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yDAAyD,CAAC;AACvG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qDAAqD,CAAC;AACxF,OAAO,KAAK,EACV,4BAA4B,EAC5B,aAAa,EACd,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0DAA0D,CAAC;AAChG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4CAA4C,CAAC;AAC9E,OAAO,KAAK,EACV,eAAe,EACf,uBAAuB,EACxB,MAAM,6CAA6C,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+CAA+C,CAAC;AAClF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAQnC,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,EAAE,uBAAuB,CAAC;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC;IACtC,WAAW,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;IAC5B,OAAO,EAAE,aAAa,CAAC;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,4BAA4B,CAAC;IAC7C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,eAAe,CAAC;IACjC,uBAAuB,CAAC,EAAE,qCAAqC,CAAC;IAChE,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,mBAAmB,CAAC,EAAE,uBAAuB,CAAC;IAC9C;;;;;OAKG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,gCAAgC,CAAC,UAAU,CAAC,CAAC;CAC1D;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAmDpD"}