@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.
- package/dist/api/index.d.ts +80 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +126 -68
- package/dist/api/index.js.map +1 -1
- package/dist/contracts/binary.d.ts +12 -0
- package/dist/contracts/binary.d.ts.map +1 -1
- package/dist/contracts/binary.js +31 -0
- package/dist/contracts/binary.js.map +1 -1
- package/dist/contracts/data.d.ts +21 -0
- package/dist/contracts/data.d.ts.map +1 -1
- package/dist/contracts/data.js +45 -4
- package/dist/contracts/data.js.map +1 -1
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +1 -1
- package/dist/contracts/index.js.map +1 -1
- package/dist/logging/access-log.d.ts +1 -1
- package/dist/logging/access-log.d.ts.map +1 -1
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/read-client.d.ts +23 -0
- package/dist/mcp/read-client.d.ts.map +1 -1
- package/dist/mcp/read-client.js +16 -4
- package/dist/mcp/read-client.js.map +1 -1
- package/dist/mcp/session.d.ts +160 -0
- package/dist/mcp/session.d.ts.map +1 -0
- package/dist/mcp/session.js +234 -0
- package/dist/mcp/session.js.map +1 -0
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +135 -15
- package/dist/mcp/tools.js.map +1 -1
- package/dist/policy/data-read.d.ts +8 -0
- package/dist/policy/data-read.d.ts.map +1 -1
- package/dist/policy/data-read.js +9 -2
- package/dist/policy/data-read.js.map +1 -1
- package/dist/policy/data-write.d.ts +69 -0
- package/dist/policy/data-write.d.ts.map +1 -0
- package/dist/policy/data-write.js +131 -0
- package/dist/policy/data-write.js.map +1 -0
- package/dist/policy/index.d.ts +2 -1
- package/dist/policy/index.d.ts.map +1 -1
- package/dist/policy/index.js +2 -1
- package/dist/policy/index.js.map +1 -1
- package/dist/write/attribution.d.ts +165 -0
- package/dist/write/attribution.d.ts.map +1 -0
- package/dist/write/attribution.js +277 -0
- package/dist/write/attribution.js.map +1 -0
- package/dist/write/index.d.ts +3 -0
- package/dist/write/index.d.ts.map +1 -0
- package/dist/write/index.js +3 -0
- package/dist/write/index.js.map +1 -0
- package/dist/write/session.d.ts +96 -0
- package/dist/write/session.d.ts.map +1 -0
- package/dist/write/session.js +136 -0
- package/dist/write/session.js.map +1 -0
- 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.
|
|
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": {
|