@mudraid/adapter-node 1.1.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/LICENSE +201 -0
- package/README.md +34 -0
- package/dist/controlLoop.d.ts +57 -0
- package/dist/controlLoop.js +245 -0
- package/dist/controlLoop.js.map +1 -0
- package/dist/decideClient.d.ts +16 -0
- package/dist/decideClient.js +22 -0
- package/dist/decideClient.js.map +1 -0
- package/dist/executionBinding.d.ts +13 -0
- package/dist/executionBinding.js +40 -0
- package/dist/executionBinding.js.map +1 -0
- package/dist/httpAuthority.d.ts +32 -0
- package/dist/httpAuthority.js +196 -0
- package/dist/httpAuthority.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/signedBundle.d.ts +20 -0
- package/dist/signedBundle.js +129 -0
- package/dist/signedBundle.js.map +1 -0
- package/dist/types.d.ts +98 -0
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -0
- package/package.json +38 -0
- package/src/controlLoop.ts +387 -0
- package/src/decideClient.ts +25 -0
- package/src/executionBinding.ts +43 -0
- package/src/httpAuthority.ts +185 -0
- package/src/index.ts +38 -0
- package/src/signedBundle.ts +130 -0
- package/src/types.ts +128 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executionBinding.js","sourceRoot":"","sources":["../src/executionBinding.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,OAAO,EAAC,UAAU,EAAC,MAAM,aAAa,CAAC;AACvC,OAAO,EAAC,aAAa,EAAuC,MAAM,mBAAmB,CAAC;AAUtF,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,KAA0B,EAAU,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAE/G,MAAM,UAAU,aAAa,CAAC,QAAwB,EAAE,MAA4B,EAAE,OAAyB;IAC7G,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,YAAY,UAAU,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAChI,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC5I,IAAI,KAAK,GAAG,OAAO,CAAC,sBAAsB,CAAC,IAAI,EAAE,CAAC;IAClD,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7E,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,6BAA6B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IACtK,MAAM,QAAQ,GAAe;QAC3B,OAAO,EAAE,6BAA6B,EAAE,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;QACzE,YAAY,EAAE,OAAO,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI;QACtF,mBAAmB,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC;QAChF,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,wBAAwB,CAAC;QAClG,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,gBAAgB,CAAC;QAC1E,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC,EAAE,eAAe,EAAE,MAAM,CAAC,kBAAkB,CAAC;QAC7E,cAAc,EAAE,QAAQ,CAAC,OAAO,EAAE,qBAAqB,EAAE,QAAQ,CAAC,MAAM;QACxE,eAAe,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE;KAC7C,CAAC;IACF,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClJ,OAAO,EAAC,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE;YAC1D,OAAO,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,YAAY,EAAE,QAAQ,CAAC,cAAc,CAAC;YAC1G,GAAG,CAAC,MAAM,CAAC,kBAAkB,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAC,WAAW,EAAE,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,EAAC,CAAC;SAChG,EAAC,CAAC;AACL,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAgB;IAC3C,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC,IAAI,IAAI,CAAC,UAAU,GAAG,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACtG,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type BundleBinding, type VerifiedBundle } from './signedBundle.js';
|
|
2
|
+
import type { DecideResult } from './types.js';
|
|
3
|
+
import { type ExecutionContext } from './executionBinding.js';
|
|
4
|
+
export type InvocationContext = ExecutionContext;
|
|
5
|
+
export interface AuthorityOptions {
|
|
6
|
+
readonly apiBase: string;
|
|
7
|
+
readonly adapterToken: string;
|
|
8
|
+
readonly binding: BundleBinding;
|
|
9
|
+
readonly timeoutMs?: number;
|
|
10
|
+
readonly adapterType?: 'node_server_adapter' | 'node_sidecar';
|
|
11
|
+
readonly fetch?: typeof fetch;
|
|
12
|
+
}
|
|
13
|
+
export declare class HttpAuthority {
|
|
14
|
+
private readonly base;
|
|
15
|
+
private readonly token;
|
|
16
|
+
private readonly binding;
|
|
17
|
+
private readonly timeout;
|
|
18
|
+
private readonly adapterType;
|
|
19
|
+
private readonly fetcher;
|
|
20
|
+
private current;
|
|
21
|
+
private lastAccepted;
|
|
22
|
+
private decisionKeys;
|
|
23
|
+
private refreshPending;
|
|
24
|
+
private observed;
|
|
25
|
+
constructor(options: AuthorityOptions);
|
|
26
|
+
get bundle(): VerifiedBundle | undefined;
|
|
27
|
+
private request;
|
|
28
|
+
/** Single-flight refresh. A failed refresh never activates unverified data. */
|
|
29
|
+
refresh(): Promise<boolean>;
|
|
30
|
+
private refreshOnce;
|
|
31
|
+
decide(toolName: string, context: InvocationContext, snapshot?: VerifiedBundle | undefined): Promise<DecideResult>;
|
|
32
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/** Authenticated, bounded authority transport. No decision or upstream retries. */
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { instant, object, verifyBundle, verifyClaims } from './signedBundle.js';
|
|
4
|
+
import { bindExecution } from './executionBinding.js';
|
|
5
|
+
function keyMap(entries) {
|
|
6
|
+
if (!Array.isArray(entries))
|
|
7
|
+
throw new Error('Invalid verification key set');
|
|
8
|
+
const keys = Object.create(null);
|
|
9
|
+
for (const entry of entries) {
|
|
10
|
+
const row = object(entry);
|
|
11
|
+
if (typeof row['key_id'] !== 'string' || typeof row['public_key_pem'] !== 'string' || Object.hasOwn(keys, row['key_id']))
|
|
12
|
+
throw new Error('Invalid verification key');
|
|
13
|
+
keys[row['key_id']] = row['public_key_pem'];
|
|
14
|
+
}
|
|
15
|
+
return keys;
|
|
16
|
+
}
|
|
17
|
+
export class HttpAuthority {
|
|
18
|
+
base;
|
|
19
|
+
token;
|
|
20
|
+
binding;
|
|
21
|
+
timeout;
|
|
22
|
+
adapterType;
|
|
23
|
+
fetcher;
|
|
24
|
+
current;
|
|
25
|
+
lastAccepted;
|
|
26
|
+
decisionKeys = Object.create(null);
|
|
27
|
+
refreshPending;
|
|
28
|
+
observed;
|
|
29
|
+
constructor(options) {
|
|
30
|
+
this.base = new URL(options.apiBase);
|
|
31
|
+
if (this.base.protocol !== 'https:' || this.base.username || this.base.password || this.base.search || this.base.hash || this.base.pathname !== '/')
|
|
32
|
+
throw new Error('Authority must be an HTTPS origin');
|
|
33
|
+
if (!options.adapterToken || options.adapterToken.length > 256 || /\s/.test(options.adapterToken))
|
|
34
|
+
throw new Error('Invalid adapter credential');
|
|
35
|
+
this.token = options.adapterToken;
|
|
36
|
+
this.adapterType = options.adapterType ?? 'node_server_adapter';
|
|
37
|
+
this.binding = Object.freeze({ ...options.binding });
|
|
38
|
+
this.timeout = options.timeoutMs ?? 5000;
|
|
39
|
+
if (!Number.isSafeInteger(this.timeout) || this.timeout < 1 || this.timeout > 30000)
|
|
40
|
+
throw new Error('Invalid authority timeout');
|
|
41
|
+
this.fetcher = options.fetch ?? globalThis.fetch;
|
|
42
|
+
}
|
|
43
|
+
get bundle() {
|
|
44
|
+
return this.current && this.current.expiresAt > Date.now() ? this.current : undefined;
|
|
45
|
+
}
|
|
46
|
+
async request(path, method, body, authenticated = true) {
|
|
47
|
+
const controller = new AbortController();
|
|
48
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
49
|
+
try {
|
|
50
|
+
const response = await this.fetcher(new URL(`/api/v1/adapter/enforcement/${path}`, this.base), {
|
|
51
|
+
method, redirect: 'error', signal: controller.signal,
|
|
52
|
+
headers: { Accept: 'application/json', ...(authenticated ? { Authorization: `Bearer ${this.token}` } : {}), ...(body === undefined ? {} : { 'Content-Type': 'application/json' }) },
|
|
53
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
54
|
+
});
|
|
55
|
+
if (!response.ok || !response.body)
|
|
56
|
+
throw new Error('Authority unavailable');
|
|
57
|
+
const reader = response.body.getReader();
|
|
58
|
+
const chunks = [];
|
|
59
|
+
let size = 0;
|
|
60
|
+
try {
|
|
61
|
+
while (true) {
|
|
62
|
+
const item = await reader.read();
|
|
63
|
+
if (item.done)
|
|
64
|
+
break;
|
|
65
|
+
size += item.value.byteLength;
|
|
66
|
+
if (size > 2 * 1024 * 1024)
|
|
67
|
+
throw new Error('Authority response too large');
|
|
68
|
+
chunks.push(item.value);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
await reader.cancel();
|
|
73
|
+
}
|
|
74
|
+
return object(JSON.parse(Buffer.concat(chunks).toString('utf8')));
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
clearTimeout(timer);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** Single-flight refresh. A failed refresh never activates unverified data. */
|
|
81
|
+
refresh() {
|
|
82
|
+
if (!this.refreshPending)
|
|
83
|
+
this.refreshPending = this.refreshOnce().finally(() => { this.refreshPending = undefined; });
|
|
84
|
+
return this.refreshPending;
|
|
85
|
+
}
|
|
86
|
+
async refreshOnce() {
|
|
87
|
+
try {
|
|
88
|
+
const heartbeat = await this.request('heartbeat', 'POST');
|
|
89
|
+
if (heartbeat['platform_id'] !== this.binding.platformId) {
|
|
90
|
+
this.current = undefined;
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const keyResponse = await this.request('keys', 'GET', undefined, false);
|
|
94
|
+
const bundleKeys = keyMap(keyResponse['keys']);
|
|
95
|
+
this.decisionKeys = Object.create(null);
|
|
96
|
+
if (Array.isArray(keyResponse['key_sets'])) {
|
|
97
|
+
for (const item of keyResponse['key_sets']) {
|
|
98
|
+
const set = object(item);
|
|
99
|
+
if (set['purpose'] === 'enforcement_decision_signing')
|
|
100
|
+
this.decisionKeys = keyMap(set['keys']);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const served = await this.request('bundle', 'GET');
|
|
104
|
+
const verified = verifyBundle(served, bundleKeys, this.binding, this.lastAccepted);
|
|
105
|
+
// Heartbeat attribution is authoritative; a stale advertised bundle is not active.
|
|
106
|
+
if (heartbeat['desired_bundle_version'] !== verified.version || heartbeat['desired_payload_digest'] !== verified.digest)
|
|
107
|
+
throw new Error('Desired bundle mismatch');
|
|
108
|
+
this.current = verified;
|
|
109
|
+
this.lastAccepted = verified;
|
|
110
|
+
const now = new Date().toISOString();
|
|
111
|
+
const observedAt = this.observed?.version === verified.version && this.observed.digest === verified.digest
|
|
112
|
+
? this.observed.at : undefined;
|
|
113
|
+
await this.request('acknowledgements', 'POST', {
|
|
114
|
+
report_id: randomUUID(), received_version: verified.version, received_at: now,
|
|
115
|
+
validated_version: verified.version, validated_at: now, active_version: verified.version,
|
|
116
|
+
active_at: now, bundle_digest: verified.digest,
|
|
117
|
+
...(observedAt === undefined ? {} : { first_observed_decision_at: observedAt }),
|
|
118
|
+
});
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// Fail closed until the next verified refresh; do not hide key revocation
|
|
123
|
+
// behind a cached bundle after an authoritative key-set replacement.
|
|
124
|
+
this.current = undefined;
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async decide(toolName, context, snapshot = this.bundle) {
|
|
129
|
+
if (!snapshot || snapshot !== this.bundle)
|
|
130
|
+
return { status: 'unconfigured' };
|
|
131
|
+
const mapped = snapshot.actions[toolName];
|
|
132
|
+
if (!mapped)
|
|
133
|
+
return { status: 'deny', reason: 'action_unmapped' };
|
|
134
|
+
if (!context.presentedAuthorization || context.presentedAuthorization.length > 8192)
|
|
135
|
+
return { status: 'deny', reason: 'credential_missing' };
|
|
136
|
+
const decisionId = randomUUID();
|
|
137
|
+
const action = mapped['action_key'];
|
|
138
|
+
try {
|
|
139
|
+
const bound = bindExecution(snapshot, mapped, context);
|
|
140
|
+
const response = await this.request('decide', 'POST', {
|
|
141
|
+
schema_version: 'mudraid.enforce.decide-request/1', decision_id: decisionId,
|
|
142
|
+
adapter: { type: this.adapterType, version: '1.1.0' },
|
|
143
|
+
bundle: { version: snapshot.version, payload_digest: snapshot.digest },
|
|
144
|
+
surface: snapshot.surface, action: mapped,
|
|
145
|
+
request: { transport: 'mcp_streamable_http', http_method: context.httpMethod, path: context.path },
|
|
146
|
+
presented_authorization: context.presentedAuthorization, execution: bound.execution,
|
|
147
|
+
});
|
|
148
|
+
if (response['schema_version'] !== '2.0' || response['decision_id'] !== decisionId)
|
|
149
|
+
throw new Error('Unbound decision');
|
|
150
|
+
const now = Date.now();
|
|
151
|
+
const decidedAt = instant(response['decided_at']);
|
|
152
|
+
if (decidedAt > now + 30000 || now - decidedAt > 60000)
|
|
153
|
+
throw new Error('Stale decision');
|
|
154
|
+
if (instant(response['deadline_at']) <= now)
|
|
155
|
+
throw new Error('Expired decision');
|
|
156
|
+
// This new runtime always requires signed decisions; there is no downgrade toggle.
|
|
157
|
+
const signature = object(response['signature']);
|
|
158
|
+
const claims = object(signature['claims']);
|
|
159
|
+
const keyId = signature['key_id'];
|
|
160
|
+
const profile = 'mudraid.decision.signature/1';
|
|
161
|
+
if (typeof keyId !== 'string' || !Object.hasOwn(this.decisionKeys, keyId) || signature['profile'] !== profile || signature['algorithm'] !== 'RS256' || claims['profile'] !== profile || claims['algorithm'] !== 'RS256' || claims['key_id'] !== keyId)
|
|
162
|
+
throw new Error('Invalid decision signature');
|
|
163
|
+
verifyClaims(claims, signature['signature'], this.decisionKeys[keyId]);
|
|
164
|
+
if (claims['execution_request_digest'] !== bound.digest)
|
|
165
|
+
throw new Error('Execution binding mismatch');
|
|
166
|
+
for (const field of ['decision_id', 'decision', 'outcome', 'decided_at', 'deadline_at']) {
|
|
167
|
+
if ((claims[field] ?? null) !== (response[field] ?? null))
|
|
168
|
+
throw new Error('Altered decision');
|
|
169
|
+
}
|
|
170
|
+
const reason = object(response['reason']);
|
|
171
|
+
if (claims['reason_primary'] !== reason['primary'])
|
|
172
|
+
throw new Error('Altered reason');
|
|
173
|
+
for (const [field, expected] of Object.entries({ platform_id: this.binding.platformId, environment: this.binding.environment, resource: this.binding.resource, action_key: action, bundle_version: snapshot.version })) {
|
|
174
|
+
if (claims[field] !== expected)
|
|
175
|
+
throw new Error('Decision binding mismatch');
|
|
176
|
+
}
|
|
177
|
+
if (instant(claims['not_before']) > now + 30000 || instant(claims['expires_at']) <= now)
|
|
178
|
+
throw new Error('Invalid decision window');
|
|
179
|
+
if (snapshot !== this.bundle)
|
|
180
|
+
throw new Error('Bundle changed during decision');
|
|
181
|
+
if (response['decision'] !== 'allow' && response['decision'] !== 'deny')
|
|
182
|
+
throw new Error('Invalid decision outcome');
|
|
183
|
+
// Observation is a verified decision, not proof that the application
|
|
184
|
+
// executed it. Report on the next refresh without delaying execution or
|
|
185
|
+
// replaying a decision. Keep at most one bundle's observation in memory.
|
|
186
|
+
if (this.observed?.version !== snapshot.version || this.observed.digest !== snapshot.digest) {
|
|
187
|
+
this.observed = { version: snapshot.version, digest: snapshot.digest, at: new Date(now).toISOString() };
|
|
188
|
+
}
|
|
189
|
+
return { status: response['decision'], decisionId, ...(typeof reason['primary'] === 'string' ? { reason: reason['primary'] } : {}) };
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return { status: 'error' };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=httpAuthority.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httpAuthority.js","sourceRoot":"","sources":["../src/httpAuthority.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAA4D,MAAM,mBAAmB,CAAC;AAG1I,OAAO,EAAC,aAAa,EAAwB,MAAM,uBAAuB,CAAC;AAW3E,SAAS,MAAM,CAAC,OAAgB;IAC9B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAC7E,MAAM,IAAI,GAA2B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACzD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,gBAAgB,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QACtK,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,GAAG,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,OAAO,aAAa;IACP,IAAI,CAAM;IACV,KAAK,CAAS;IACd,OAAO,CAAgB;IACvB,OAAO,CAAS;IAChB,WAAW,CAAyC;IACpD,OAAO,CAAe;IAC/B,OAAO,CAA6B;IACpC,YAAY,CAA6B;IACzC,YAAY,GAA2B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3D,cAAc,CAA+B;IAC7C,QAAQ,CAA4D;IAE5E,YAAY,OAAyB;QACnC,IAAI,CAAC,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,KAAK,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAC1M,IAAI,CAAC,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACjJ,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC;QAClC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,qBAAqB,CAAC;QAChE,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,EAAC,GAAG,OAAO,CAAC,OAAO,EAAC,CAAC,CAAC;QACnD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,GAAG,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAClI,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC;IACnD,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACxF,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,IAAY,EAAE,MAAc,EAAE,IAAc,EAAE,aAAa,GAAG,IAAI;QACtF,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACjE,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,+BAA+B,IAAI,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE;gBAC7F,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM;gBACpD,OAAO,EAAE,EAAC,MAAM,EAAE,kBAAkB,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAC,aAAa,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAC,cAAc,EAAE,kBAAkB,EAAC,CAAC,EAAC;gBAC7K,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAC,CAAC;aAC5D,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;YAC7E,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACzC,MAAM,MAAM,GAAiB,EAAE,CAAC;YAChC,IAAI,IAAI,GAAG,CAAC,CAAC;YACb,IAAI,CAAC;gBACH,OAAO,IAAI,EAAE,CAAC;oBACZ,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;oBACjC,IAAI,IAAI,CAAC,IAAI;wBAAE,MAAM;oBACrB,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;oBAC9B,IAAI,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI;wBAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;oBAC5E,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;oBAAS,CAAC;gBACT,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;YACxB,CAAC;YACD,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACpE,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,+EAA+E;IAC/E,OAAO;QACL,IAAI,CAAC,IAAI,CAAC,cAAc;YAAE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QACvH,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAEO,KAAK,CAAC,WAAW;QACvB,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YAC1D,IAAI,SAAS,CAAC,aAAa,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;gBACzD,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;gBACzB,OAAO,KAAK,CAAC;YACf,CAAC;YACD,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;YACxE,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;YAC/C,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;gBAC3C,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC3C,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;oBACzB,IAAI,GAAG,CAAC,SAAS,CAAC,KAAK,8BAA8B;wBAAE,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;gBACjG,CAAC;YACH,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YACnD,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;YACnF,mFAAmF;YACnF,IAAI,SAAS,CAAC,wBAAwB,CAAC,KAAK,QAAQ,CAAC,OAAO,IAAI,SAAS,CAAC,wBAAwB,CAAC,KAAK,QAAQ,CAAC,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;YACpK,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC;YACxB,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC;YAC7B,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACrC,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,OAAO,KAAK,QAAQ,CAAC,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;gBACxG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;YACjC,MAAM,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,MAAM,EAAE;gBAC7C,SAAS,EAAE,UAAU,EAAE,EAAE,gBAAgB,EAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG;gBAC7E,iBAAiB,EAAE,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,cAAc,EAAE,QAAQ,CAAC,OAAO;gBACxF,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,QAAQ,CAAC,MAAM;gBAC9C,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAC,0BAA0B,EAAE,UAAU,EAAC,CAAC;aAC9E,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,0EAA0E;YAC1E,qEAAqE;YACrE,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;YACzB,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,QAAgB,EAAE,OAA0B,EAAE,QAAQ,GAAG,IAAI,CAAC,MAAM;QAC/E,IAAI,CAAC,QAAQ,IAAI,QAAQ,KAAK,IAAI,CAAC,MAAM;YAAE,OAAO,EAAC,MAAM,EAAE,cAAc,EAAC,CAAC;QAC3E,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM;YAAE,OAAO,EAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAC,CAAC;QAChE,IAAI,CAAC,OAAO,CAAC,sBAAsB,IAAI,OAAO,CAAC,sBAAsB,CAAC,MAAM,GAAG,IAAI;YAAE,OAAO,EAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,EAAC,CAAC;QAC3I,MAAM,UAAU,GAAG,UAAU,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YACvD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE;gBACpD,cAAc,EAAE,kCAAkC,EAAE,WAAW,EAAE,UAAU;gBAC3E,OAAO,EAAE,EAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,EAAC;gBACnD,MAAM,EAAE,EAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,CAAC,MAAM,EAAC;gBACpE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM;gBACzC,OAAO,EAAE,EAAC,SAAS,EAAE,qBAAqB,EAAE,WAAW,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAC;gBAChG,uBAAuB,EAAE,OAAO,CAAC,sBAAsB,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS;aACpF,CAAC,CAAC;YACH,IAAI,QAAQ,CAAC,gBAAgB,CAAC,KAAK,KAAK,IAAI,QAAQ,CAAC,aAAa,CAAC,KAAK,UAAU;gBAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;YACxH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC;YAClD,IAAI,SAAS,GAAG,GAAG,GAAG,KAAK,IAAI,GAAG,GAAG,SAAS,GAAG,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;YAC1F,IAAI,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,IAAI,GAAG;gBAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;YACjF,mFAAmF;YACnF,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;YAChD,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC3C,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;YAClC,MAAM,OAAO,GAAG,8BAA8B,CAAC;YAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,IAAI,SAAS,CAAC,SAAS,CAAC,KAAK,OAAO,IAAI,SAAS,CAAC,WAAW,CAAC,KAAK,OAAO,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,OAAO,IAAI,MAAM,CAAC,WAAW,CAAC,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;YACrS,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;YACvE,IAAI,MAAM,CAAC,0BAA0B,CAAC,KAAK,KAAK,CAAC,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;YACvG,KAAK,MAAM,KAAK,IAAI,CAAC,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,CAAC,EAAE,CAAC;gBACxF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;YACjG,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC1C,IAAI,MAAM,CAAC,gBAAgB,CAAC,KAAK,MAAM,CAAC,SAAS,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;YACtF,KAAK,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,EAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,CAAC,OAAO,EAAC,CAAC,EAAE,CAAC;gBACrN,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,QAAQ;oBAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;YAC/E,CAAC;YACD,IAAI,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,GAAG,GAAG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,GAAG;gBAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;YACpI,IAAI,QAAQ,KAAK,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;YAChF,IAAI,QAAQ,CAAC,UAAU,CAAC,KAAK,OAAO,IAAI,QAAQ,CAAC,UAAU,CAAC,KAAK,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;YACrH,qEAAqE;YACrE,wEAAwE;YACxE,yEAAyE;YACzE,IAAI,IAAI,CAAC,QAAQ,EAAE,OAAO,KAAK,QAAQ,CAAC,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;gBAC5F,IAAI,CAAC,QAAQ,GAAG,EAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAC,CAAC;YACxG,CAAC;YACD,OAAO,EAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAC,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAC,CAAC;QACnI,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAC,MAAM,EAAE,OAAO,EAAC,CAAC;QAC3B,CAAC;IACH,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@mudraid/adapter-node` — framework-neutral TypeScript/Node server adapter for
|
|
3
|
+
* MudraID enforcement (EP-120-US-05, first slice).
|
|
4
|
+
*
|
|
5
|
+
* Public surface: the typed decision vocabulary, the V2 control loop, and the
|
|
6
|
+
* injectable `/decide` seam. The framework hooks (Express/Fastify/MCP), the real
|
|
7
|
+
* HTTP `/decide` client, and live fact extraction are DEFERRED remainders.
|
|
8
|
+
*/
|
|
9
|
+
export { ADAPTER_DECISION_CONTRACT_VERSION, MAX_TOOL_NAME_LEN, RESERVED_HEADER_PREFIX, type AdapterCode, type Decision, type DecideClient, type DecideResult, type DecideStatus, type JsonShape, type Outcome, type ReasonTier, type RequestFacts, type TrustedContextHeader, } from './types.js';
|
|
10
|
+
export { evaluateV2, isReservedHeader, newDecisionId, normalizeStrippedHeaders, shouldForward, validToolName, } from './controlLoop.js';
|
|
11
|
+
export { staticDecideClient, throwingDecideClient } from './decideClient.js';
|
|
12
|
+
export { verifyBundle, type BundleBinding, type VerifiedBundle } from './signedBundle.js';
|
|
13
|
+
export { HttpAuthority, type AuthorityOptions, type InvocationContext } from './httpAuthority.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@mudraid/adapter-node` — framework-neutral TypeScript/Node server adapter for
|
|
3
|
+
* MudraID enforcement (EP-120-US-05, first slice).
|
|
4
|
+
*
|
|
5
|
+
* Public surface: the typed decision vocabulary, the V2 control loop, and the
|
|
6
|
+
* injectable `/decide` seam. The framework hooks (Express/Fastify/MCP), the real
|
|
7
|
+
* HTTP `/decide` client, and live fact extraction are DEFERRED remainders.
|
|
8
|
+
*/
|
|
9
|
+
export { ADAPTER_DECISION_CONTRACT_VERSION, MAX_TOOL_NAME_LEN, RESERVED_HEADER_PREFIX, } from './types.js';
|
|
10
|
+
export { evaluateV2, isReservedHeader, newDecisionId, normalizeStrippedHeaders, shouldForward, validToolName, } from './controlLoop.js';
|
|
11
|
+
export { staticDecideClient, throwingDecideClient } from './decideClient.js';
|
|
12
|
+
export { verifyBundle } from './signedBundle.js';
|
|
13
|
+
export { HttpAuthority } from './httpAuthority.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,iCAAiC,EACjC,iBAAiB,EACjB,sBAAsB,GAWvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,wBAAwB,EACxB,aAAa,EACb,aAAa,GACd,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE7E,OAAO,EAAE,YAAY,EAA2C,MAAM,mBAAmB,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAiD,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type JsonObject = Record<string, unknown>;
|
|
2
|
+
export interface BundleBinding {
|
|
3
|
+
readonly platformId: string;
|
|
4
|
+
readonly environment: string;
|
|
5
|
+
readonly resource: string;
|
|
6
|
+
}
|
|
7
|
+
export interface VerifiedBundle {
|
|
8
|
+
readonly version: number;
|
|
9
|
+
readonly digest: string;
|
|
10
|
+
readonly expiresAt: number;
|
|
11
|
+
readonly surface: Readonly<JsonObject>;
|
|
12
|
+
readonly actions: Readonly<Record<string, Readonly<JsonObject>>>;
|
|
13
|
+
}
|
|
14
|
+
export declare function object(value: unknown): JsonObject;
|
|
15
|
+
/** Python sort_keys/ensure_ascii canonicalization used by the authority. */
|
|
16
|
+
export declare function canonicalJson(value: unknown): string;
|
|
17
|
+
export declare function instant(value: unknown): number;
|
|
18
|
+
export declare function verifyClaims(claims: JsonObject, encoded: unknown, pem: unknown): void;
|
|
19
|
+
/** Verify signatures, content, exact tenant binding, time and rollback fencing. */
|
|
20
|
+
export declare function verifyBundle(input: unknown, keys: Readonly<Record<string, string>>, binding: BundleBinding, active?: VerifiedBundle, now?: number): VerifiedBundle;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/** Public-key-only verification of the control plane's signed bundle contract. */
|
|
2
|
+
import { createHash, createPublicKey, verify } from 'node:crypto';
|
|
3
|
+
export function object(value) {
|
|
4
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
5
|
+
throw new Error('Invalid object');
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
/** Python sort_keys/ensure_ascii canonicalization used by the authority. */
|
|
9
|
+
export function canonicalJson(value) {
|
|
10
|
+
if (value === null || typeof value === 'boolean')
|
|
11
|
+
return JSON.stringify(value);
|
|
12
|
+
if (typeof value === 'number') {
|
|
13
|
+
if (!Number.isSafeInteger(value))
|
|
14
|
+
throw new Error('Unsupported canonical number');
|
|
15
|
+
return JSON.stringify(value);
|
|
16
|
+
}
|
|
17
|
+
if (typeof value === 'string') {
|
|
18
|
+
return JSON.stringify(value).replace(/[\u007f-\uffff]/g, c => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);
|
|
19
|
+
}
|
|
20
|
+
if (Array.isArray(value))
|
|
21
|
+
return `[${value.map(canonicalJson).join(',')}]`;
|
|
22
|
+
const record = object(value);
|
|
23
|
+
// Python compares Unicode code points; JavaScript's default compares UTF-16.
|
|
24
|
+
const compare = (a, b) => {
|
|
25
|
+
const aa = Array.from(a, c => c.codePointAt(0));
|
|
26
|
+
const bb = Array.from(b, c => c.codePointAt(0));
|
|
27
|
+
for (let i = 0; i < Math.min(aa.length, bb.length); i++) {
|
|
28
|
+
if (aa[i] !== bb[i])
|
|
29
|
+
return aa[i] - bb[i];
|
|
30
|
+
}
|
|
31
|
+
return aa.length - bb.length;
|
|
32
|
+
};
|
|
33
|
+
return `{${Object.keys(record).sort(compare).map(k => `${canonicalJson(k)}:${canonicalJson(record[k])}`).join(',')}}`;
|
|
34
|
+
}
|
|
35
|
+
export function instant(value) {
|
|
36
|
+
if (typeof value !== 'string' || !/(Z|[+-]\d{2}:\d{2})$/.test(value))
|
|
37
|
+
throw new Error('Invalid timestamp');
|
|
38
|
+
const time = Date.parse(value);
|
|
39
|
+
if (!Number.isFinite(time))
|
|
40
|
+
throw new Error('Invalid timestamp');
|
|
41
|
+
return time;
|
|
42
|
+
}
|
|
43
|
+
export function verifyClaims(claims, encoded, pem) {
|
|
44
|
+
if (typeof encoded !== 'string' || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(encoded) || !encoded)
|
|
45
|
+
throw new Error('Invalid signature');
|
|
46
|
+
if (typeof pem !== 'string')
|
|
47
|
+
throw new Error('Unknown signing key');
|
|
48
|
+
const key = createPublicKey(pem);
|
|
49
|
+
if (key.asymmetricKeyType !== 'rsa' || (key.asymmetricKeyDetails?.modulusLength ?? 0) < 2048)
|
|
50
|
+
throw new Error('Invalid signing key');
|
|
51
|
+
if (!verify('RSA-SHA256', Buffer.from(canonicalJson(claims)), key, Buffer.from(encoded, 'base64')))
|
|
52
|
+
throw new Error('Invalid signature');
|
|
53
|
+
}
|
|
54
|
+
function equal(actual, expected) {
|
|
55
|
+
if (actual !== expected)
|
|
56
|
+
throw new Error('Bundle binding mismatch');
|
|
57
|
+
}
|
|
58
|
+
function freeze(value) {
|
|
59
|
+
if (value !== null && typeof value === 'object') {
|
|
60
|
+
for (const child of Object.values(value))
|
|
61
|
+
freeze(child);
|
|
62
|
+
Object.freeze(value);
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
/** Verify signatures, content, exact tenant binding, time and rollback fencing. */
|
|
67
|
+
export function verifyBundle(input, keys, binding, active, now = Date.now()) {
|
|
68
|
+
const envelope = object(input);
|
|
69
|
+
const payload = object(envelope['payload']);
|
|
70
|
+
const claims = object(envelope['signature_claims']);
|
|
71
|
+
const version = envelope['bundle_version'];
|
|
72
|
+
if (typeof version !== 'number' || !Number.isSafeInteger(version) || version < 1)
|
|
73
|
+
throw new Error('Invalid bundle version');
|
|
74
|
+
equal(envelope['schema_version'], '1.0');
|
|
75
|
+
equal(payload['schema_version'], '1.0');
|
|
76
|
+
equal(payload['bundle_version'], version);
|
|
77
|
+
const digest = createHash('sha256').update(canonicalJson(payload)).digest('hex');
|
|
78
|
+
equal(envelope['payload_digest'], digest);
|
|
79
|
+
const keyId = envelope['signature_key_id'];
|
|
80
|
+
if (typeof keyId !== 'string' || !Object.hasOwn(keys, keyId))
|
|
81
|
+
throw new Error('Unknown signing key');
|
|
82
|
+
const profile = 'mudraid.bundle.signature/1';
|
|
83
|
+
equal(envelope['signature_profile'], profile);
|
|
84
|
+
equal(envelope['signature_algorithm'], 'RS256');
|
|
85
|
+
equal(claims['profile'], profile);
|
|
86
|
+
equal(claims['algorithm'], 'RS256');
|
|
87
|
+
equal(claims['key_id'], keyId);
|
|
88
|
+
verifyClaims(claims, envelope['signature_value'], keys[keyId]);
|
|
89
|
+
equal(claims['payload_digest'], digest);
|
|
90
|
+
equal(claims['bundle_version'], version);
|
|
91
|
+
const content = object(payload['content']);
|
|
92
|
+
const surface = object(content['surface']);
|
|
93
|
+
for (const [field, expected] of [
|
|
94
|
+
['platform_id', binding.platformId], ['environment', binding.environment],
|
|
95
|
+
['canonical_resource_uri', binding.resource],
|
|
96
|
+
]) {
|
|
97
|
+
if (!expected.trim())
|
|
98
|
+
throw new Error('Unbound surface');
|
|
99
|
+
equal(surface[field], expected);
|
|
100
|
+
equal(claims[field], expected);
|
|
101
|
+
}
|
|
102
|
+
const starts = instant(claims['not_before']);
|
|
103
|
+
const expires = instant(claims['expires_at']);
|
|
104
|
+
if (starts > now || expires <= now || expires <= starts)
|
|
105
|
+
throw new Error('Bundle outside validity window');
|
|
106
|
+
const evaluation = object(content['evaluation']);
|
|
107
|
+
for (const [field, expected] of Object.entries({ mode: 'live', on_timeout: 'deny', on_error: 'deny', on_unmapped_action: 'deny', on_stale_bundle: 'deny', forward: 'once', decide_required: true, retry_forwarded_request: false }))
|
|
108
|
+
equal(evaluation[field], expected);
|
|
109
|
+
const matcher = object(content['matcher']);
|
|
110
|
+
equal(matcher['kind'], 'mcp_tool_exact');
|
|
111
|
+
const entries = matcher['actions'];
|
|
112
|
+
if (!Array.isArray(entries) || entries.length === 0 || entries.length > 10000)
|
|
113
|
+
throw new Error('Invalid action map');
|
|
114
|
+
const actions = Object.create(null);
|
|
115
|
+
for (const entry of entries) {
|
|
116
|
+
const action = object(entry);
|
|
117
|
+
const tool = action['tool_name'];
|
|
118
|
+
const actionKey = action['action_key'];
|
|
119
|
+
if (typeof tool !== 'string' || !tool || Buffer.byteLength(tool) > 512 || Object.hasOwn(actions, tool))
|
|
120
|
+
throw new Error('Invalid or ambiguous tool');
|
|
121
|
+
if (typeof actionKey !== 'string' || !actionKey || Buffer.byteLength(actionKey) > 512)
|
|
122
|
+
throw new Error('Invalid action');
|
|
123
|
+
actions[tool] = JSON.parse(JSON.stringify(action));
|
|
124
|
+
}
|
|
125
|
+
if (active && (version < active.version || (version === active.version && digest !== active.digest)))
|
|
126
|
+
throw new Error('Bundle rollback or conflict');
|
|
127
|
+
return freeze({ version, digest, expiresAt: expires, surface: JSON.parse(JSON.stringify(surface)), actions });
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=signedBundle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signedBundle.js","sourceRoot":"","sources":["../src/signedBundle.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed decision vocabulary for the MudraID Node server adapter.
|
|
3
|
+
*
|
|
4
|
+
* These types are the TypeScript mirror of the portable adapter-decision
|
|
5
|
+
* contract `mudraid.adapter.decision/1`
|
|
6
|
+
* (`shared/mudraid_contracts/mudraid_contracts/adapters/decision.py`) and of the
|
|
7
|
+
* Python middleware V2 control loop
|
|
8
|
+
* (`sdks/mudraid-middleware-python/src/mudraid_middleware/_v2_control_loop.py`).
|
|
9
|
+
*
|
|
10
|
+
* The whole point of this adapter is EXACT parity: the same facts must produce
|
|
11
|
+
* the same outcome + adapter code as the Kong Lua handler and the Python
|
|
12
|
+
* middleware. So every value here is a closed union — never a bare `string` —
|
|
13
|
+
* so a divergent code is a compile error, not a silent runtime drift.
|
|
14
|
+
*/
|
|
15
|
+
/** Versioned identifier of the portable contract this adapter reproduces. */
|
|
16
|
+
export declare const ADAPTER_DECISION_CONTRACT_VERSION: "mudraid.adapter.decision/1";
|
|
17
|
+
/** Reserved request-header prefix, stripped before evaluation (contract A03-04). */
|
|
18
|
+
export declare const RESERVED_HEADER_PREFIX: "x-mudraid-";
|
|
19
|
+
/** Bounded canonical action-name length in bytes (matches `MAX_TOOL_NAME_LEN`). */
|
|
20
|
+
export declare const MAX_TOOL_NAME_LEN: 512;
|
|
21
|
+
/** Closed outcome vocabulary — byte-identical to the contract's `OUTCOMES`. */
|
|
22
|
+
export type Outcome = 'allow' | 'deny' | 'not_safely_decided';
|
|
23
|
+
/** Which tier produced the reason: pre-decision framing vs. authorization. */
|
|
24
|
+
export type ReasonTier = 'transport' | 'authorization';
|
|
25
|
+
/** Shape of the parsed request body, as the fact extractor classifies it. */
|
|
26
|
+
export type JsonShape = 'object' | 'array' | 'scalar' | 'not_json';
|
|
27
|
+
/**
|
|
28
|
+
* Stable, agent-facing adapter error codes. Identical to the codes the Kong Lua
|
|
29
|
+
* handler and the Python middleware surface, so a Node caller sees the SAME code
|
|
30
|
+
* for the SAME failure. `null` on an allow / pass-through (nothing to surface).
|
|
31
|
+
*/
|
|
32
|
+
export type AdapterCode = 'ENFORCE_METHOD_NOT_ALLOWED' | 'ENFORCE_BODY_TOO_LARGE' | 'ENFORCE_BODY_UNREADABLE' | 'ENFORCE_MALFORMED_REQUEST' | 'ENFORCE_BATCH_UNSUPPORTED' | 'ENFORCE_MESSAGE_NOT_ALLOWED' | 'ENFORCE_ACTION_UNMAPPED' | 'ENFORCE_NO_VALID_BUNDLE' | 'ENFORCE_DECISION_DENY' | 'ENFORCE_DECIDE_UNAVAILABLE';
|
|
33
|
+
/**
|
|
34
|
+
* The failure-aware outcome vocabulary a live `/decide` call reports back to the
|
|
35
|
+
* control loop.
|
|
36
|
+
*
|
|
37
|
+
* - `allow` / `deny` — an authority decision was reached;
|
|
38
|
+
* - `timeout` / `error` / `unreachable` / `unconfigured` /
|
|
39
|
+
* `credential_unconfigured` — the call could not be completed; every one is
|
|
40
|
+
* deny-closed (`not_safely_decided`), never optimistically allowed.
|
|
41
|
+
*/
|
|
42
|
+
export type DecideStatus = 'allow' | 'deny' | 'timeout' | 'error' | 'unreachable' | 'unconfigured' | 'credential_unconfigured';
|
|
43
|
+
/** The outcome of one live `/decide` call, as the adapter sees it. */
|
|
44
|
+
export interface DecideResult {
|
|
45
|
+
readonly status: DecideStatus;
|
|
46
|
+
/** A `/decide`-supplied deny reason code (only meaningful on `status: 'deny'`). */
|
|
47
|
+
readonly reason?: string;
|
|
48
|
+
/** Correlation id forwarded as trusted context on an allow. */
|
|
49
|
+
readonly decisionId?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The injectable `/decide` seam. The control loop invokes it EXACTLY at the
|
|
53
|
+
* `/decide` branch (a mapped `tools/call` on an active bundle) and nowhere else.
|
|
54
|
+
* Tests inject a fake so no network is required; the real HTTP client is a
|
|
55
|
+
* deferred remainder of this story.
|
|
56
|
+
*/
|
|
57
|
+
export type DecideClient = (action: string) => Promise<DecideResult>;
|
|
58
|
+
/**
|
|
59
|
+
* The normalized facts a single evaluation consumes. Mirrors the portable
|
|
60
|
+
* contract's decision facts. Fact *extraction* from a live framework request is
|
|
61
|
+
* a deferred remainder; the decision *tree* lives only in `evaluateV2`, so
|
|
62
|
+
* extraction and decision never drift into two competing policy copies.
|
|
63
|
+
*/
|
|
64
|
+
export interface RequestFacts {
|
|
65
|
+
/** Whether this surface is a bundled (protected) MudraID surface. */
|
|
66
|
+
readonly protected: boolean;
|
|
67
|
+
/** Reserved `x-mudraid-*` headers the client presented (original case kept). */
|
|
68
|
+
readonly reservedHeadersPresented?: readonly string[];
|
|
69
|
+
/** Whether a verified signed bundle is currently active. */
|
|
70
|
+
readonly bundleActive: boolean;
|
|
71
|
+
readonly method: string;
|
|
72
|
+
readonly bodyReadable?: boolean;
|
|
73
|
+
readonly bodyTooLarge?: boolean;
|
|
74
|
+
readonly jsonShape?: JsonShape;
|
|
75
|
+
readonly jsonrpc?: string | null;
|
|
76
|
+
readonly rpcMethod?: string | null;
|
|
77
|
+
readonly toolName?: string | null;
|
|
78
|
+
/** Whether an exact canonical action is mapped for this tool. */
|
|
79
|
+
readonly actionMapped?: boolean;
|
|
80
|
+
/** Resolved canonical action name, if distinct from `toolName`. */
|
|
81
|
+
readonly action?: string | null;
|
|
82
|
+
}
|
|
83
|
+
/** A trusted-context header injected downstream, only on a bound allow. */
|
|
84
|
+
export type TrustedContextHeader = readonly [name: string, value: string];
|
|
85
|
+
/** The normalized outcome of one V2 evaluation. */
|
|
86
|
+
export interface Decision {
|
|
87
|
+
readonly outcome: Outcome;
|
|
88
|
+
readonly reasonCode: string;
|
|
89
|
+
readonly reasonTier: ReasonTier;
|
|
90
|
+
readonly httpStatus: number;
|
|
91
|
+
readonly adapterCode: AdapterCode | null;
|
|
92
|
+
/** Stable agent-facing message; never contains secrets or token material. */
|
|
93
|
+
readonly message: string;
|
|
94
|
+
/** Reserved headers stripped from the request before evaluation. */
|
|
95
|
+
readonly strippedReservedHeaders: readonly string[];
|
|
96
|
+
/** Injected only on an authorized allow; empty on every pass-through/deny. */
|
|
97
|
+
readonly trustedContext: readonly TrustedContextHeader[];
|
|
98
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed decision vocabulary for the MudraID Node server adapter.
|
|
3
|
+
*
|
|
4
|
+
* These types are the TypeScript mirror of the portable adapter-decision
|
|
5
|
+
* contract `mudraid.adapter.decision/1`
|
|
6
|
+
* (`shared/mudraid_contracts/mudraid_contracts/adapters/decision.py`) and of the
|
|
7
|
+
* Python middleware V2 control loop
|
|
8
|
+
* (`sdks/mudraid-middleware-python/src/mudraid_middleware/_v2_control_loop.py`).
|
|
9
|
+
*
|
|
10
|
+
* The whole point of this adapter is EXACT parity: the same facts must produce
|
|
11
|
+
* the same outcome + adapter code as the Kong Lua handler and the Python
|
|
12
|
+
* middleware. So every value here is a closed union — never a bare `string` —
|
|
13
|
+
* so a divergent code is a compile error, not a silent runtime drift.
|
|
14
|
+
*/
|
|
15
|
+
/** Versioned identifier of the portable contract this adapter reproduces. */
|
|
16
|
+
export const ADAPTER_DECISION_CONTRACT_VERSION = 'mudraid.adapter.decision/1';
|
|
17
|
+
/** Reserved request-header prefix, stripped before evaluation (contract A03-04). */
|
|
18
|
+
export const RESERVED_HEADER_PREFIX = 'x-mudraid-';
|
|
19
|
+
/** Bounded canonical action-name length in bytes (matches `MAX_TOOL_NAME_LEN`). */
|
|
20
|
+
export const MAX_TOOL_NAME_LEN = 512;
|
|
21
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,6EAA6E;AAC7E,MAAM,CAAC,MAAM,iCAAiC,GAAG,4BAAqC,CAAC;AAEvF,oFAAoF;AACpF,MAAM,CAAC,MAAM,sBAAsB,GAAG,YAAqB,CAAC;AAE5D,mFAAmF;AACnF,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAY,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mudraid/adapter-node",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Framework-neutral TypeScript/Node server adapter implementing the portable MudraID adapter-decision V2 control loop (EP-120-US-05, first slice).",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"src"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -p tsconfig.build.json",
|
|
22
|
+
"typecheck": "tsc --noEmit",
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"test:watch": "vitest"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "20.17.9",
|
|
31
|
+
"typescript": "5.6.3",
|
|
32
|
+
"vitest": "2.1.8"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/MudraID/mudraid-adapter-node.git"
|
|
37
|
+
}
|
|
38
|
+
}
|