knosky 0.6.3 → 0.7.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/CHANGELOG.md +149 -93
- package/CREDITS.md +14 -14
- package/LICENSE.md +76 -76
- package/LIMITATIONS.md +33 -23
- package/PRIVACY.md +30 -30
- package/README.md +170 -117
- package/SECURITY.md +78 -46
- package/action/post-comment.mjs +94 -89
- package/action.yml +62 -62
- package/bin/knosky.mjs +279 -105
- package/core/CONTRACT.md +70 -70
- package/core/append-only-checkpoint.mjs +215 -0
- package/core/audit-writer.mjs +317 -0
- package/core/benchmark-results.mjs +225 -225
- package/core/bundle.mjs +178 -178
- package/core/churn.mjs +23 -23
- package/core/ci.mjs +268 -268
- package/core/comparison.mjs +189 -189
- package/core/config.mjs +189 -189
- package/core/constants.mjs +13 -13
- package/core/contract.mjs +123 -123
- package/core/cross-repo.mjs +111 -111
- package/core/decision-codes.mjs +92 -0
- package/core/destination.mjs +161 -161
- package/core/district-classification.mjs +111 -0
- package/core/doctor-scorecard.mjs +369 -0
- package/core/domain-store.mjs +347 -0
- package/core/edges.mjs +43 -43
- package/core/escalate.mjs +68 -68
- package/core/freshness.mjs +198 -194
- package/core/fs-indexer.mjs +218 -218
- package/core/key-store.mjs +348 -348
- package/core/layout.mjs +46 -46
- package/core/ledger.mjs +176 -141
- package/core/local-ipc-identity.mjs +500 -0
- package/core/lod.mjs +155 -155
- package/core/mode-b.mjs +410 -0
- package/core/multi-model-benchmark.mjs +405 -405
- package/core/net-lockdown.mjs +421 -0
- package/core/onboarding.mjs +223 -223
- package/core/operator-auth.mjs +317 -0
- package/core/overlays.mjs +45 -45
- package/core/policy-lattice.mjs +142 -0
- package/core/pr-comment.mjs +198 -198
- package/core/protocol-spec.mjs +460 -460
- package/core/provenance.mjs +320 -0
- package/core/retrieve.mjs +63 -63
- package/core/route.mjs +304 -304
- package/core/schema.mjs +275 -275
- package/core/signing-tiers.mjs +1265 -0
- package/core/swarm-bench.mjs +106 -0
- package/core/swarm-coordinator.mjs +867 -0
- package/core/trust-root-rekey.mjs +410 -0
- package/mcp/server.mjs +264 -108
- package/package.json +56 -46
- package/renderer/art/kenney/buildingTiles_sheet.xml +130 -130
- package/renderer/art/kenney/cityDetails_sheet.xml +12 -12
- package/renderer/art/kenney/landscapeTiles_sheet.xml +129 -129
- package/renderer/art/kenney/sheet_allCars.xml +545 -545
- package/renderer/build-rich.mjs +43 -43
- package/renderer/city.template.html +808 -808
- package/ssot/decision-codes.json +133 -0
- package/ssot/ladder-l0-l3.md +232 -0
- package/ssot/tool-menu.json +130 -0
package/core/mode-b.mjs
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
// KnoSky Mode B evaluator (DEC-106/108).
|
|
2
|
+
// identity → policy lattice → audit receipt → ALLOW subgraph | DENY_* / ADVISORY_UNAUTH
|
|
3
|
+
|
|
4
|
+
import { evaluate, DENY, ALLOW, NOT_APPLICABLE } from './policy-lattice.mjs';
|
|
5
|
+
import { resolveLeaseIdentity } from './local-ipc-identity.mjs';
|
|
6
|
+
import { writeDecisionReceipt } from './audit-writer.mjs';
|
|
7
|
+
import { CODES, envelope } from './decision-codes.mjs';
|
|
8
|
+
import {
|
|
9
|
+
loadDomain,
|
|
10
|
+
policyRulesFromDomain,
|
|
11
|
+
inferRouteClass,
|
|
12
|
+
resolveDomainRoot,
|
|
13
|
+
} from './domain-store.mjs';
|
|
14
|
+
import { kcRoute } from './route.mjs';
|
|
15
|
+
import { kcBundle } from './bundle.mjs';
|
|
16
|
+
import { loadClass, DEFAULT_CLASS } from './district-classification.mjs';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {object} ModeBRequest
|
|
20
|
+
* @property {'route'|'bundle'|'policy_check'} tool
|
|
21
|
+
* @property {string} [destination]
|
|
22
|
+
* @property {string[]} [nodeIds]
|
|
23
|
+
* @property {string} [leaseId]
|
|
24
|
+
* @property {string|null} [agentId] payload claim — never trusted alone
|
|
25
|
+
* @property {boolean} [advisory] force Mode A
|
|
26
|
+
* @property {number} [limit]
|
|
27
|
+
* @property {string} [root] repo root for bundle secret scan
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create a Mode B front-door bound to a city ctx + domain.
|
|
32
|
+
* @param {object} opts
|
|
33
|
+
* @param {object} opts.cityCtx from retrieve.load
|
|
34
|
+
* @param {string} opts.cityPath
|
|
35
|
+
* @param {string} [opts.domainRoot]
|
|
36
|
+
* @param {'coding'|'security'|'advisory'} [opts.profile]
|
|
37
|
+
*/
|
|
38
|
+
export function createModeBDoor(opts) {
|
|
39
|
+
const cityCtx = opts.cityCtx;
|
|
40
|
+
// Normalize byId so route/filter code never assumes Map vs plain object.
|
|
41
|
+
if (cityCtx && cityCtx.byId && typeof cityCtx.byId.get !== 'function') {
|
|
42
|
+
cityCtx.byId = new Map(Object.entries(cityCtx.byId));
|
|
43
|
+
}
|
|
44
|
+
const cityPath = opts.cityPath;
|
|
45
|
+
const domainRoot = resolveDomainRoot(cityPath, opts.domainRoot);
|
|
46
|
+
// Reload domain on each handle() so lease revoke / policy updates are visible
|
|
47
|
+
// without process restart (stale-state window closed at request boundary).
|
|
48
|
+
let domain = loadDomain(domainRoot);
|
|
49
|
+
const profile = opts.profile || process.env.KC_PROFILE || 'coding';
|
|
50
|
+
|
|
51
|
+
function refreshDomain() {
|
|
52
|
+
domain = loadDomain(domainRoot);
|
|
53
|
+
return domain;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function bindIdentity(req) {
|
|
57
|
+
// Always re-read domain so revocations / policy edits apply mid-process.
|
|
58
|
+
refreshDomain();
|
|
59
|
+
// Advisory path may skip identity
|
|
60
|
+
if (req.advisory || profile === 'advisory') {
|
|
61
|
+
return { ok: true, advisory: true, agentId: null, lease: null };
|
|
62
|
+
}
|
|
63
|
+
const requireId = domain.policy.require_identity !== false;
|
|
64
|
+
if (!requireId) {
|
|
65
|
+
return { ok: true, advisory: false, agentId: req.agentId || 'anonymous-local', lease: null };
|
|
66
|
+
}
|
|
67
|
+
if (!req.leaseId) {
|
|
68
|
+
return { ok: false, code: CODES.DENY_IDENTITY, hint: 'Provide leaseId from knosky agent register (or set KC_MODE=advisory).' };
|
|
69
|
+
}
|
|
70
|
+
const resolved = resolveLeaseIdentity(domain.leaseStore, req.leaseId, req.agentId ?? null);
|
|
71
|
+
if (!resolved.ok) {
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
code: CODES.DENY_IDENTITY,
|
|
75
|
+
hint: `Identity bind failed (${resolved.reason}). Register agent + lease in domain.`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return { ok: true, advisory: false, agentId: resolved.agentId, lease: resolved.lease };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function decidePolicy(agentId, className, tool) {
|
|
82
|
+
const rules = policyRulesFromDomain(domain.policy);
|
|
83
|
+
const subject = { agentId, class: className, tool };
|
|
84
|
+
const { decision, reasons } = evaluate(rules, subject);
|
|
85
|
+
if (decision === DENY) {
|
|
86
|
+
return { ok: false, code: CODES.DENY_POLICY, reasons };
|
|
87
|
+
}
|
|
88
|
+
if (decision === NOT_APPLICABLE && domain.policy.require_identity !== false) {
|
|
89
|
+
// No allow found — fail closed when identity required
|
|
90
|
+
return { ok: false, code: CODES.DENY_POLICY, reasons: reasons.concat(['no_allow_rule']) };
|
|
91
|
+
}
|
|
92
|
+
return { ok: true, decision, reasons };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function receipt(decision_code, agentId, tool, destination, meta) {
|
|
96
|
+
const wr = writeDecisionReceipt({
|
|
97
|
+
domainRoot,
|
|
98
|
+
decision_code,
|
|
99
|
+
agent_id: agentId,
|
|
100
|
+
tool,
|
|
101
|
+
destination,
|
|
102
|
+
mode: decision_code === CODES.ADVISORY_UNAUTH ? 'A' : 'B',
|
|
103
|
+
meta,
|
|
104
|
+
});
|
|
105
|
+
if (!wr.ok) {
|
|
106
|
+
return { ok: false, code: CODES.DENY_AUDIT, hint: wr.reason };
|
|
107
|
+
}
|
|
108
|
+
return { ok: true, receipt_id: wr.receipt_id, ledger_seq: wr.ledger_seq };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function filterRouteByPolicy(routeDoc, agentId) {
|
|
112
|
+
// Drop waypoints whose node class is denied for agent
|
|
113
|
+
const rules = policyRulesFromDomain(domain.policy);
|
|
114
|
+
const getNodeById = (id) => {
|
|
115
|
+
if (!id || !cityCtx?.byId) return null;
|
|
116
|
+
if (typeof cityCtx.byId.get === 'function') return cityCtx.byId.get(id) || null;
|
|
117
|
+
return cityCtx.byId[id] || null;
|
|
118
|
+
};
|
|
119
|
+
const filterList = (arr) => {
|
|
120
|
+
if (!Array.isArray(arr)) return [];
|
|
121
|
+
return arr.filter((e) => {
|
|
122
|
+
const id = e.id || (e.path ? `fs:${e.path}` : null);
|
|
123
|
+
let cls = DEFAULT_CLASS;
|
|
124
|
+
const node = getNodeById(id);
|
|
125
|
+
if (node) cls = loadClass(node);
|
|
126
|
+
const { decision } = evaluate(rules, { agentId, class: cls, tool: 'route' });
|
|
127
|
+
return decision !== DENY;
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
return {
|
|
131
|
+
...routeDoc,
|
|
132
|
+
route: filterList(routeDoc.route),
|
|
133
|
+
alternates: filterList(routeDoc.alternates),
|
|
134
|
+
tests: filterList(routeDoc.tests),
|
|
135
|
+
docs: filterList(routeDoc.docs),
|
|
136
|
+
authorized: true,
|
|
137
|
+
mode: 'B',
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @param {ModeBRequest} req
|
|
143
|
+
*/
|
|
144
|
+
function handle(req) {
|
|
145
|
+
const request_id = `req_${Date.now().toString(36)}`;
|
|
146
|
+
try {
|
|
147
|
+
if (!cityCtx) {
|
|
148
|
+
return envelope({
|
|
149
|
+
code: CODES.ERROR_INDEX,
|
|
150
|
+
mode: 'B',
|
|
151
|
+
request_id,
|
|
152
|
+
next_action: 'Set KC_CITY to a valid city-data.json',
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const tool = req.tool || 'route';
|
|
157
|
+
const identity = bindIdentity(req);
|
|
158
|
+
|
|
159
|
+
// Mode A advisory
|
|
160
|
+
if (identity.advisory || req.advisory === true || profile === 'advisory') {
|
|
161
|
+
if (tool === 'policy_check') {
|
|
162
|
+
return envelope({
|
|
163
|
+
code: CODES.ADVISORY_UNAUTH,
|
|
164
|
+
mode: 'A',
|
|
165
|
+
request_id,
|
|
166
|
+
payload: { note: 'policy_check in advisory mode does not authorize', decision: 'ADVISORY' },
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (tool === 'route') {
|
|
170
|
+
if (!req.destination || typeof req.destination !== 'string') {
|
|
171
|
+
return envelope({ code: CODES.ERROR_INVALID_INPUT, mode: 'A', request_id });
|
|
172
|
+
}
|
|
173
|
+
const doc = kcRoute(cityCtx, String(req.destination).slice(0, 400), {
|
|
174
|
+
limit: req.limit ? Math.min(Math.max(1, req.limit), 20) : 8,
|
|
175
|
+
});
|
|
176
|
+
const labeled = {
|
|
177
|
+
...doc,
|
|
178
|
+
authorized: false,
|
|
179
|
+
mode: 'A',
|
|
180
|
+
advisory: true,
|
|
181
|
+
caveats: [...(doc.caveats || []), 'ADVISORY_UNAUTH: not policy-certified; verify before acting'],
|
|
182
|
+
};
|
|
183
|
+
// optional receipt for advisory trail
|
|
184
|
+
const rc = receipt(CODES.ADVISORY_UNAUTH, null, tool, req.destination, { advisory: true });
|
|
185
|
+
return envelope({
|
|
186
|
+
code: CODES.ADVISORY_UNAUTH,
|
|
187
|
+
mode: 'A',
|
|
188
|
+
request_id,
|
|
189
|
+
receipt_id: rc.ok ? rc.receipt_id : undefined,
|
|
190
|
+
payload: labeled,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
if (tool === 'bundle') {
|
|
194
|
+
return envelope({
|
|
195
|
+
code: CODES.ADVISORY_UNAUTH,
|
|
196
|
+
mode: 'A',
|
|
197
|
+
request_id,
|
|
198
|
+
next_action: 'Bundle under Mode B requires identity; switch profile=coding with leaseId',
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (!identity.ok) {
|
|
204
|
+
return envelope({
|
|
205
|
+
code: identity.code || CODES.DENY_IDENTITY,
|
|
206
|
+
mode: 'B',
|
|
207
|
+
request_id,
|
|
208
|
+
next_action: identity.hint,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const agentId = identity.agentId;
|
|
213
|
+
|
|
214
|
+
// Build structural artifact first (metadata), then authorize
|
|
215
|
+
let raw = null;
|
|
216
|
+
let destination = req.destination || null;
|
|
217
|
+
let className = DEFAULT_CLASS;
|
|
218
|
+
/** @type {string[]} */
|
|
219
|
+
let bundleNodeIds = Array.isArray(req.nodeIds) ? req.nodeIds.map(String) : [];
|
|
220
|
+
|
|
221
|
+
if (tool === 'route' || tool === 'policy_check') {
|
|
222
|
+
if (!req.destination || typeof req.destination !== 'string') {
|
|
223
|
+
return envelope({ code: CODES.ERROR_INVALID_INPUT, mode: 'B', request_id });
|
|
224
|
+
}
|
|
225
|
+
raw = kcRoute(cityCtx, String(req.destination).slice(0, 400), {
|
|
226
|
+
limit: req.limit ? Math.min(Math.max(1, req.limit), 20) : 8,
|
|
227
|
+
});
|
|
228
|
+
className = inferRouteClass(cityCtx, raw);
|
|
229
|
+
} else if (tool === 'bundle') {
|
|
230
|
+
if (!bundleNodeIds.length && req.destination) {
|
|
231
|
+
raw = kcRoute(cityCtx, String(req.destination).slice(0, 400), { limit: 8 });
|
|
232
|
+
// Policy-filter route waypoints BEFORE bundling (don't leak restricted ids)
|
|
233
|
+
const filtered = filterRouteByPolicy(raw, agentId);
|
|
234
|
+
bundleNodeIds = (filtered.route || []).map((e) => e.id).filter(Boolean);
|
|
235
|
+
className = inferRouteClass(cityCtx, filtered);
|
|
236
|
+
} else if (bundleNodeIds.length) {
|
|
237
|
+
// Explicit nodeIds: derive class from allowed nodes; drop denied nodes
|
|
238
|
+
const kept = [];
|
|
239
|
+
let best = null;
|
|
240
|
+
const order = ['public', 'internal', 'restricted', 'confidential', 'blocked'];
|
|
241
|
+
for (const id of bundleNodeIds) {
|
|
242
|
+
const node =
|
|
243
|
+
typeof cityCtx.byId?.get === 'function' ? cityCtx.byId.get(id) : cityCtx.byId?.[id];
|
|
244
|
+
const cls = node ? loadClass(node) : DEFAULT_CLASS;
|
|
245
|
+
const polN = decidePolicy(agentId, cls, 'bundle');
|
|
246
|
+
if (polN.ok) {
|
|
247
|
+
kept.push(id);
|
|
248
|
+
if (best == null || order.indexOf(cls) < order.indexOf(best)) best = cls;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
bundleNodeIds = kept;
|
|
252
|
+
className = best || DEFAULT_CLASS;
|
|
253
|
+
} else {
|
|
254
|
+
return envelope({ code: CODES.ERROR_INVALID_INPUT, mode: 'B', request_id });
|
|
255
|
+
}
|
|
256
|
+
if (!bundleNodeIds.length) {
|
|
257
|
+
return envelope({
|
|
258
|
+
code: CODES.DENY_POLICY,
|
|
259
|
+
mode: 'B',
|
|
260
|
+
request_id,
|
|
261
|
+
next_action: 'No authorized nodes remain for bundle under current agent classes',
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
return envelope({ code: CODES.ERROR_INVALID_INPUT, mode: 'B', request_id });
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const pol = decidePolicy(agentId, className, tool);
|
|
269
|
+
if (!pol.ok) {
|
|
270
|
+
const rc = receipt(pol.code, agentId, tool, destination, { reasons: pol.reasons, class: className });
|
|
271
|
+
return envelope({
|
|
272
|
+
code: pol.code,
|
|
273
|
+
mode: 'B',
|
|
274
|
+
request_id,
|
|
275
|
+
receipt_id: rc.ok ? rc.receipt_id : undefined,
|
|
276
|
+
next_action: rc.ok
|
|
277
|
+
? 'Adjust agent class allowlist in .knosky/policy.json or pick another destination'
|
|
278
|
+
: `Audit write failed: ${rc.hint}`,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (tool === 'policy_check') {
|
|
283
|
+
const rc = receipt(CODES.ALLOW, agentId, tool, destination, { class: className });
|
|
284
|
+
if (!rc.ok) {
|
|
285
|
+
return envelope({
|
|
286
|
+
code: CODES.DENY_AUDIT,
|
|
287
|
+
mode: 'B',
|
|
288
|
+
request_id,
|
|
289
|
+
next_action: rc.hint,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
return envelope({
|
|
293
|
+
code: CODES.ALLOW,
|
|
294
|
+
mode: 'B',
|
|
295
|
+
request_id,
|
|
296
|
+
receipt_id: rc.receipt_id,
|
|
297
|
+
payload: {
|
|
298
|
+
would_allow: true,
|
|
299
|
+
class: className,
|
|
300
|
+
agent_id: agentId,
|
|
301
|
+
reasons: pol.reasons,
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (tool === 'route') {
|
|
307
|
+
// Audit BEFORE allow payload (DEC-106)
|
|
308
|
+
const rc = receipt(CODES.ALLOW, agentId, tool, destination, { class: className });
|
|
309
|
+
if (!rc.ok) {
|
|
310
|
+
return envelope({
|
|
311
|
+
code: CODES.DENY_AUDIT,
|
|
312
|
+
mode: 'B',
|
|
313
|
+
request_id,
|
|
314
|
+
next_action: rc.hint,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
const authorized = filterRouteByPolicy(raw, agentId);
|
|
318
|
+
authorized.receipt_id = rc.receipt_id;
|
|
319
|
+
authorized.agent_id = agentId;
|
|
320
|
+
return envelope({
|
|
321
|
+
code: CODES.ALLOW,
|
|
322
|
+
mode: 'B',
|
|
323
|
+
request_id,
|
|
324
|
+
receipt_id: rc.receipt_id,
|
|
325
|
+
payload: authorized,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (tool === 'bundle') {
|
|
330
|
+
const ids = bundleNodeIds;
|
|
331
|
+
if (!ids.length) {
|
|
332
|
+
return envelope({ code: CODES.ERROR_INVALID_INPUT, mode: 'B', request_id });
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
const manifest = kcBundle(cityCtx, ids, {
|
|
336
|
+
root: req.root || null,
|
|
337
|
+
expiry: req.expiry ?? null,
|
|
338
|
+
});
|
|
339
|
+
if (manifest.secret_scan && manifest.secret_scan.status === 'blocked') {
|
|
340
|
+
// Final decision is DENY — audit the denial, never an ALLOW receipt
|
|
341
|
+
const rcDeny = receipt(CODES.DENY_EVIDENCE, agentId, tool, destination, {
|
|
342
|
+
class: className,
|
|
343
|
+
reason: 'secret_scan_blocked',
|
|
344
|
+
});
|
|
345
|
+
return envelope({
|
|
346
|
+
code: CODES.DENY_EVIDENCE,
|
|
347
|
+
mode: 'B',
|
|
348
|
+
request_id,
|
|
349
|
+
receipt_id: rcDeny.ok ? rcDeny.receipt_id : undefined,
|
|
350
|
+
next_action: 'Bundle blocked by fail-closed secret scan',
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
// Audit ALLOW only after evidence checks pass
|
|
354
|
+
const rc = receipt(CODES.ALLOW, agentId, tool, destination, { class: className });
|
|
355
|
+
if (!rc.ok) {
|
|
356
|
+
return envelope({
|
|
357
|
+
code: CODES.DENY_AUDIT,
|
|
358
|
+
mode: 'B',
|
|
359
|
+
request_id,
|
|
360
|
+
next_action: rc.hint,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
return envelope({
|
|
364
|
+
code: CODES.ALLOW,
|
|
365
|
+
mode: 'B',
|
|
366
|
+
request_id,
|
|
367
|
+
receipt_id: rc.receipt_id,
|
|
368
|
+
payload: { ...manifest, authorized: true, agent_id: agentId },
|
|
369
|
+
});
|
|
370
|
+
} catch (err) {
|
|
371
|
+
return envelope({
|
|
372
|
+
code: CODES.ERROR_INVALID_INPUT,
|
|
373
|
+
mode: 'B',
|
|
374
|
+
request_id,
|
|
375
|
+
next_action: err && err.message ? err.message : String(err),
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return envelope({ code: CODES.ERROR_INVALID_INPUT, mode: 'B', request_id });
|
|
381
|
+
} catch (err) {
|
|
382
|
+
const msg = err && err.message ? err.message : String(err);
|
|
383
|
+
// Do not mask security-path failures as "bad input"
|
|
384
|
+
const securityish =
|
|
385
|
+
/audit|ledger|hwm|policy|identity|lease|operator|deny|fsync|permission|EPERM|ENOSPC/i.test(
|
|
386
|
+
msg,
|
|
387
|
+
);
|
|
388
|
+
return envelope({
|
|
389
|
+
code: securityish ? CODES.DENY_AUDIT : CODES.ERROR_INVALID_INPUT,
|
|
390
|
+
mode: 'B',
|
|
391
|
+
request_id,
|
|
392
|
+
next_action: securityish
|
|
393
|
+
? `Security path error (fail-closed): ${msg.slice(0, 200)}`
|
|
394
|
+
: msg.slice(0, 200),
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
get domainRoot() {
|
|
401
|
+
return domainRoot;
|
|
402
|
+
},
|
|
403
|
+
get domain() {
|
|
404
|
+
return domain;
|
|
405
|
+
},
|
|
406
|
+
refreshDomain,
|
|
407
|
+
profile,
|
|
408
|
+
handle,
|
|
409
|
+
};
|
|
410
|
+
}
|