@opendatalabs/personal-server-ts-core 1.4.0 → 1.6.0

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 (58) hide show
  1. package/dist/api/index.d.ts +80 -0
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +126 -68
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/contracts/binary.d.ts +12 -0
  6. package/dist/contracts/binary.d.ts.map +1 -1
  7. package/dist/contracts/binary.js +31 -0
  8. package/dist/contracts/binary.js.map +1 -1
  9. package/dist/contracts/data.d.ts +21 -0
  10. package/dist/contracts/data.d.ts.map +1 -1
  11. package/dist/contracts/data.js +45 -4
  12. package/dist/contracts/data.js.map +1 -1
  13. package/dist/contracts/index.d.ts +1 -1
  14. package/dist/contracts/index.d.ts.map +1 -1
  15. package/dist/contracts/index.js +1 -1
  16. package/dist/contracts/index.js.map +1 -1
  17. package/dist/logging/access-log.d.ts +1 -1
  18. package/dist/logging/access-log.d.ts.map +1 -1
  19. package/dist/mcp/index.d.ts +1 -0
  20. package/dist/mcp/index.d.ts.map +1 -1
  21. package/dist/mcp/index.js +1 -0
  22. package/dist/mcp/index.js.map +1 -1
  23. package/dist/mcp/read-client.d.ts +23 -0
  24. package/dist/mcp/read-client.d.ts.map +1 -1
  25. package/dist/mcp/read-client.js +16 -4
  26. package/dist/mcp/read-client.js.map +1 -1
  27. package/dist/mcp/session.d.ts +160 -0
  28. package/dist/mcp/session.d.ts.map +1 -0
  29. package/dist/mcp/session.js +234 -0
  30. package/dist/mcp/session.js.map +1 -0
  31. package/dist/mcp/tools.d.ts.map +1 -1
  32. package/dist/mcp/tools.js +135 -15
  33. package/dist/mcp/tools.js.map +1 -1
  34. package/dist/policy/data-read.d.ts +8 -0
  35. package/dist/policy/data-read.d.ts.map +1 -1
  36. package/dist/policy/data-read.js +9 -2
  37. package/dist/policy/data-read.js.map +1 -1
  38. package/dist/policy/data-write.d.ts +69 -0
  39. package/dist/policy/data-write.d.ts.map +1 -0
  40. package/dist/policy/data-write.js +131 -0
  41. package/dist/policy/data-write.js.map +1 -0
  42. package/dist/policy/index.d.ts +2 -1
  43. package/dist/policy/index.d.ts.map +1 -1
  44. package/dist/policy/index.js +2 -1
  45. package/dist/policy/index.js.map +1 -1
  46. package/dist/write/attribution.d.ts +165 -0
  47. package/dist/write/attribution.d.ts.map +1 -0
  48. package/dist/write/attribution.js +277 -0
  49. package/dist/write/attribution.js.map +1 -0
  50. package/dist/write/index.d.ts +3 -0
  51. package/dist/write/index.d.ts.map +1 -0
  52. package/dist/write/index.js +3 -0
  53. package/dist/write/index.js.map +1 -0
  54. package/dist/write/session.d.ts +96 -0
  55. package/dist/write/session.d.ts.map +1 -0
  56. package/dist/write/session.js +136 -0
  57. package/dist/write/session.js.map +1 -0
  58. package/package.json +5 -1
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Write API session (server-signed delegated writes, v1).
3
+ *
4
+ * A registered builder that holds its OWN key and a WRITE-grant proves control
5
+ * of that key ONCE — a Web3Signed handshake to `POST /v1/write/session` — and
6
+ * the PS mints a short-lived bearer token bound to `{ builderAddress, grantId }`
7
+ * (the grant's `write:`-prefixed scope entries define what it may write).
8
+ * Writes then present that token on the EXISTING ingest endpoint
9
+ * (`POST /v1/data/:scope`); each write authorizes as the builder via
10
+ * `verifyDataWritePolicy` and the PS ingests / encrypts / uploads / registers
11
+ * through the normal owner path — the PS signs AddData as the owner exactly as
12
+ * it does today, and the builder never holds the owner key.
13
+ *
14
+ * Deliberately the same handshake shape as the self-signing MCP session
15
+ * (mcp/session.ts): prove key control once, short-lived bearer after. The
16
+ * session token is authorization only; per-write builder ATTRIBUTION is a
17
+ * separate signed proof (see ./attribution.ts).
18
+ */
19
+ import { hashConnectionToken } from "../mcp/connection-api.js";
20
+ import { createInMemoryMcpProofReplayStore, } from "../mcp/session.js";
21
+ import { writeScopePatterns } from "../policy/data-write.js";
22
+ import { GrantOwnerMismatchError, GrantRequiredError, GrantRevokedError, InvalidSignatureError, ProtocolError, ScopeMismatchError, UnregisteredBuilderError, } from "../errors/catalog.js";
23
+ export const createInMemoryWriteProofReplayStore = createInMemoryMcpProofReplayStore;
24
+ export const hashWriteSessionToken = hashConnectionToken;
25
+ /**
26
+ * KNOWN LIMITATION: in-memory only, same trade-off as the MCP session store —
27
+ * a process restart drops live tokens and the builder must re-handshake.
28
+ * Acceptable for today's single-instance Personal Server; persist via the
29
+ * host's state store before multi-instance.
30
+ */
31
+ export function createInMemoryWriteSessionStore() {
32
+ const byHash = new Map();
33
+ return {
34
+ async create(record) {
35
+ byHash.set(record.tokenHash, record);
36
+ },
37
+ async getByTokenHash(tokenHash) {
38
+ const record = byHash.get(tokenHash);
39
+ if (!record)
40
+ return null;
41
+ if (record.expiresAtMs <= Date.now()) {
42
+ byHash.delete(tokenHash);
43
+ return null;
44
+ }
45
+ return record;
46
+ },
47
+ };
48
+ }
49
+ const DEFAULT_SESSION_TTL_MS = 60 * 60 * 1000;
50
+ /**
51
+ * Validate the handshake identity and mint a write-session token. Same
52
+ * division of labor as createMcpSession: the handshake does the minimal
53
+ * checks that give a clear error (builder registered, grant exists + not
54
+ * revoked + carries write scopes, grantee == builder, grantor == owner);
55
+ * per-scope / expiry enforcement stays authoritative at write time, where
56
+ * `verifyDataWritePolicy` runs against the live grant on every POST.
57
+ */
58
+ export async function createWriteSession(input, options) {
59
+ const builder = await options.authSessionVerifier.getBuilder(input.builderAddress);
60
+ if (!builder)
61
+ throw new UnregisteredBuilderError();
62
+ const grant = await options.grantVerifier.getGrant(input.grantId);
63
+ if (!grant) {
64
+ throw new GrantRequiredError({
65
+ reason: "Grant not found",
66
+ grantId: input.grantId,
67
+ });
68
+ }
69
+ if (grant.revokedAt !== null) {
70
+ throw new GrantRevokedError({ grantId: grant.id });
71
+ }
72
+ const patterns = writeScopePatterns(grant.scopes ?? []);
73
+ if (patterns.length === 0) {
74
+ throw new ScopeMismatchError({
75
+ reason: "Grant has no write scopes (write-grant entries use the write: prefix)",
76
+ grantedScopes: grant.scopes ?? [],
77
+ });
78
+ }
79
+ if (builder.id.toLowerCase() !== grant.granteeId.toLowerCase()) {
80
+ throw new InvalidSignatureError({
81
+ reason: "Handshake signer is not the grant builder",
82
+ expected: grant.granteeId,
83
+ actual: input.builderAddress,
84
+ });
85
+ }
86
+ // Ownership binding — fail closed on a grantor-less grant: gateway
87
+ // responses are untrusted runtime data, despite their type.
88
+ if (!grant.grantorAddress ||
89
+ grant.grantorAddress.toLowerCase() !== options.serverOwner.toLowerCase()) {
90
+ throw new GrantOwnerMismatchError({
91
+ grantId: grant.id,
92
+ expected: options.serverOwner,
93
+ actual: grant.grantorAddress ?? null,
94
+ });
95
+ }
96
+ // Prepare the token first (deterministic, can't meaningfully fail) so the
97
+ // only fallible step after consuming the proof is persistence.
98
+ const token = options.randomToken();
99
+ const tokenHash = await hashWriteSessionToken(token);
100
+ const ttlMs = options.ttlMs ?? DEFAULT_SESSION_TTL_MS;
101
+ const nowMs = options.now?.() ?? Date.now();
102
+ // Replay guard: a still-valid handshake proof must mint at most one token.
103
+ // Same consume/rollback discipline as createMcpSession.
104
+ const usingReplayGuard = Boolean(input.proof && options.replayStore);
105
+ if (input.proof && options.replayStore) {
106
+ const replayed = await options.replayStore.consume(input.proof.id, input.proof.expiresAtMs);
107
+ if (replayed) {
108
+ throw new ProtocolError(401, "WRITE_SESSION_PROOF_REPLAY", "Handshake proof already used; sign a fresh proof");
109
+ }
110
+ }
111
+ try {
112
+ await options.store.create({
113
+ tokenHash,
114
+ builderAddress: input.builderAddress,
115
+ grantId: grant.id,
116
+ writeScopes: patterns,
117
+ createdAt: new Date(nowMs).toISOString(),
118
+ expiresAtMs: nowMs + ttlMs,
119
+ });
120
+ }
121
+ catch (err) {
122
+ // Persistence failed — release the reservation so a legitimate retry with
123
+ // the same still-valid proof isn't rejected as a replay.
124
+ if (usingReplayGuard && input.proof && options.replayStore?.release) {
125
+ await options.replayStore.release(input.proof.id);
126
+ }
127
+ throw err;
128
+ }
129
+ return {
130
+ accessToken: token,
131
+ expiresInSeconds: Math.floor(ttlMs / 1000),
132
+ grantId: grant.id,
133
+ writeScopes: patterns,
134
+ };
135
+ }
136
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/write/session.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendatalabs/personal-server-ts-core",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "Protocol logic for the Vana Personal Server — auth, grants, scopes, storage, keys",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -123,6 +123,10 @@
123
123
  "./payment": {
124
124
  "types": "./dist/payment/index.d.ts",
125
125
  "import": "./dist/payment/index.js"
126
+ },
127
+ "./write": {
128
+ "types": "./dist/write/index.d.ts",
129
+ "import": "./dist/write/index.js"
126
130
  }
127
131
  },
128
132
  "scripts": {