@tenuo/core 0.3.2-beta.0 → 0.3.4-beta.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/README.md +20 -9
- package/dist/api.d.ts +36 -3
- package/dist/api.js.map +1 -1
- package/dist/build-info.json +3 -3
- package/dist/client.js +170 -6
- package/dist/client.js.map +1 -1
- package/dist/generated/tenuo_wasm.d.ts +15 -1
- package/dist/generated/tenuo_wasm.js +43 -4
- package/dist/generated/tenuo_wasm_bg.wasm +0 -0
- package/dist/generated/tenuo_wasm_bg.wasm.d.ts +3 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +15 -46
- package/dist/mcp.js.map +1 -1
- package/dist/replay.d.ts +20 -0
- package/dist/replay.js +117 -0
- package/dist/replay.js.map +1 -0
- package/dist/session.d.ts +1 -0
- package/dist/session.js +11 -0
- package/dist/session.js.map +1 -1
- package/dist/wasm.d.ts +40 -1
- package/dist/wasm.js +19 -0
- package/dist/wasm.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EACL,mBAAmB,EACnB,UAAU,EACV,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACnB,eAAe,GAChB,MAAM,WAAW,CAAC;AA4EnB,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,GAAG,EACH,KAAK,EACL,GAAG,EACH,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,KAAK,EACL,GAAG,EACH,GAAG,EACH,GAAG,EACH,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,OAAO,EACP,KAAK,EACL,KAAK,EACL,KAAK,EACL,MAAM,EACN,KAAK,EACL,UAAU,EACV,OAAO,EACP,QAAQ,GACT,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEnE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAG/D,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAElF,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC","sourcesContent":["export type {\n ApprovalGateInspection,\n ApprovalRequirement,\n ApprovalRequirementStatus,\n ConstraintDiagnostics,\n ConstraintViolationKind,\n ShlexCheck,\n} from \"./wasm.ts\";\n\nexport {\n approvalRequirement,\n checkShlex,\n diagnoseConstraints,\n evaluateApprovalGates,\n inspectApprovalGate,\n parseStrictJson,\n} from \"./wasm.ts\";\n\nexport type {\n AllConstraint,\n AllowPolicy,\n AnyOfConstraint,\n ApprovalContextAttestation,\n ApprovalInfo,\n ApprovalRequest,\n ApprovalRequestSummary,\n ArgApprovalGate,\n CelConstraint,\n CidrConstraint,\n Clearance,\n ConstraintExpr,\n ContainsConstraint,\n ControlPlaneApprovalRequestV1,\n ControlPlaneApprovalResponseV1,\n CreateTenuoOptions,\n Decision,\n DevRoot,\n EmailConstraint,\n ExactConstraint,\n ExecuteOptions,\n ExplainedField,\n Explanation,\n IssueInput,\n IssuerKey,\n MaxConstraint,\n McpAttachOptions,\n McpCallParams,\n McpHandlerPolicy,\n McpJsonRpcError,\n McpVerifyOptions,\n MinConstraint,\n NarrowInput,\n NarrowOptions,\n NonceStore,\n NotConstraint,\n NotOneOfConstraint,\n OneOfConstraint,\n PatternConstraint,\n PresentedCall,\n ProtectedTool,\n PublicKeyHandle,\n RangeConstraint,\n ReceiptChainInfo,\n ReceiptInfo,\n RegexConstraint,\n RequireApproval,\n RevocationListInfo,\n RevocationListInput,\n Session,\n SessionAllow,\n SessionFromWireInput,\n SessionInfo,\n SessionInput,\n ShlexConstraint,\n SignApprovalOptions,\n SubsetConstraint,\n Tenuo,\n LegacyTenuoErrorCode,\n ProtocolDenialCode,\n TenuoErrorCode,\n TenuoMcp,\n TenuoMcpMeta,\n ToolApprovalGate,\n ToolLike,\n ToolPolicy,\n UnderConstraint,\n UrlPatternConstraint,\n UrlSafeConstraint,\n WarrantPart,\n WildcardConstraint,\n} from \"./api.ts\";\n\nexport {\n ApprovalRequiredError,\n AuthorizationDeniedError,\n TenuoConfigurationError,\n TenuoError,\n} from \"./errors.ts\";\n\nexport {\n all,\n anyOf,\n cel,\n cidr,\n contains,\n email,\n exact,\n max,\n min,\n not,\n notOneOf,\n oneOf,\n pathGlob,\n pattern,\n range,\n regex,\n shlex,\n subset,\n under,\n urlPattern,\n urlSafe,\n wildcard,\n} from \"./constraints.ts\";\n\nexport { memoryNonceStore } from \"./nonce.ts\";\n\nexport { Session as SessionHandle, isSession } from \"./session.ts\";\n\nexport { ConnectToken, parseConnectToken } from \"./connect.ts\";\nexport type { ResolveEndpointOptions } from \"./connect.ts\";\n\nexport { HolderIdentity, generateIdentity, identityFromKey } from \"./identity.ts\";\n\nexport { Runtime } from \"./runtime.ts\";\nexport type { ReceiptsMode, RuntimeOptions, SessionWarrant } from \"./runtime.ts\";\n\nexport { createTenuo } from \"./client.ts\";\n"]}
|
package/dist/mcp.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { McpAttachOptions, McpVerifyOptions, PresentedCall, Session as SessionContract, TenuoMcp } from "./api.ts";
|
|
2
|
-
import type
|
|
2
|
+
import { type WasmContext, type WasmDecision } from "./wasm.ts";
|
|
3
3
|
export type Decide = (decision: WasmDecision, tool: string, native?: object, args?: Record<string, unknown>) => void;
|
|
4
4
|
/**
|
|
5
5
|
* Transport-agnostic half of `attach`: authorize locally, then sign
|
package/dist/mcp.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { AuthorizationDeniedError, TenuoConfigurationError, TenuoError } from "./errors.js";
|
|
2
2
|
import { collectReceipt, emitIsolatedReceipt } from "./receipts.js";
|
|
3
|
+
import { admitNonce, consumeApprovals, snapshotApprovals } from "./replay.js";
|
|
3
4
|
import { nativeSession } from "./session.js";
|
|
5
|
+
import { loadWasm } from "./wasm.js";
|
|
4
6
|
/**
|
|
5
7
|
* Transport-agnostic half of `attach`: authorize locally, then sign
|
|
6
8
|
* proof-of-possession over the call. Shared by `tenuo.present()` and
|
|
@@ -38,15 +40,19 @@ export async function verifyPresented(context, decide, presented, name, args, op
|
|
|
38
40
|
if (envelope === undefined) {
|
|
39
41
|
throw new TenuoConfigurationError(`${label} needs { warrant, signature } from tenuo.present() or tenuo.mcp.attach()`);
|
|
40
42
|
}
|
|
43
|
+
const approvals = snapshotApprovals(envelope.approvals);
|
|
41
44
|
// Capture once: replay admission and receipt callbacks may yield or mutate
|
|
42
45
|
// the caller's object. Execution must use the exact snapshot verified here.
|
|
43
46
|
const snapshot = captureArguments(args);
|
|
44
|
-
const decision = context.authorizePresented(envelope.warrant, name, snapshot.json, envelope.signature,
|
|
47
|
+
const decision = context.authorizePresented(envelope.warrant, name, snapshot.json, envelope.signature, approvals, options?.allow, options?.requestId);
|
|
45
48
|
// Rust already signed this envelope. Emit before the nonce store can
|
|
46
49
|
// refuse a replay — otherwise an attacker leaves no audit artifact.
|
|
47
50
|
emitReceipt(options?.onReceipt, decision.receipt, undefined, host);
|
|
48
51
|
if (decision.outcome === "allow") {
|
|
49
52
|
await admitPop(options?.nonceStore, envelope.signature, options?.onNonceStoreError);
|
|
53
|
+
// A fresh PoP over the same approvals is a new token; consume the
|
|
54
|
+
// approvals themselves so a nonce store makes them one-use here too.
|
|
55
|
+
await consumeApprovals(options?.nonceStore, approvals, receiptRequestHash(decision.receipt), name, options?.onNonceStoreError);
|
|
50
56
|
}
|
|
51
57
|
decide(decision, name);
|
|
52
58
|
// Run the host parse of the JSON text the proof covers. The raw host
|
|
@@ -137,7 +143,6 @@ const VERIFY_OPTION_KEYS = new Set([
|
|
|
137
143
|
"onNonceStoreError",
|
|
138
144
|
"requestId",
|
|
139
145
|
]);
|
|
140
|
-
const REPLAY_STORE_UNAVAILABLE = "Replay store unavailable";
|
|
141
146
|
const ATTACH_OPTION_KEYS = new Set(["approvals", "onReceipt", "requestId"]);
|
|
142
147
|
function assertKnownKeys(value, known, label) {
|
|
143
148
|
const unknown = Object.keys(value).filter((key) => !known.has(key));
|
|
@@ -158,57 +163,21 @@ function isHandlerPolicy(value) {
|
|
|
158
163
|
const hasStoreError = "onNonceStoreError" in record && typeof record.onNonceStoreError === "function";
|
|
159
164
|
return keys.length === 0 || hasAllow || hasReceipt || hasNonce || hasStoreError;
|
|
160
165
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
let admitted;
|
|
166
|
-
try {
|
|
167
|
-
admitted = store.checkAndRecord(signature);
|
|
168
|
-
}
|
|
169
|
-
catch (error) {
|
|
170
|
-
throw replayStoreUnavailable(error, onNonceStoreError);
|
|
171
|
-
}
|
|
172
|
-
if (isThenable(admitted)) {
|
|
173
|
-
try {
|
|
174
|
-
admitted = await admitted;
|
|
175
|
-
}
|
|
176
|
-
catch (error) {
|
|
177
|
-
throw replayStoreUnavailable(error, onNonceStoreError);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
if (typeof admitted !== "boolean") {
|
|
181
|
-
throw replayStoreUnavailable(new TenuoConfigurationError("NonceStore.checkAndRecord() must return boolean or Promise<boolean>"), onNonceStoreError);
|
|
182
|
-
}
|
|
183
|
-
if (!admitted) {
|
|
184
|
-
throw new AuthorizationDeniedError("TENUO_INVALID_POP", "PoP replay detected — this exact authorization token was already consumed.");
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
function replayStoreUnavailable(cause, onNonceStoreError) {
|
|
188
|
-
emitIsolated(onNonceStoreError, cause);
|
|
189
|
-
const error = new TenuoConfigurationError(REPLAY_STORE_UNAVAILABLE);
|
|
190
|
-
if (cause !== undefined) {
|
|
191
|
-
error.cause = cause;
|
|
192
|
-
}
|
|
193
|
-
return error;
|
|
166
|
+
function admitPop(store, signature, onNonceStoreError) {
|
|
167
|
+
return admitNonce(store, signature, onNonceStoreError, () => new AuthorizationDeniedError("TENUO_INVALID_POP", "PoP replay detected — this exact authorization token was already consumed."));
|
|
194
168
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
169
|
+
/** The request hash core wrote into the decision receipt, when there is one. */
|
|
170
|
+
function receiptRequestHash(receipt) {
|
|
171
|
+
if (receipt === undefined) {
|
|
172
|
+
return undefined;
|
|
198
173
|
}
|
|
199
174
|
try {
|
|
200
|
-
|
|
201
|
-
if (isThenable(result)) {
|
|
202
|
-
void Promise.resolve(result).catch(() => undefined);
|
|
203
|
-
}
|
|
175
|
+
return loadWasm().sdkVerifyReceipt(receipt).request_hash;
|
|
204
176
|
}
|
|
205
177
|
catch {
|
|
206
|
-
|
|
178
|
+
return undefined;
|
|
207
179
|
}
|
|
208
180
|
}
|
|
209
|
-
function isThenable(value) {
|
|
210
|
-
return value !== null && typeof value === "object" && "then" in value && typeof value.then === "function";
|
|
211
|
-
}
|
|
212
181
|
function emitReceipt(onReceipt, receipt, session, host) {
|
|
213
182
|
collectReceipt(receipt, session, host);
|
|
214
183
|
emitIsolatedReceipt(onReceipt, receipt);
|
package/dist/mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n McpAttachOptions,\n McpCallParams,\n McpHandlerPolicy,\n McpJsonRpcError,\n McpVerifyOptions,\n PresentedCall,\n Session as SessionContract,\n TenuoErrorCode,\n TenuoMcp,\n} from \"./api.ts\";\nimport { AuthorizationDeniedError, TenuoConfigurationError, TenuoError } from \"./errors.ts\";\nimport { collectReceipt, emitIsolatedReceipt } from \"./receipts.ts\";\nimport { nativeSession, type Session } from \"./session.ts\";\nimport type { WasmContext, WasmDecision } from \"./wasm.ts\";\n\nexport type Decide = (decision: WasmDecision, tool: string, native?: object, args?: Record<string, unknown>) => void;\n\n/**\n * Transport-agnostic half of `attach`: authorize locally, then sign\n * proof-of-possession over the call. Shared by `tenuo.present()` and\n * `tenuo.mcp.attach()`.\n */\nexport function presentCall(\n context: WasmContext,\n decide: Decide,\n session: SessionContract,\n name: string,\n args: Readonly<Record<string, unknown>>,\n options: McpAttachOptions | undefined,\n label: string,\n host?: object,\n): { readonly presented: PresentedCall; readonly wireArgs: Record<string, unknown> } {\n if (options !== undefined) {\n assertKnownKeys(options, ATTACH_OPTION_KEYS, `${label} options`);\n }\n const native = nativeSession(session as Session);\n const snapshot = captureArguments(args);\n const local = context.authorize(\n native,\n name,\n snapshot.json,\n options?.approvals,\n undefined,\n options?.requestId,\n );\n emitReceipt(options?.onReceipt, local.receipt, session, host);\n decide(local, name, native, snapshot.executionArgs());\n const envelope = context.signMeta(\n native,\n name,\n snapshot.json,\n Math.floor(Date.now() / 1000),\n options?.approvals ?? null,\n );\n const presented = {\n warrant: envelope.warrant,\n signature: envelope.signature,\n ...(envelope.approvals !== undefined && envelope.approvals.length > 0\n ? { approvals: envelope.approvals }\n : {}),\n };\n return { presented, wireArgs: snapshot.executionArgs() };\n}\n\n/**\n * Transport-agnostic half of `verify`: check a presented warrant chain and\n * proof-of-possession, then apply the host ceiling. Shared by\n * `tenuo.verify()` and `tenuo.mcp.verify()`.\n */\nexport async function verifyPresented(\n context: WasmContext,\n decide: Decide,\n presented: PresentedCall,\n name: string,\n args: Readonly<Record<string, unknown>>,\n options: McpVerifyOptions | undefined,\n label: string,\n host?: object,\n): Promise<Readonly<Record<string, unknown>>> {\n if (options !== undefined) {\n assertKnownKeys(options, VERIFY_OPTION_KEYS, `${label} options`);\n }\n const envelope = presentedEnvelope(presented);\n if (envelope === undefined) {\n throw new TenuoConfigurationError(\n `${label} needs { warrant, signature } from tenuo.present() or tenuo.mcp.attach()`,\n );\n }\n // Capture once: replay admission and receipt callbacks may yield or mutate\n // the caller's object. Execution must use the exact snapshot verified here.\n const snapshot = captureArguments(args);\n const decision = context.authorizePresented(\n envelope.warrant,\n name,\n snapshot.json,\n envelope.signature,\n envelope.approvals,\n options?.allow,\n options?.requestId,\n );\n // Rust already signed this envelope. Emit before the nonce store can\n // refuse a replay — otherwise an attacker leaves no audit artifact.\n emitReceipt(options?.onReceipt, decision.receipt, undefined, host);\n if (decision.outcome === \"allow\") {\n await admitPop(options?.nonceStore, envelope.signature, options?.onNonceStoreError);\n }\n decide(decision, name);\n // Run the host parse of the JSON text the proof covers. The raw host\n // object can contain values that JSON drops.\n return snapshot.executionArgs();\n}\n\nexport function createMcp(context: WasmContext, decide: Decide, host?: object): TenuoMcp {\n const mcp: TenuoMcp = {\n attach(session, name, args, options) {\n const { presented, wireArgs } = presentCall(\n context,\n decide,\n session,\n name,\n args,\n options,\n \"mcp.attach()\",\n host,\n );\n return { name, arguments: wireArgs, _meta: { tenuo: presented } };\n },\n\n async verify(name, args, meta, options) {\n const envelope = tenuoEnvelope(meta);\n if (envelope === undefined) {\n throw new TenuoConfigurationError(\n \"MCP call is missing _meta.tenuo. The client must call tenuo.mcp.attach().\",\n );\n }\n return verifyPresented(context, decide, envelope, name, args, options, \"mcp.verify()\", host);\n },\n\n handler(name, policyOrExecute, maybeExecute?) {\n const policy = isHandlerPolicy(policyOrExecute) ? policyOrExecute : undefined;\n const execute = isHandlerPolicy(policyOrExecute) ? maybeExecute : policyOrExecute;\n if (typeof execute !== \"function\") {\n throw new TenuoConfigurationError(\"mcp.handler() requires an execute function\");\n }\n return async (args, extra) => {\n const authorized = await mcp.verify(name, args, extra?._meta ?? extra?.meta, {\n ...(policy?.allow !== undefined ? { allow: policy.allow } : {}),\n ...(policy?.onReceipt !== undefined ? { onReceipt: policy.onReceipt } : {}),\n ...(policy?.nonceStore !== undefined ? { nonceStore: policy.nonceStore } : {}),\n ...(policy?.onNonceStoreError !== undefined ? { onNonceStoreError: policy.onNonceStoreError } : {}),\n });\n return execute(authorized as never);\n };\n },\n\n jsonRpcError(error) {\n return jsonRpcError(error);\n },\n };\n return mcp;\n}\n\n/** A private immutable input snapshot, not an authorization token. */\nfunction captureArguments(args: Readonly<Record<string, unknown>>): {\n readonly json: string;\n executionArgs(): Record<string, unknown>;\n} {\n const text = JSON.stringify(args);\n if (typeof text !== \"string\") {\n throw new TenuoConfigurationError(\"arguments must be JSON\");\n }\n return Object.freeze({\n json: text,\n executionArgs: () => plainArgs(JSON.parse(text)),\n });\n}\n\nfunction presentedEnvelope(\n value: unknown,\n): { warrant: string; signature: string; approvals?: string[] } | undefined {\n if (value === null || typeof value !== \"object\") {\n return undefined;\n }\n const tenuo = value as { warrant?: unknown; signature?: unknown; approvals?: unknown };\n if (typeof tenuo.warrant !== \"string\" || tenuo.warrant.length === 0) {\n return undefined;\n }\n if (typeof tenuo.signature !== \"string\" || tenuo.signature.length === 0) {\n return undefined;\n }\n if (tenuo.approvals != null &&\n (!Array.isArray(tenuo.approvals) || tenuo.approvals.some((item) => typeof item !== \"string\"))) {\n return undefined;\n }\n const approvals = tenuo.approvals ?? undefined;\n return {\n warrant: tenuo.warrant,\n signature: tenuo.signature,\n ...(approvals !== undefined && approvals.length > 0 ? { approvals } : {}),\n };\n}\n\nfunction tenuoEnvelope(\n meta: unknown,\n): { warrant: string; signature: string; approvals?: string[] } | undefined {\n if (meta === null || typeof meta !== \"object\") {\n return undefined;\n }\n const root = meta as { tenuo?: unknown; _meta?: unknown };\n const block = root.tenuo ?? (root._meta as { tenuo?: unknown } | undefined)?.tenuo;\n return presentedEnvelope(block);\n}\n\nconst HANDLER_POLICY_KEYS = new Set([\"allow\", \"onReceipt\", \"nonceStore\", \"onNonceStoreError\"]);\nconst VERIFY_OPTION_KEYS = new Set([\n \"allow\",\n \"onReceipt\",\n \"nonceStore\",\n \"onNonceStoreError\",\n \"requestId\",\n]);\nconst REPLAY_STORE_UNAVAILABLE = \"Replay store unavailable\";\nconst ATTACH_OPTION_KEYS = new Set([\"approvals\", \"onReceipt\", \"requestId\"]);\nfunction assertKnownKeys(value: object, known: ReadonlySet<string>, label: string): void {\n const unknown = Object.keys(value).filter((key) => !known.has(key));\n if (unknown[0] !== undefined) {\n throw new TenuoConfigurationError(\n `${label} has unknown key '${unknown[0]}'. Use ${[...known].join(\", \")}.`,\n );\n }\n}\n\nfunction isHandlerPolicy(value: unknown): value is McpHandlerPolicy {\n if (value === null || typeof value !== \"object\" || Array.isArray(value) || typeof value === \"function\") {\n return false;\n }\n const keys = Object.keys(value);\n assertKnownKeys(value, HANDLER_POLICY_KEYS, \"mcp.handler() policy\");\n const record = value as {\n allow?: unknown;\n onReceipt?: unknown;\n nonceStore?: unknown;\n onNonceStoreError?: unknown;\n };\n const hasAllow =\n \"allow\" in record && record.allow !== null && typeof record.allow === \"object\" && !Array.isArray(record.allow);\n const hasReceipt = \"onReceipt\" in record && typeof record.onReceipt === \"function\";\n const hasNonce = \"nonceStore\" in record && record.nonceStore !== null && typeof record.nonceStore === \"object\";\n const hasStoreError = \"onNonceStoreError\" in record && typeof record.onNonceStoreError === \"function\";\n return keys.length === 0 || hasAllow || hasReceipt || hasNonce || hasStoreError;\n}\n\nasync function admitPop(\n store: { checkAndRecord(popSignature: string): boolean | Promise<boolean> } | undefined,\n signature: string,\n onNonceStoreError?: (error: unknown) => void | Promise<void>,\n): Promise<void> {\n if (store === undefined) {\n return;\n }\n let admitted: boolean | Promise<boolean>;\n try {\n admitted = store.checkAndRecord(signature);\n } catch (error) {\n throw replayStoreUnavailable(error, onNonceStoreError);\n }\n if (isThenable(admitted)) {\n try {\n admitted = await admitted;\n } catch (error) {\n throw replayStoreUnavailable(error, onNonceStoreError);\n }\n }\n if (typeof admitted !== \"boolean\") {\n throw replayStoreUnavailable(\n new TenuoConfigurationError(\"NonceStore.checkAndRecord() must return boolean or Promise<boolean>\"),\n onNonceStoreError,\n );\n }\n if (!admitted) {\n throw new AuthorizationDeniedError(\n \"TENUO_INVALID_POP\",\n \"PoP replay detected — this exact authorization token was already consumed.\",\n );\n }\n}\n\nfunction replayStoreUnavailable(\n cause: unknown,\n onNonceStoreError?: (error: unknown) => void | Promise<void>,\n): TenuoConfigurationError {\n emitIsolated(onNonceStoreError, cause);\n const error = new TenuoConfigurationError(REPLAY_STORE_UNAVAILABLE);\n if (cause !== undefined) {\n error.cause = cause;\n }\n return error;\n}\n\nfunction emitIsolated(\n hook: ((error: unknown) => void | Promise<void>) | undefined,\n error: unknown,\n): void {\n if (hook === undefined) {\n return;\n }\n try {\n const result = hook(error);\n if (isThenable(result)) {\n void Promise.resolve(result).catch(() => undefined);\n }\n } catch {\n // Isolated — must not change the public error.\n }\n}\n\nfunction isThenable(value: unknown): value is Promise<boolean> {\n return value !== null && typeof value === \"object\" && \"then\" in value && typeof value.then === \"function\";\n}\n\nfunction emitReceipt(\n onReceipt: ((receipt: string) => void | Promise<void>) | undefined,\n receipt: string | undefined,\n session?: SessionContract,\n host?: object,\n): void {\n collectReceipt(receipt, session, host);\n emitIsolatedReceipt(onReceipt, receipt);\n}\n\nfunction plainArgs(value: unknown): Record<string, unknown> {\n if (value instanceof Map) {\n return Object.fromEntries(value);\n }\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n return { ...(value as Record<string, unknown>) };\n }\n return {};\n}\n\nfunction jsonRpcError(error: unknown): McpJsonRpcError {\n if (error instanceof TenuoError) {\n const code: TenuoErrorCode = error.code;\n const rpc = code === \"TENUO_APPROVAL_REQUIRED\" ? -32002 : code === \"TENUO_CANONICALIZATION\" ? -32602 : -32001;\n return { code: rpc, message: error.message, data: { tenuo: { code } } };\n }\n const message = error instanceof Error ? error.message : String(error);\n return { code: -32001, message };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n McpAttachOptions,\n McpCallParams,\n McpHandlerPolicy,\n McpJsonRpcError,\n McpVerifyOptions,\n PresentedCall,\n Session as SessionContract,\n TenuoErrorCode,\n TenuoMcp,\n} from \"./api.ts\";\nimport { AuthorizationDeniedError, TenuoConfigurationError, TenuoError } from \"./errors.ts\";\nimport { collectReceipt, emitIsolatedReceipt } from \"./receipts.ts\";\nimport { admitNonce, consumeApprovals, snapshotApprovals } from \"./replay.ts\";\nimport { nativeSession, type Session } from \"./session.ts\";\nimport { loadWasm, type WasmContext, type WasmDecision } from \"./wasm.ts\";\n\nexport type Decide = (decision: WasmDecision, tool: string, native?: object, args?: Record<string, unknown>) => void;\n\n/**\n * Transport-agnostic half of `attach`: authorize locally, then sign\n * proof-of-possession over the call. Shared by `tenuo.present()` and\n * `tenuo.mcp.attach()`.\n */\nexport function presentCall(\n context: WasmContext,\n decide: Decide,\n session: SessionContract,\n name: string,\n args: Readonly<Record<string, unknown>>,\n options: McpAttachOptions | undefined,\n label: string,\n host?: object,\n): { readonly presented: PresentedCall; readonly wireArgs: Record<string, unknown> } {\n if (options !== undefined) {\n assertKnownKeys(options, ATTACH_OPTION_KEYS, `${label} options`);\n }\n const native = nativeSession(session as Session);\n const snapshot = captureArguments(args);\n const local = context.authorize(\n native,\n name,\n snapshot.json,\n options?.approvals,\n undefined,\n options?.requestId,\n );\n emitReceipt(options?.onReceipt, local.receipt, session, host);\n decide(local, name, native, snapshot.executionArgs());\n const envelope = context.signMeta(\n native,\n name,\n snapshot.json,\n Math.floor(Date.now() / 1000),\n options?.approvals ?? null,\n );\n const presented = {\n warrant: envelope.warrant,\n signature: envelope.signature,\n ...(envelope.approvals !== undefined && envelope.approvals.length > 0\n ? { approvals: envelope.approvals }\n : {}),\n };\n return { presented, wireArgs: snapshot.executionArgs() };\n}\n\n/**\n * Transport-agnostic half of `verify`: check a presented warrant chain and\n * proof-of-possession, then apply the host ceiling. Shared by\n * `tenuo.verify()` and `tenuo.mcp.verify()`.\n */\nexport async function verifyPresented(\n context: WasmContext,\n decide: Decide,\n presented: PresentedCall,\n name: string,\n args: Readonly<Record<string, unknown>>,\n options: McpVerifyOptions | undefined,\n label: string,\n host?: object,\n): Promise<Readonly<Record<string, unknown>>> {\n if (options !== undefined) {\n assertKnownKeys(options, VERIFY_OPTION_KEYS, `${label} options`);\n }\n const envelope = presentedEnvelope(presented);\n if (envelope === undefined) {\n throw new TenuoConfigurationError(\n `${label} needs { warrant, signature } from tenuo.present() or tenuo.mcp.attach()`,\n );\n }\n const approvals = snapshotApprovals(envelope.approvals);\n // Capture once: replay admission and receipt callbacks may yield or mutate\n // the caller's object. Execution must use the exact snapshot verified here.\n const snapshot = captureArguments(args);\n const decision = context.authorizePresented(\n envelope.warrant,\n name,\n snapshot.json,\n envelope.signature,\n approvals,\n options?.allow,\n options?.requestId,\n );\n // Rust already signed this envelope. Emit before the nonce store can\n // refuse a replay — otherwise an attacker leaves no audit artifact.\n emitReceipt(options?.onReceipt, decision.receipt, undefined, host);\n if (decision.outcome === \"allow\") {\n await admitPop(options?.nonceStore, envelope.signature, options?.onNonceStoreError);\n // A fresh PoP over the same approvals is a new token; consume the\n // approvals themselves so a nonce store makes them one-use here too.\n await consumeApprovals(\n options?.nonceStore,\n approvals,\n receiptRequestHash(decision.receipt),\n name,\n options?.onNonceStoreError,\n );\n }\n decide(decision, name);\n // Run the host parse of the JSON text the proof covers. The raw host\n // object can contain values that JSON drops.\n return snapshot.executionArgs();\n}\n\nexport function createMcp(context: WasmContext, decide: Decide, host?: object): TenuoMcp {\n const mcp: TenuoMcp = {\n attach(session, name, args, options) {\n const { presented, wireArgs } = presentCall(\n context,\n decide,\n session,\n name,\n args,\n options,\n \"mcp.attach()\",\n host,\n );\n return { name, arguments: wireArgs, _meta: { tenuo: presented } };\n },\n\n async verify(name, args, meta, options) {\n const envelope = tenuoEnvelope(meta);\n if (envelope === undefined) {\n throw new TenuoConfigurationError(\n \"MCP call is missing _meta.tenuo. The client must call tenuo.mcp.attach().\",\n );\n }\n return verifyPresented(context, decide, envelope, name, args, options, \"mcp.verify()\", host);\n },\n\n handler(name, policyOrExecute, maybeExecute?) {\n const policy = isHandlerPolicy(policyOrExecute) ? policyOrExecute : undefined;\n const execute = isHandlerPolicy(policyOrExecute) ? maybeExecute : policyOrExecute;\n if (typeof execute !== \"function\") {\n throw new TenuoConfigurationError(\"mcp.handler() requires an execute function\");\n }\n return async (args, extra) => {\n const authorized = await mcp.verify(name, args, extra?._meta ?? extra?.meta, {\n ...(policy?.allow !== undefined ? { allow: policy.allow } : {}),\n ...(policy?.onReceipt !== undefined ? { onReceipt: policy.onReceipt } : {}),\n ...(policy?.nonceStore !== undefined ? { nonceStore: policy.nonceStore } : {}),\n ...(policy?.onNonceStoreError !== undefined ? { onNonceStoreError: policy.onNonceStoreError } : {}),\n });\n return execute(authorized as never);\n };\n },\n\n jsonRpcError(error) {\n return jsonRpcError(error);\n },\n };\n return mcp;\n}\n\n/** A private immutable input snapshot, not an authorization token. */\nfunction captureArguments(args: Readonly<Record<string, unknown>>): {\n readonly json: string;\n executionArgs(): Record<string, unknown>;\n} {\n const text = JSON.stringify(args);\n if (typeof text !== \"string\") {\n throw new TenuoConfigurationError(\"arguments must be JSON\");\n }\n return Object.freeze({\n json: text,\n executionArgs: () => plainArgs(JSON.parse(text)),\n });\n}\n\nfunction presentedEnvelope(\n value: unknown,\n): { warrant: string; signature: string; approvals?: string[] } | undefined {\n if (value === null || typeof value !== \"object\") {\n return undefined;\n }\n const tenuo = value as { warrant?: unknown; signature?: unknown; approvals?: unknown };\n if (typeof tenuo.warrant !== \"string\" || tenuo.warrant.length === 0) {\n return undefined;\n }\n if (typeof tenuo.signature !== \"string\" || tenuo.signature.length === 0) {\n return undefined;\n }\n if (tenuo.approvals != null &&\n (!Array.isArray(tenuo.approvals) || tenuo.approvals.some((item) => typeof item !== \"string\"))) {\n return undefined;\n }\n const approvals = tenuo.approvals ?? undefined;\n return {\n warrant: tenuo.warrant,\n signature: tenuo.signature,\n ...(approvals !== undefined && approvals.length > 0 ? { approvals } : {}),\n };\n}\n\nfunction tenuoEnvelope(\n meta: unknown,\n): { warrant: string; signature: string; approvals?: string[] } | undefined {\n if (meta === null || typeof meta !== \"object\") {\n return undefined;\n }\n const root = meta as { tenuo?: unknown; _meta?: unknown };\n const block = root.tenuo ?? (root._meta as { tenuo?: unknown } | undefined)?.tenuo;\n return presentedEnvelope(block);\n}\n\nconst HANDLER_POLICY_KEYS = new Set([\"allow\", \"onReceipt\", \"nonceStore\", \"onNonceStoreError\"]);\nconst VERIFY_OPTION_KEYS = new Set([\n \"allow\",\n \"onReceipt\",\n \"nonceStore\",\n \"onNonceStoreError\",\n \"requestId\",\n]);\nconst ATTACH_OPTION_KEYS = new Set([\"approvals\", \"onReceipt\", \"requestId\"]);\nfunction assertKnownKeys(value: object, known: ReadonlySet<string>, label: string): void {\n const unknown = Object.keys(value).filter((key) => !known.has(key));\n if (unknown[0] !== undefined) {\n throw new TenuoConfigurationError(\n `${label} has unknown key '${unknown[0]}'. Use ${[...known].join(\", \")}.`,\n );\n }\n}\n\nfunction isHandlerPolicy(value: unknown): value is McpHandlerPolicy {\n if (value === null || typeof value !== \"object\" || Array.isArray(value) || typeof value === \"function\") {\n return false;\n }\n const keys = Object.keys(value);\n assertKnownKeys(value, HANDLER_POLICY_KEYS, \"mcp.handler() policy\");\n const record = value as {\n allow?: unknown;\n onReceipt?: unknown;\n nonceStore?: unknown;\n onNonceStoreError?: unknown;\n };\n const hasAllow =\n \"allow\" in record && record.allow !== null && typeof record.allow === \"object\" && !Array.isArray(record.allow);\n const hasReceipt = \"onReceipt\" in record && typeof record.onReceipt === \"function\";\n const hasNonce = \"nonceStore\" in record && record.nonceStore !== null && typeof record.nonceStore === \"object\";\n const hasStoreError = \"onNonceStoreError\" in record && typeof record.onNonceStoreError === \"function\";\n return keys.length === 0 || hasAllow || hasReceipt || hasNonce || hasStoreError;\n}\n\nfunction admitPop(\n store: McpVerifyOptions[\"nonceStore\"],\n signature: string,\n onNonceStoreError?: (error: unknown) => void | Promise<void>,\n): Promise<void> {\n return admitNonce(store, signature, onNonceStoreError, () =>\n new AuthorizationDeniedError(\n \"TENUO_INVALID_POP\",\n \"PoP replay detected — this exact authorization token was already consumed.\",\n ),\n );\n}\n\n/** The request hash core wrote into the decision receipt, when there is one. */\nfunction receiptRequestHash(receipt: string | undefined): string | undefined {\n if (receipt === undefined) {\n return undefined;\n }\n try {\n return loadWasm().sdkVerifyReceipt(receipt).request_hash;\n } catch {\n return undefined;\n }\n}\n\nfunction emitReceipt(\n onReceipt: ((receipt: string) => void | Promise<void>) | undefined,\n receipt: string | undefined,\n session?: SessionContract,\n host?: object,\n): void {\n collectReceipt(receipt, session, host);\n emitIsolatedReceipt(onReceipt, receipt);\n}\n\nfunction plainArgs(value: unknown): Record<string, unknown> {\n if (value instanceof Map) {\n return Object.fromEntries(value);\n }\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n return { ...(value as Record<string, unknown>) };\n }\n return {};\n}\n\nfunction jsonRpcError(error: unknown): McpJsonRpcError {\n if (error instanceof TenuoError) {\n const code: TenuoErrorCode = error.code;\n const rpc = code === \"TENUO_APPROVAL_REQUIRED\" ? -32002 : code === \"TENUO_CANONICALIZATION\" ? -32602 : -32001;\n return { code: rpc, message: error.message, data: { tenuo: { code } } };\n }\n const message = error instanceof Error ? error.message : String(error);\n return { code: -32001, message };\n}\n"]}
|
package/dist/replay.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { NonceStore } from "./api.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Record `key` in a `NonceStore`. Throws `replayed()` when the store has
|
|
4
|
+
* already seen it, and the constant "Replay store unavailable" when the
|
|
5
|
+
* store throws, rejects, or returns a non-boolean. Shared by the MCP PoP
|
|
6
|
+
* check and approval consumption.
|
|
7
|
+
*/
|
|
8
|
+
export declare function admitNonce(store: NonceStore | undefined, key: string, onNonceStoreError: ((error: unknown) => void | Promise<void>) | undefined, replayed: () => Error): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Make approvals one-use: record every approval envelope that counts for
|
|
11
|
+
* this call (valid signature and, when `requestHash` is known, bound to this
|
|
12
|
+
* exact request). Run only after core allowed the call. Do not apply a second
|
|
13
|
+
* expiry check here: core intentionally accepts its configured clock-skew
|
|
14
|
+
* grace window, and an approval it accepted must still be consumed.
|
|
15
|
+
*
|
|
16
|
+
* Keys come from signed approval fields, so re-encoding an envelope (hex,
|
|
17
|
+
* base64, bytes) does not make it fresh again.
|
|
18
|
+
*/
|
|
19
|
+
export declare function consumeApprovals(store: NonceStore | undefined, approvals: unknown, requestHash: string | undefined, tool: string, onNonceStoreError: ((error: unknown) => void | Promise<void>) | undefined): Promise<void>;
|
|
20
|
+
export declare function snapshotApprovals(approvals: unknown): unknown;
|
package/dist/replay.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { AuthorizationDeniedError, TenuoConfigurationError } from "./errors.js";
|
|
3
|
+
import { loadWasm } from "./wasm.js";
|
|
4
|
+
const REPLAY_STORE_UNAVAILABLE = "Replay store unavailable";
|
|
5
|
+
/**
|
|
6
|
+
* Record `key` in a `NonceStore`. Throws `replayed()` when the store has
|
|
7
|
+
* already seen it, and the constant "Replay store unavailable" when the
|
|
8
|
+
* store throws, rejects, or returns a non-boolean. Shared by the MCP PoP
|
|
9
|
+
* check and approval consumption.
|
|
10
|
+
*/
|
|
11
|
+
export async function admitNonce(store, key, onNonceStoreError, replayed) {
|
|
12
|
+
if (store === undefined) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
let admitted;
|
|
16
|
+
try {
|
|
17
|
+
admitted = store.checkAndRecord(key);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
throw replayStoreUnavailable(error, onNonceStoreError);
|
|
21
|
+
}
|
|
22
|
+
if (isThenable(admitted)) {
|
|
23
|
+
try {
|
|
24
|
+
admitted = await admitted;
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
throw replayStoreUnavailable(error, onNonceStoreError);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (typeof admitted !== "boolean") {
|
|
31
|
+
throw replayStoreUnavailable(new TenuoConfigurationError("NonceStore.checkAndRecord() must return boolean or Promise<boolean>"), onNonceStoreError);
|
|
32
|
+
}
|
|
33
|
+
if (!admitted) {
|
|
34
|
+
throw replayed();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Make approvals one-use: record every approval envelope that counts for
|
|
39
|
+
* this call (valid signature and, when `requestHash` is known, bound to this
|
|
40
|
+
* exact request). Run only after core allowed the call. Do not apply a second
|
|
41
|
+
* expiry check here: core intentionally accepts its configured clock-skew
|
|
42
|
+
* grace window, and an approval it accepted must still be consumed.
|
|
43
|
+
*
|
|
44
|
+
* Keys come from signed approval fields, so re-encoding an envelope (hex,
|
|
45
|
+
* base64, bytes) does not make it fresh again.
|
|
46
|
+
*/
|
|
47
|
+
export async function consumeApprovals(store, approvals, requestHash, tool, onNonceStoreError) {
|
|
48
|
+
if (store === undefined || !Array.isArray(approvals) || approvals.length === 0) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const expected = requestHash?.toLowerCase();
|
|
52
|
+
const keys = new Set();
|
|
53
|
+
for (const envelope of approvals) {
|
|
54
|
+
const info = inspect(envelope);
|
|
55
|
+
if (!info.signature_valid) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (expected !== undefined && info.request_hash.toLowerCase() !== expected) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
keys.add(approvalKey(info));
|
|
62
|
+
}
|
|
63
|
+
for (const key of keys) {
|
|
64
|
+
await admitNonce(store, key, onNonceStoreError, () => new AuthorizationDeniedError("TENUO_INSUFFICIENT_APPROVALS", `Approval already used: an approval for ${tool} was consumed by an earlier call. Request a new approval.`));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function snapshotApprovals(approvals) {
|
|
68
|
+
if (!Array.isArray(approvals)) {
|
|
69
|
+
return approvals;
|
|
70
|
+
}
|
|
71
|
+
return approvals.map((envelope) => envelope instanceof Uint8Array ? new Uint8Array(envelope) : envelope);
|
|
72
|
+
}
|
|
73
|
+
function inspect(envelope) {
|
|
74
|
+
if (typeof envelope !== "string" && !(envelope instanceof Uint8Array)) {
|
|
75
|
+
throw new TenuoConfigurationError("could not inspect an authorized approval for replay protection");
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
return loadWasm().sdkInspectApproval(envelope);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
const wrapped = new TenuoConfigurationError("could not inspect an authorized approval for replay protection");
|
|
82
|
+
wrapped.cause = error;
|
|
83
|
+
throw wrapped;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function approvalKey(info) {
|
|
87
|
+
const digest = createHash("sha256")
|
|
88
|
+
.update(JSON.stringify([
|
|
89
|
+
info.approver_public_key.toLowerCase(),
|
|
90
|
+
info.request_hash.toLowerCase(),
|
|
91
|
+
info.nonce.toLowerCase(),
|
|
92
|
+
]))
|
|
93
|
+
.digest("hex");
|
|
94
|
+
return `tenuo-approval-v1:${digest}`;
|
|
95
|
+
}
|
|
96
|
+
function replayStoreUnavailable(cause, onNonceStoreError) {
|
|
97
|
+
if (onNonceStoreError !== undefined) {
|
|
98
|
+
try {
|
|
99
|
+
const result = onNonceStoreError(cause);
|
|
100
|
+
if (isThenable(result)) {
|
|
101
|
+
void Promise.resolve(result).catch(() => undefined);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// Isolated — must not change the public error.
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const error = new TenuoConfigurationError(REPLAY_STORE_UNAVAILABLE);
|
|
109
|
+
if (cause !== undefined) {
|
|
110
|
+
error.cause = cause;
|
|
111
|
+
}
|
|
112
|
+
return error;
|
|
113
|
+
}
|
|
114
|
+
function isThenable(value) {
|
|
115
|
+
return value !== null && typeof value === "object" && "then" in value && typeof value.then === "function";
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { NonceStore } from \"./api.ts\";\nimport { AuthorizationDeniedError, TenuoConfigurationError } from \"./errors.ts\";\nimport { loadWasm, type WasmApprovalInfo } from \"./wasm.ts\";\n\nconst REPLAY_STORE_UNAVAILABLE = \"Replay store unavailable\";\n\n/**\n * Record `key` in a `NonceStore`. Throws `replayed()` when the store has\n * already seen it, and the constant \"Replay store unavailable\" when the\n * store throws, rejects, or returns a non-boolean. Shared by the MCP PoP\n * check and approval consumption.\n */\nexport async function admitNonce(\n store: NonceStore | undefined,\n key: string,\n onNonceStoreError: ((error: unknown) => void | Promise<void>) | undefined,\n replayed: () => Error,\n): Promise<void> {\n if (store === undefined) {\n return;\n }\n let admitted: boolean | Promise<boolean>;\n try {\n admitted = store.checkAndRecord(key);\n } catch (error) {\n throw replayStoreUnavailable(error, onNonceStoreError);\n }\n if (isThenable(admitted)) {\n try {\n admitted = await admitted;\n } catch (error) {\n throw replayStoreUnavailable(error, onNonceStoreError);\n }\n }\n if (typeof admitted !== \"boolean\") {\n throw replayStoreUnavailable(\n new TenuoConfigurationError(\"NonceStore.checkAndRecord() must return boolean or Promise<boolean>\"),\n onNonceStoreError,\n );\n }\n if (!admitted) {\n throw replayed();\n }\n}\n\n/**\n * Make approvals one-use: record every approval envelope that counts for\n * this call (valid signature and, when `requestHash` is known, bound to this\n * exact request). Run only after core allowed the call. Do not apply a second\n * expiry check here: core intentionally accepts its configured clock-skew\n * grace window, and an approval it accepted must still be consumed.\n *\n * Keys come from signed approval fields, so re-encoding an envelope (hex,\n * base64, bytes) does not make it fresh again.\n */\nexport async function consumeApprovals(\n store: NonceStore | undefined,\n approvals: unknown,\n requestHash: string | undefined,\n tool: string,\n onNonceStoreError: ((error: unknown) => void | Promise<void>) | undefined,\n): Promise<void> {\n if (store === undefined || !Array.isArray(approvals) || approvals.length === 0) {\n return;\n }\n const expected = requestHash?.toLowerCase();\n const keys = new Set<string>();\n for (const envelope of approvals as unknown[]) {\n const info = inspect(envelope);\n if (!info.signature_valid) {\n continue;\n }\n if (expected !== undefined && info.request_hash.toLowerCase() !== expected) {\n continue;\n }\n keys.add(approvalKey(info));\n }\n for (const key of keys) {\n await admitNonce(store, key, onNonceStoreError, () =>\n new AuthorizationDeniedError(\n \"TENUO_INSUFFICIENT_APPROVALS\",\n `Approval already used: an approval for ${tool} was consumed by an earlier call. Request a new approval.`,\n ),\n );\n }\n}\n\nexport function snapshotApprovals(approvals: unknown): unknown {\n if (!Array.isArray(approvals)) {\n return approvals;\n }\n return approvals.map((envelope) =>\n envelope instanceof Uint8Array ? new Uint8Array(envelope) : envelope,\n );\n}\n\nfunction inspect(envelope: unknown): WasmApprovalInfo {\n if (typeof envelope !== \"string\" && !(envelope instanceof Uint8Array)) {\n throw new TenuoConfigurationError(\"could not inspect an authorized approval for replay protection\");\n }\n try {\n return loadWasm().sdkInspectApproval(envelope);\n } catch (error) {\n const wrapped = new TenuoConfigurationError(\n \"could not inspect an authorized approval for replay protection\",\n );\n wrapped.cause = error;\n throw wrapped;\n }\n}\n\nfunction approvalKey(info: WasmApprovalInfo): string {\n const digest = createHash(\"sha256\")\n .update(\n JSON.stringify([\n info.approver_public_key.toLowerCase(),\n info.request_hash.toLowerCase(),\n info.nonce.toLowerCase(),\n ]),\n )\n .digest(\"hex\");\n return `tenuo-approval-v1:${digest}`;\n}\n\nfunction replayStoreUnavailable(\n cause: unknown,\n onNonceStoreError?: (error: unknown) => void | Promise<void>,\n): TenuoConfigurationError {\n if (onNonceStoreError !== undefined) {\n try {\n const result = onNonceStoreError(cause);\n if (isThenable(result)) {\n void Promise.resolve(result).catch(() => undefined);\n }\n } catch {\n // Isolated — must not change the public error.\n }\n }\n const error = new TenuoConfigurationError(REPLAY_STORE_UNAVAILABLE);\n if (cause !== undefined) {\n error.cause = cause;\n }\n return error;\n}\n\nfunction isThenable(value: unknown): value is Promise<unknown> {\n return value !== null && typeof value === \"object\" && \"then\" in value && typeof value.then === \"function\";\n}\n"]}
|
package/dist/session.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare class Session implements SessionContract {
|
|
|
11
11
|
toString(): string;
|
|
12
12
|
[inspect](): string;
|
|
13
13
|
toWire(): string[];
|
|
14
|
+
toWireString(): string;
|
|
14
15
|
dedupKey(tool: string, args: Readonly<Record<string, unknown>>): string;
|
|
15
16
|
inspect(): SessionInfo;
|
|
16
17
|
/**
|
package/dist/session.js
CHANGED
|
@@ -30,6 +30,17 @@ export class Session {
|
|
|
30
30
|
}
|
|
31
31
|
return tokens;
|
|
32
32
|
}
|
|
33
|
+
toWireString() {
|
|
34
|
+
const native = nativeSessions.get(this);
|
|
35
|
+
if (typeof native?.toStackWire !== "function") {
|
|
36
|
+
throw new TenuoConfigurationError("session is not bound to the WASM core");
|
|
37
|
+
}
|
|
38
|
+
const stack = native.toStackWire();
|
|
39
|
+
if (typeof stack !== "string" || stack.length === 0) {
|
|
40
|
+
throw new TenuoConfigurationError("toWireString() did not return a warrant stack");
|
|
41
|
+
}
|
|
42
|
+
return stack;
|
|
43
|
+
}
|
|
33
44
|
dedupKey(tool, args) {
|
|
34
45
|
const native = nativeSessions.get(this);
|
|
35
46
|
if (typeof native?.dedupKey !== "function") {
|
package/dist/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Session as SessionContract, SessionInfo } from \"./api.ts\";\nimport { TenuoConfigurationError } from \"./errors.ts\";\nimport {\n acknowledgeSessionReceipts,\n drainSessionReceipts,\n peekSessionReceipts,\n} from \"./runtime.ts\";\nimport type { WasmSession, WasmSessionInfo } from \"./wasm.ts\";\n\nconst inspect = Symbol.for(\"nodejs.util.inspect.custom\");\nconst nativeSessions = new WeakMap<Session, WasmSession>();\n\n/**\n * Opaque session handle. Structural clones and JSON are not sessions.\n */\nexport class Session implements SessionContract {\n readonly [Symbol.toStringTag] = \"TenuoSession\" as const;\n\n constructor(native: WasmSession) {\n nativeSessions.set(this, native);\n }\n\n toJSON(): string {\n return \"[TenuoSession]\";\n }\n\n toString(): string {\n return \"[TenuoSession]\";\n }\n\n [inspect](): string {\n return \"[TenuoSession]\";\n }\n\n toWire(): string[] {\n const native = nativeSessions.get(this) as { toWire?: () => unknown } | undefined;\n if (native?.toWire === undefined) {\n throw new TenuoConfigurationError(\"session is not bound to the WASM core\");\n }\n const tokens = native.toWire();\n if (!Array.isArray(tokens) || tokens.some((token) => typeof token !== \"string\")) {\n throw new TenuoConfigurationError(\"toWire() did not return warrant tokens\");\n }\n return tokens;\n }\n\n dedupKey(tool: string, args: Readonly<Record<string, unknown>>): string {\n const native = nativeSessions.get(this) as\n | { dedupKey?: (tool: string, args: unknown) => string }\n | undefined;\n if (typeof native?.dedupKey !== \"function\") {\n throw new TenuoConfigurationError(\"session is not bound to the WASM core\");\n }\n return native.dedupKey(tool, args);\n }\n\n inspect(): SessionInfo {\n const native = nativeSessions.get(this) as { describe?: () => unknown } | undefined;\n if (typeof native?.describe !== \"function\") {\n throw new TenuoConfigurationError(\"session is not bound to the WASM core\");\n }\n const info = native.describe() as WasmSessionInfo;\n const out: {\n -readonly [K in keyof SessionInfo]: SessionInfo[K];\n } = {\n kind: info.kind,\n holderPublicKey: info.holder_public_key,\n rootPublicKey: info.root_public_key,\n depth: info.depth,\n maxDepth: info.max_depth,\n terminal: info.terminal,\n expiresAt: info.expires_at,\n tools: [...info.tools],\n warrantIds: [...info.warrant_ids],\n canAuthorize: info.can_authorize,\n approvalGatedTools: [...info.approval_gated_tools],\n };\n if (info.clearance !== undefined) {\n out.clearance = info.clearance;\n }\n if (info.session_id !== undefined) {\n out.sessionId = info.session_id;\n }\n if (info.agent_id !== undefined) {\n out.agentId = info.agent_id;\n }\n if (info.issuable_tools !== undefined) {\n out.issuableTools = [...info.issuable_tools];\n }\n if (info.max_issue_depth !== undefined) {\n out.maxIssueDepth = info.max_issue_depth;\n }\n if (info.required_approvers !== undefined) {\n out.requiredApprovers = [...info.required_approvers];\n }\n if (info.min_approvals !== undefined) {\n out.minApprovals = info.min_approvals;\n }\n return out;\n }\n\n /**\n * Snapshot of collected receipts. Same as `drainReceipts`; nothing is\n * removed until `acknowledgeReceipts`.\n */\n peekReceipts(): string[] {\n return peekSessionReceipts(this);\n }\n\n drainReceipts(): string[] {\n return drainSessionReceipts(this);\n }\n\n acknowledgeReceipts(count: number): number {\n return acknowledgeSessionReceipts(this, count);\n }\n}\n\nexport function isSession(value: unknown): value is Session {\n return value instanceof Session;\n}\n\nexport function nativeSession(session: Session): WasmSession {\n const native = nativeSessions.get(session);\n if (!native) {\n throw new TenuoConfigurationError(\"session is not bound to the WASM core\");\n }\n return native;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Session as SessionContract, SessionInfo } from \"./api.ts\";\nimport { TenuoConfigurationError } from \"./errors.ts\";\nimport {\n acknowledgeSessionReceipts,\n drainSessionReceipts,\n peekSessionReceipts,\n} from \"./runtime.ts\";\nimport type { WasmSession, WasmSessionInfo } from \"./wasm.ts\";\n\nconst inspect = Symbol.for(\"nodejs.util.inspect.custom\");\nconst nativeSessions = new WeakMap<Session, WasmSession>();\n\n/**\n * Opaque session handle. Structural clones and JSON are not sessions.\n */\nexport class Session implements SessionContract {\n readonly [Symbol.toStringTag] = \"TenuoSession\" as const;\n\n constructor(native: WasmSession) {\n nativeSessions.set(this, native);\n }\n\n toJSON(): string {\n return \"[TenuoSession]\";\n }\n\n toString(): string {\n return \"[TenuoSession]\";\n }\n\n [inspect](): string {\n return \"[TenuoSession]\";\n }\n\n toWire(): string[] {\n const native = nativeSessions.get(this) as { toWire?: () => unknown } | undefined;\n if (native?.toWire === undefined) {\n throw new TenuoConfigurationError(\"session is not bound to the WASM core\");\n }\n const tokens = native.toWire();\n if (!Array.isArray(tokens) || tokens.some((token) => typeof token !== \"string\")) {\n throw new TenuoConfigurationError(\"toWire() did not return warrant tokens\");\n }\n return tokens;\n }\n\n toWireString(): string {\n const native = nativeSessions.get(this) as { toStackWire?: () => unknown } | undefined;\n if (typeof native?.toStackWire !== \"function\") {\n throw new TenuoConfigurationError(\"session is not bound to the WASM core\");\n }\n const stack = native.toStackWire();\n if (typeof stack !== \"string\" || stack.length === 0) {\n throw new TenuoConfigurationError(\"toWireString() did not return a warrant stack\");\n }\n return stack;\n }\n\n dedupKey(tool: string, args: Readonly<Record<string, unknown>>): string {\n const native = nativeSessions.get(this) as\n | { dedupKey?: (tool: string, args: unknown) => string }\n | undefined;\n if (typeof native?.dedupKey !== \"function\") {\n throw new TenuoConfigurationError(\"session is not bound to the WASM core\");\n }\n return native.dedupKey(tool, args);\n }\n\n inspect(): SessionInfo {\n const native = nativeSessions.get(this) as { describe?: () => unknown } | undefined;\n if (typeof native?.describe !== \"function\") {\n throw new TenuoConfigurationError(\"session is not bound to the WASM core\");\n }\n const info = native.describe() as WasmSessionInfo;\n const out: {\n -readonly [K in keyof SessionInfo]: SessionInfo[K];\n } = {\n kind: info.kind,\n holderPublicKey: info.holder_public_key,\n rootPublicKey: info.root_public_key,\n depth: info.depth,\n maxDepth: info.max_depth,\n terminal: info.terminal,\n expiresAt: info.expires_at,\n tools: [...info.tools],\n warrantIds: [...info.warrant_ids],\n canAuthorize: info.can_authorize,\n approvalGatedTools: [...info.approval_gated_tools],\n };\n if (info.clearance !== undefined) {\n out.clearance = info.clearance;\n }\n if (info.session_id !== undefined) {\n out.sessionId = info.session_id;\n }\n if (info.agent_id !== undefined) {\n out.agentId = info.agent_id;\n }\n if (info.issuable_tools !== undefined) {\n out.issuableTools = [...info.issuable_tools];\n }\n if (info.max_issue_depth !== undefined) {\n out.maxIssueDepth = info.max_issue_depth;\n }\n if (info.required_approvers !== undefined) {\n out.requiredApprovers = [...info.required_approvers];\n }\n if (info.min_approvals !== undefined) {\n out.minApprovals = info.min_approvals;\n }\n return out;\n }\n\n /**\n * Snapshot of collected receipts. Same as `drainReceipts`; nothing is\n * removed until `acknowledgeReceipts`.\n */\n peekReceipts(): string[] {\n return peekSessionReceipts(this);\n }\n\n drainReceipts(): string[] {\n return drainSessionReceipts(this);\n }\n\n acknowledgeReceipts(count: number): number {\n return acknowledgeSessionReceipts(this, count);\n }\n}\n\nexport function isSession(value: unknown): value is Session {\n return value instanceof Session;\n}\n\nexport function nativeSession(session: Session): WasmSession {\n const native = nativeSessions.get(session);\n if (!native) {\n throw new TenuoConfigurationError(\"session is not bound to the WASM core\");\n }\n return native;\n}\n"]}
|
package/dist/wasm.d.ts
CHANGED
|
@@ -15,6 +15,18 @@ export type WasmInspect = {
|
|
|
15
15
|
signature_hex: string;
|
|
16
16
|
id: string;
|
|
17
17
|
};
|
|
18
|
+
export type ConstraintViolationKind = "tool_not_authorized" | "unknown_field" | "missing_required" | "constraint_mismatch";
|
|
19
|
+
export type ConstraintDiagnostics = {
|
|
20
|
+
closedWorld: boolean;
|
|
21
|
+
violations: Array<{
|
|
22
|
+
field: string;
|
|
23
|
+
kind: ConstraintViolationKind;
|
|
24
|
+
reason: string;
|
|
25
|
+
}>;
|
|
26
|
+
};
|
|
27
|
+
type WasmConstraintDiagnostics = Omit<ConstraintDiagnostics, "closedWorld"> & {
|
|
28
|
+
closed_world: boolean;
|
|
29
|
+
};
|
|
18
30
|
export type WasmReceipt = {
|
|
19
31
|
authentic: true;
|
|
20
32
|
/** Hex key the receipt is signed under — resolve against your authorizer set. */
|
|
@@ -193,6 +205,7 @@ export type WasmAttestation = {
|
|
|
193
205
|
export type WasmApprovalInfo = {
|
|
194
206
|
approver_public_key: string;
|
|
195
207
|
request_hash: string;
|
|
208
|
+
nonce: string;
|
|
196
209
|
external_id: string;
|
|
197
210
|
approved_at: number;
|
|
198
211
|
expires_at: number;
|
|
@@ -246,12 +259,13 @@ type Generated = {
|
|
|
246
259
|
fromChain(parts: unknown, holder: Uint8Array): WasmSession;
|
|
247
260
|
};
|
|
248
261
|
sdkInspectWarrant(wire: string): WasmInspect;
|
|
262
|
+
sdkDiagnoseConstraints(wire: string, tool: string, args: unknown): WasmConstraintDiagnostics;
|
|
249
263
|
sdkInspectParts(payloadHex: string, signatureHex: string): WasmInspect;
|
|
250
264
|
sdkProtocolLimits(): WasmProtocolLimits;
|
|
251
265
|
sdkPublicKeyFromHolderKey(holderSecret: Uint8Array): string;
|
|
252
266
|
sdkSignApproval(session: WasmSession, tool: string, args: unknown, approverSecret: Uint8Array, externalId: string, asOf?: number): string;
|
|
253
267
|
sdkSignApprovalForRequest(requestHashHex: string, approverSecret: Uint8Array, externalId: string, ttlSeconds?: number, warrantExpiresAt?: number): string;
|
|
254
|
-
sdkInspectApproval(envelope: string): WasmApprovalInfo;
|
|
268
|
+
sdkInspectApproval(envelope: string | Uint8Array): WasmApprovalInfo;
|
|
255
269
|
sdkSignRevocationList(ids: string[], issuerSecret: Uint8Array): string;
|
|
256
270
|
sdkSignRevocationListVersioned(ids: string[], version: number | undefined, issuerSecret: Uint8Array): string;
|
|
257
271
|
sdkSignPublishedRevocationList(ids: string[], version: number, issuerSecret: Uint8Array): string;
|
|
@@ -270,7 +284,18 @@ type Generated = {
|
|
|
270
284
|
error?: string;
|
|
271
285
|
};
|
|
272
286
|
parse_strict_json(jsonText: string): void;
|
|
287
|
+
shlex_inspect(allow: string[], command: string): ShlexCheck;
|
|
273
288
|
};
|
|
289
|
+
/** Decision from the core Shlex check. The same object Python `Shlex.check` returns. */
|
|
290
|
+
export interface ShlexCheck {
|
|
291
|
+
allowed: boolean;
|
|
292
|
+
reason: string;
|
|
293
|
+
tokens: string[];
|
|
294
|
+
operators: string[];
|
|
295
|
+
expansion: string[];
|
|
296
|
+
controls: string[];
|
|
297
|
+
binary_allowed: boolean;
|
|
298
|
+
}
|
|
274
299
|
export declare function wasmAvailable(): boolean;
|
|
275
300
|
export declare function loadWasm(): Generated;
|
|
276
301
|
export declare function createDevContext(receiptSigner?: Uint8Array): WasmContext;
|
|
@@ -281,6 +306,12 @@ export declare function importSessionFromWire(warrant: string, holderKey: Uint8A
|
|
|
281
306
|
export declare function importSessionFromParts(payloadHex: string, signatureHex: string, holderKey: Uint8Array): WasmSession;
|
|
282
307
|
export declare function importSessionFromChain(parts: unknown, holderKey: Uint8Array): WasmSession;
|
|
283
308
|
export declare function inspectWarrant(wire: string): WasmInspect;
|
|
309
|
+
/**
|
|
310
|
+
* List every capability-constraint failure in deterministic order.
|
|
311
|
+
* Diagnostic only: this does not verify signatures, expiry, revocation,
|
|
312
|
+
* approvals, or proof-of-possession, and must not be used to authorize.
|
|
313
|
+
*/
|
|
314
|
+
export declare function diagnoseConstraints(wire: string, tool: string, args: unknown): ConstraintDiagnostics;
|
|
284
315
|
export declare function inspectParts(payloadHex: string, signatureHex: string): WasmInspect;
|
|
285
316
|
/** Hex public key for a 32-byte holder secret. Ed25519, derived in Rust. */
|
|
286
317
|
export declare function publicKeyHexFromHolderKey(holderSecret: Uint8Array): string;
|
|
@@ -310,6 +341,14 @@ export type ApprovalGateInspection = {
|
|
|
310
341
|
* property and `null` stays `null`.
|
|
311
342
|
*/
|
|
312
343
|
export declare function parseStrictJson(text: string): unknown;
|
|
344
|
+
/**
|
|
345
|
+
* The core's Shlex decision for one command.
|
|
346
|
+
*
|
|
347
|
+
* `allow` is the binary allowlist. Each entry matches the command word
|
|
348
|
+
* literally, and any command word containing `..` is rejected. The reason is
|
|
349
|
+
* the same string the warrant uses.
|
|
350
|
+
*/
|
|
351
|
+
export declare function checkShlex(allow: readonly string[], command: string): ShlexCheck;
|
|
313
352
|
/**
|
|
314
353
|
* Typed preflight of a warrant's approval gates for `(tool, args)`.
|
|
315
354
|
*
|
package/dist/wasm.js
CHANGED
|
@@ -76,6 +76,15 @@ export function importSessionFromChain(parts, holderKey) {
|
|
|
76
76
|
export function inspectWarrant(wire) {
|
|
77
77
|
return loadWasm().sdkInspectWarrant(wire);
|
|
78
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* List every capability-constraint failure in deterministic order.
|
|
81
|
+
* Diagnostic only: this does not verify signatures, expiry, revocation,
|
|
82
|
+
* approvals, or proof-of-possession, and must not be used to authorize.
|
|
83
|
+
*/
|
|
84
|
+
export function diagnoseConstraints(wire, tool, args) {
|
|
85
|
+
const result = loadWasm().sdkDiagnoseConstraints(wire, tool, args);
|
|
86
|
+
return { closedWorld: result.closed_world, violations: result.violations };
|
|
87
|
+
}
|
|
79
88
|
export function inspectParts(payloadHex, signatureHex) {
|
|
80
89
|
return loadWasm().sdkInspectParts(payloadHex, signatureHex);
|
|
81
90
|
}
|
|
@@ -103,6 +112,16 @@ export function parseStrictJson(text) {
|
|
|
103
112
|
loadWasm().parse_strict_json(text);
|
|
104
113
|
return JSON.parse(text);
|
|
105
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* The core's Shlex decision for one command.
|
|
117
|
+
*
|
|
118
|
+
* `allow` is the binary allowlist. Each entry matches the command word
|
|
119
|
+
* literally, and any command word containing `..` is rejected. The reason is
|
|
120
|
+
* the same string the warrant uses.
|
|
121
|
+
*/
|
|
122
|
+
export function checkShlex(allow, command) {
|
|
123
|
+
return loadWasm().shlex_inspect([...allow], command);
|
|
124
|
+
}
|
|
106
125
|
/**
|
|
107
126
|
* Typed preflight of a warrant's approval gates for `(tool, args)`.
|
|
108
127
|
*
|