enigma-memory 0.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 +55 -0
- package/README.md +379 -0
- package/apps/cli/bin/enigma.mjs +1117 -0
- package/apps/desktop/src/app.js +1327 -0
- package/apps/desktop/src/index.html +1373 -0
- package/apps/desktop/src/styles.css +386 -0
- package/apps/gateway/bin/enigma-gateway.mjs +52 -0
- package/apps/gateway/src/server.mjs +1403 -0
- package/apps/native-host/README.md +265 -0
- package/apps/native-host/bin/enigma-native-host.mjs +499 -0
- package/apps/native-host/manifests/com.enigma.native_host.chrome.json +9 -0
- package/apps/native-host/manifests/com.enigma.native_host.edge.json +9 -0
- package/apps/native-host/manifests/com.enigma.native_host.firefox.json +9 -0
- package/apps/relay/bin/enigma-relay.mjs +52 -0
- package/apps/relay/src/server.mjs +1571 -0
- package/apps/verifier/bin/enigma-verify.mjs +240 -0
- package/deploy/docker-compose.production.example.yml +150 -0
- package/deploy/kubernetes/enigma-backend.example.yaml +580 -0
- package/package.json +196 -0
- package/packages/adapters/PACKAGE_CONTRACT.md +47 -0
- package/packages/adapters/src/index.js +183 -0
- package/packages/boundary/src/index.js +468 -0
- package/packages/connectors/src/index.js +533 -0
- package/packages/core/src/index.js +766 -0
- package/packages/enterprise/src/index.js +505 -0
- package/packages/importers/src/index.js +1083 -0
- package/packages/mcp-server/bin/enigma-mcp.mjs +19 -0
- package/packages/mcp-server/src/index.js +1185 -0
- package/packages/mesh/src/index.js +531 -0
- package/packages/metering/PACKAGE_CONTRACT.md +20 -0
- package/packages/metering/src/index.js +246 -0
- package/packages/optimizer/src/index.js +707 -0
- package/packages/passport/src/index.js +651 -0
- package/packages/settlement/PACKAGE_CONTRACT.md +28 -0
- package/packages/settlement/src/index.js +369 -0
- package/packages/storage/PACKAGE_CONTRACT.md +23 -0
- package/packages/storage/src/index.js +392 -0
- package/packages/vault/src/index.js +904 -0
- package/scripts/build-ai-orchestration-plan.mjs +248 -0
- package/scripts/build-cloudflare-pages-release-packet.mjs +287 -0
- package/scripts/build-cloudflare-token-policy.mjs +239 -0
- package/scripts/build-cloudflare-token-request.mjs +314 -0
- package/scripts/build-demo-assets.mjs +263 -0
- package/scripts/build-edge-backend-workers.mjs +353 -0
- package/scripts/build-goal-completion-audit.mjs +398 -0
- package/scripts/build-hosted-probe-worker.mjs +191 -0
- package/scripts/build-operator-acceptance-packet.mjs +254 -0
- package/scripts/build-operator-evidence-starter.mjs +525 -0
- package/scripts/build-production-backend-env-kit.mjs +384 -0
- package/scripts/build-production-dependency-report.mjs +369 -0
- package/scripts/build-production-handoff-packet.mjs +475 -0
- package/scripts/build-production-readiness-manifest.mjs +209 -0
- package/scripts/build-production-status-board.mjs +297 -0
- package/scripts/build-production-storage-migration.mjs +86 -0
- package/scripts/build-production-workplan.mjs +371 -0
- package/scripts/build-review-packet.mjs +594 -0
- package/scripts/check.mjs +419 -0
- package/scripts/cloudflare-ops.mjs +1672 -0
- package/scripts/cloudflare-secret-env.mjs +128 -0
- package/scripts/collect-hosted-backend-live-evidence.mjs +263 -0
- package/scripts/infrastructure-readiness.mjs +944 -0
- package/scripts/install-enigma-local.mjs +270 -0
- package/scripts/memory-optimization-benchmark.mjs +242 -0
- package/scripts/monitor-live-endpoints.mjs +358 -0
- package/scripts/release-audit.mjs +5588 -0
- package/scripts/release-provenance.mjs +250 -0
- package/scripts/run-backend-readiness-smoke.mjs +220 -0
- package/scripts/validate-backup-restore-drill.mjs +166 -0
- package/scripts/validate-cloudflare-credentials.mjs +112 -0
- package/scripts/validate-cloudflare-worker-inspect.mjs +179 -0
- package/scripts/validate-domain-tls.mjs +248 -0
- package/scripts/validate-hosted-backend-live.mjs +319 -0
- package/scripts/validate-incident-drill.mjs +221 -0
- package/scripts/validate-kms-custody.mjs +235 -0
- package/scripts/validate-legal-compliance-approval.mjs +274 -0
- package/scripts/validate-monitoring-alerting.mjs +263 -0
- package/scripts/validate-network-access-policy.mjs +285 -0
- package/scripts/validate-operator-acceptance.mjs +298 -0
- package/scripts/validate-production-manifests.mjs +325 -0
- package/scripts/validate-public-site-security.mjs +252 -0
- package/scripts/validate-security-threat-model.mjs +285 -0
- package/scripts/validate-service-settlement.mjs +256 -0
- package/scripts/validate-support-sla.mjs +190 -0
- package/scripts/validate-tenant-policy-approval.mjs +228 -0
- package/scripts/validate-usage-metering.mjs +224 -0
- package/scripts/validate-whitepaper-claims.mjs +166 -0
- package/specs/boundary-manifest-v1.schema.json +35 -0
- package/specs/capsule-v1.schema.json +55 -0
- package/specs/claim-boundary-manifest-v1.schema.json +22 -0
- package/specs/connector-profile-v1.schema.json +62 -0
- package/specs/deletion-tombstone-v1.schema.json +26 -0
- package/specs/enterprise-policy-v1.schema.json +59 -0
- package/specs/gateway-request-v1.schema.json +32 -0
- package/specs/goal-completion-audit-v1.schema.json +70 -0
- package/specs/hosted-backend-live-evidence-v1.schema.json +147 -0
- package/specs/import-report-v1.schema.json +214 -0
- package/specs/infrastructure-readiness-manifest-v1.schema.json +130 -0
- package/specs/memory-event-v1.schema.json +42 -0
- package/specs/mesh-node-v1.schema.json +126 -0
- package/specs/passport-v1.schema.json +50 -0
- package/specs/production-handoff-packet-v1.schema.json +149 -0
- package/specs/receipt-v1.schema.json +61 -0
- package/specs/relay-record-v1.schema.json +43 -0
- package/specs/state-checkpoint-v1.schema.json +37 -0
- package/specs/trust-bundle-v1.schema.json +56 -0
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
import { createPublicKey, randomUUID } from 'node:crypto';
|
|
2
|
+
import {
|
|
3
|
+
createMemoryAddress,
|
|
4
|
+
generateSigningKeyPair,
|
|
5
|
+
sha256Hex,
|
|
6
|
+
signPayload,
|
|
7
|
+
verifySignature,
|
|
8
|
+
} from '../../core/src/index.js';
|
|
9
|
+
|
|
10
|
+
const POLICY_SCHEMA = 'enigma.enterprise_policy.v1';
|
|
11
|
+
const EVALUATION_SCHEMA = 'enigma.enterprise_policy_evaluation.v1';
|
|
12
|
+
const GATEWAY_DECISION_SCHEMA = 'enigma.gateway_decision.v1';
|
|
13
|
+
const SIEM_EVENT_SCHEMA = 'enigma.enterprise_siem_event.v1';
|
|
14
|
+
const DEFAULT_CREATED_AT = '1970-01-01T00:00:00.000Z';
|
|
15
|
+
const DEFAULT_ACTIVE_ROOT = `sha256:${sha256Hex('enigma.enterprise.empty_active_root.v1')}`;
|
|
16
|
+
const SHA256_PREFIX = 'sha256:';
|
|
17
|
+
|
|
18
|
+
function isPlainRecord(value) {
|
|
19
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function nowIso(now) {
|
|
23
|
+
if (now instanceof Date) return now.toISOString();
|
|
24
|
+
if (typeof now === 'string') return new Date(now).toISOString();
|
|
25
|
+
return new Date().toISOString();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function uniqueStrings(value) {
|
|
29
|
+
const input = Array.isArray(value) ? value : value === undefined || value === null ? [] : [value];
|
|
30
|
+
return [...new Set(input.map((item) => String(item)).filter((item) => item.length > 0))].sort();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function positiveInteger(value, fallback) {
|
|
34
|
+
const number = Number(value ?? fallback);
|
|
35
|
+
return Number.isInteger(number) && number >= 0 ? number : fallback;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function stableStringify(value) {
|
|
39
|
+
if (value === null) return 'null';
|
|
40
|
+
if (typeof value === 'string') return JSON.stringify(value);
|
|
41
|
+
if (typeof value === 'number') {
|
|
42
|
+
if (!Number.isFinite(value)) throw new TypeError('stable JSON cannot encode non-finite numbers');
|
|
43
|
+
return JSON.stringify(Object.is(value, -0) ? 0 : value);
|
|
44
|
+
}
|
|
45
|
+
if (typeof value === 'boolean') return value ? 'true' : 'false';
|
|
46
|
+
if (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(',')}]`;
|
|
47
|
+
if (isPlainRecord(value)) {
|
|
48
|
+
return `{${Object.keys(value)
|
|
49
|
+
.filter((key) => value[key] !== undefined)
|
|
50
|
+
.sort()
|
|
51
|
+
.map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`)
|
|
52
|
+
.join(',')}}`;
|
|
53
|
+
}
|
|
54
|
+
throw new TypeError(`stable JSON cannot encode ${typeof value}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function hashValue(value) {
|
|
58
|
+
return `${SHA256_PREFIX}${sha256Hex(typeof value === 'string' ? value : stableStringify(value))}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function keyIdFromPublicKey(publicKey) {
|
|
62
|
+
return `ed25519:${sha256Hex(publicKey).slice(0, 32)}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function publicKeyFromPrivateKey(privateKey) {
|
|
66
|
+
return createPublicKey(privateKey).export({ type: 'spki', format: 'pem' });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function withoutUndefinedFields(record) {
|
|
70
|
+
return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function normalizeKms(options = {}) {
|
|
74
|
+
const kms = options.kms ?? options.kmsMetadata ?? {};
|
|
75
|
+
return withoutUndefinedFields({
|
|
76
|
+
provider: kms.provider ?? options.kms_provider ?? options.kmsProvider ?? 'customer_kms',
|
|
77
|
+
key_id: kms.key_id ?? kms.keyId ?? options.kms_key_id ?? options.kmsKeyId,
|
|
78
|
+
key_version: kms.key_version ?? kms.keyVersion ?? options.kms_key_version ?? options.kmsKeyVersion,
|
|
79
|
+
region: kms.region ?? options.kms_region ?? options.kmsRegion,
|
|
80
|
+
evidence_hash: kms.evidence_hash ?? kms.evidenceHash ?? (kms.evidence ? hashValue(kms.evidence) : undefined),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function normalizeLegalHolds(value) {
|
|
85
|
+
const holds = Array.isArray(value) ? value : value === undefined || value === null ? [] : [value];
|
|
86
|
+
return holds
|
|
87
|
+
.map((hold, index) => {
|
|
88
|
+
if (typeof hold === 'string') {
|
|
89
|
+
return {
|
|
90
|
+
hold_id: `hold_${String(index + 1).padStart(3, '0')}`,
|
|
91
|
+
memory_addr: hold,
|
|
92
|
+
status: 'active',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (!isPlainRecord(hold)) return undefined;
|
|
96
|
+
return withoutUndefinedFields({
|
|
97
|
+
hold_id: String(hold.hold_id ?? hold.holdId ?? `hold_${String(index + 1).padStart(3, '0')}`),
|
|
98
|
+
memory_addr: hold.memory_addr ?? hold.memoryAddr,
|
|
99
|
+
memory_id: hold.memory_id ?? hold.memoryId,
|
|
100
|
+
subject_id: hold.subject_id ?? hold.subjectId,
|
|
101
|
+
scope_hash: hold.scope_hash ?? hold.scopeHash ?? (hold.scope ? hashValue(hold.scope) : undefined),
|
|
102
|
+
reason_code: hold.reason_code ?? hold.reasonCode,
|
|
103
|
+
status: hold.status ?? (hold.active === false ? 'released' : 'active'),
|
|
104
|
+
});
|
|
105
|
+
})
|
|
106
|
+
.filter(Boolean)
|
|
107
|
+
.sort((left, right) => stableStringify(left).localeCompare(stableStringify(right)));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function policyForHash(policy) {
|
|
111
|
+
const { policy_hash, policyHash, ...rest } = policy ?? {};
|
|
112
|
+
return rest;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function hashPolicy(policy) {
|
|
116
|
+
if (!isPlainRecord(policy)) return hashValue({ schema: POLICY_SCHEMA, invalid: true });
|
|
117
|
+
return hashValue(policyForHash(policy));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function includes(list, value) {
|
|
121
|
+
return list.includes(String(value ?? ''));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function activeLegalHoldMatches(hold, request) {
|
|
125
|
+
if (hold.status && hold.status !== 'active') return false;
|
|
126
|
+
const memoryAddr = request.memory_addr ?? request.memoryAddr;
|
|
127
|
+
const memoryId = request.memory_id ?? request.memoryId;
|
|
128
|
+
const subjectId = request.subject_id ?? request.subjectId;
|
|
129
|
+
if (hold.memory_addr && memoryAddr && hold.memory_addr === memoryAddr) return true;
|
|
130
|
+
if (hold.memory_id && memoryId && hold.memory_id === memoryId) return true;
|
|
131
|
+
if (hold.subject_id && subjectId && hold.subject_id === subjectId) return true;
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function sanitizeKeyEvidence(value = {}) {
|
|
136
|
+
const evidence = value.key_evidence ?? value.keyEvidence ?? value;
|
|
137
|
+
if (!isPlainRecord(evidence)) return {};
|
|
138
|
+
return withoutUndefinedFields({
|
|
139
|
+
provider: evidence.provider,
|
|
140
|
+
key_id: evidence.key_id ?? evidence.keyId,
|
|
141
|
+
key_version: evidence.key_version ?? evidence.keyVersion,
|
|
142
|
+
region: evidence.region,
|
|
143
|
+
evidence_hash: evidence.evidence_hash ?? evidence.evidenceHash ?? (evidence.evidence ? hashValue(evidence.evidence) : undefined),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function unsignedGatewayDecision(decision) {
|
|
148
|
+
const { signature, signer, public_key, publicKey, verification_key, verificationKey, ...unsigned } = decision;
|
|
149
|
+
return unsigned;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function requestFromArgs(args = {}) {
|
|
153
|
+
return withoutUndefinedFields({
|
|
154
|
+
...(isPlainRecord(args.request) ? args.request : {}),
|
|
155
|
+
operation: args.operation ?? args.request?.operation,
|
|
156
|
+
provider: args.provider ?? args.request?.provider,
|
|
157
|
+
model: args.model ?? args.request?.model,
|
|
158
|
+
region: args.region ?? args.request?.region,
|
|
159
|
+
purpose: args.purpose ?? args.request?.purpose,
|
|
160
|
+
sensitivity: args.sensitivity ?? args.request?.sensitivity,
|
|
161
|
+
memory_addr: args.memory_addr ?? args.memoryAddr ?? args.request?.memory_addr ?? args.request?.memoryAddr,
|
|
162
|
+
memory_id: args.memory_id ?? args.memoryId ?? args.request?.memory_id ?? args.request?.memoryId,
|
|
163
|
+
subject_id: args.subject_id ?? args.subjectId ?? args.request?.subject_id ?? args.request?.subjectId,
|
|
164
|
+
legal_hold_delete: args.legal_hold_delete ?? args.legalHoldDelete ?? args.request?.legal_hold_delete ?? args.request?.legalHoldDelete,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function createEnterprisePolicy(options = {}) {
|
|
169
|
+
const createdAt = options.created_at ?? options.createdAt ?? nowIso(options.now ?? DEFAULT_CREATED_AT);
|
|
170
|
+
const policy = {
|
|
171
|
+
schema: POLICY_SCHEMA,
|
|
172
|
+
policy_id: options.policy_id ?? options.policyId ?? `policy_${randomUUID()}`,
|
|
173
|
+
tenant_id: options.tenant_id ?? options.tenantId ?? 'tenant_local',
|
|
174
|
+
mode: options.mode ?? 'byoc',
|
|
175
|
+
created_at: createdAt,
|
|
176
|
+
updated_at: options.updated_at ?? options.updatedAt ?? createdAt,
|
|
177
|
+
default_action: 'deny_unknown',
|
|
178
|
+
public_proof: options.public_proof ?? options.publicProof ?? 'hash_only',
|
|
179
|
+
allowed_operations: uniqueStrings(options.allowed_operations ?? options.allowedOperations ?? ['retrieve', 'remember', 'write', 'delete']),
|
|
180
|
+
allowed_providers: uniqueStrings(options.allowed_providers ?? options.allowedProviders),
|
|
181
|
+
allowed_models: uniqueStrings(options.allowed_models ?? options.allowedModels),
|
|
182
|
+
allowed_regions: uniqueStrings(options.allowed_regions ?? options.allowedRegions),
|
|
183
|
+
denied_sensitivities: uniqueStrings(options.denied_sensitivities ?? options.sensitivity_deny_list ?? options.deniedSensitivities ?? options.sensitivityDenyList),
|
|
184
|
+
allowed_purposes: uniqueStrings(options.allowed_purposes ?? options.purpose_allow_list ?? options.allowedPurposes ?? options.purposeAllowList),
|
|
185
|
+
legal_holds: normalizeLegalHolds(options.legal_holds ?? options.legalHolds),
|
|
186
|
+
retention_days: positiveInteger(options.retention_days ?? options.retentionDays, 365),
|
|
187
|
+
kms: normalizeKms(options),
|
|
188
|
+
provider_native_memory: 'cache_only',
|
|
189
|
+
};
|
|
190
|
+
return { ...policy, policy_hash: hashPolicy(policy) };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function evaluateEnterprisePolicy(policy, request = {}) {
|
|
194
|
+
const evaluatedAt = nowIso(request.now);
|
|
195
|
+
const reasonCodes = [];
|
|
196
|
+
|
|
197
|
+
if (!isPlainRecord(policy) || policy.schema !== POLICY_SCHEMA) {
|
|
198
|
+
return {
|
|
199
|
+
schema: EVALUATION_SCHEMA,
|
|
200
|
+
allowed: false,
|
|
201
|
+
allow: false,
|
|
202
|
+
decision: 'deny',
|
|
203
|
+
reason_codes: ['POLICY_INVALID'],
|
|
204
|
+
policy_hash: hashPolicy(policy),
|
|
205
|
+
evaluated_at: evaluatedAt,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const allowedOperations = uniqueStrings(policy.allowed_operations ?? policy.allowedOperations);
|
|
210
|
+
const allowedProviders = uniqueStrings(policy.allowed_providers ?? policy.allowedProviders);
|
|
211
|
+
const allowedModels = uniqueStrings(policy.allowed_models ?? policy.allowedModels);
|
|
212
|
+
const allowedRegions = uniqueStrings(policy.allowed_regions ?? policy.allowedRegions);
|
|
213
|
+
const allowedPurposes = uniqueStrings(policy.allowed_purposes ?? policy.allowedPurposes);
|
|
214
|
+
const deniedSensitivities = uniqueStrings(policy.denied_sensitivities ?? policy.sensitivity_deny_list ?? policy.deniedSensitivities ?? policy.sensitivityDenyList);
|
|
215
|
+
const legalHolds = normalizeLegalHolds(policy.legal_holds ?? policy.legalHolds);
|
|
216
|
+
|
|
217
|
+
const operation = request.operation === undefined || request.operation === null || request.operation === '' ? undefined : String(request.operation);
|
|
218
|
+
const provider = request.provider;
|
|
219
|
+
const model = request.model;
|
|
220
|
+
const region = request.region;
|
|
221
|
+
const purpose = request.purpose;
|
|
222
|
+
const sensitivity = request.sensitivity;
|
|
223
|
+
|
|
224
|
+
if (!operation) reasonCodes.push('OPERATION_UNKNOWN');
|
|
225
|
+
else if (allowedOperations.length === 0 || !includes(allowedOperations, operation)) reasonCodes.push('OPERATION_DENIED');
|
|
226
|
+
if (!provider) reasonCodes.push('PROVIDER_UNKNOWN');
|
|
227
|
+
else if (allowedProviders.length === 0 || !includes(allowedProviders, provider)) reasonCodes.push('PROVIDER_DENIED');
|
|
228
|
+
|
|
229
|
+
if (!model) reasonCodes.push('MODEL_UNKNOWN');
|
|
230
|
+
else if (allowedModels.length === 0 || !includes(allowedModels, model)) reasonCodes.push('MODEL_DENIED');
|
|
231
|
+
|
|
232
|
+
if (!region) reasonCodes.push('REGION_UNKNOWN');
|
|
233
|
+
else if (allowedRegions.length === 0 || !includes(allowedRegions, region)) reasonCodes.push('REGION_DENIED');
|
|
234
|
+
|
|
235
|
+
if (!purpose) reasonCodes.push('PURPOSE_UNKNOWN');
|
|
236
|
+
else if (allowedPurposes.length === 0 || !includes(allowedPurposes, purpose)) reasonCodes.push('PURPOSE_DENIED');
|
|
237
|
+
|
|
238
|
+
if (!sensitivity) reasonCodes.push('SENSITIVITY_UNKNOWN');
|
|
239
|
+
else if (includes(deniedSensitivities, sensitivity)) reasonCodes.push('SENSITIVITY_DENIED');
|
|
240
|
+
|
|
241
|
+
if ((operation === 'delete' || request.legal_hold_delete === true || request.legalHoldDelete === true) && legalHolds.some((hold) => activeLegalHoldMatches(hold, request))) {
|
|
242
|
+
reasonCodes.push('LEGAL_HOLD_DELETE_DENIED');
|
|
243
|
+
} else if (request.legal_hold_delete === true || request.legalHoldDelete === true) {
|
|
244
|
+
reasonCodes.push('LEGAL_HOLD_DELETE_DENIED');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const allowed = reasonCodes.length === 0;
|
|
248
|
+
const decision = allowed ? 'allow' : 'deny';
|
|
249
|
+
const memoryAddr = request.memory_addr ?? request.memoryAddr;
|
|
250
|
+
return withoutUndefinedFields({
|
|
251
|
+
schema: EVALUATION_SCHEMA,
|
|
252
|
+
allowed,
|
|
253
|
+
allow: allowed,
|
|
254
|
+
decision,
|
|
255
|
+
reason_codes: allowed ? ['ALLOW'] : reasonCodes,
|
|
256
|
+
policy_id: policy.policy_id,
|
|
257
|
+
policy_hash: hashPolicy(policy),
|
|
258
|
+
evaluated_at: evaluatedAt,
|
|
259
|
+
operation,
|
|
260
|
+
provider,
|
|
261
|
+
model,
|
|
262
|
+
region,
|
|
263
|
+
purpose,
|
|
264
|
+
sensitivity,
|
|
265
|
+
memory_addr_hash: memoryAddr ? hashValue(memoryAddr) : undefined,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function minimizeEnterpriseEvaluation(evaluation = {}) {
|
|
270
|
+
return withoutUndefinedFields({
|
|
271
|
+
schema: evaluation.schema,
|
|
272
|
+
allowed: evaluation.allowed,
|
|
273
|
+
allow: evaluation.allow,
|
|
274
|
+
decision: evaluation.decision,
|
|
275
|
+
reason_codes: evaluation.reason_codes,
|
|
276
|
+
policy_id: evaluation.policy_id,
|
|
277
|
+
policy_hash: evaluation.policy_hash,
|
|
278
|
+
evaluated_at: evaluation.evaluated_at,
|
|
279
|
+
memory_addr_hash: evaluation.memory_addr_hash,
|
|
280
|
+
operation_hash: evaluation.operation_hash ?? (evaluation.operation ? hashValue(evaluation.operation) : undefined),
|
|
281
|
+
provider_hash: evaluation.provider_hash ?? (evaluation.provider ? hashValue(evaluation.provider) : undefined),
|
|
282
|
+
model_hash: evaluation.model_hash ?? (evaluation.model ? hashValue(evaluation.model) : undefined),
|
|
283
|
+
region_hash: evaluation.region_hash ?? (evaluation.region ? hashValue(evaluation.region) : undefined),
|
|
284
|
+
purpose_hash: evaluation.purpose_hash ?? (evaluation.purpose ? hashValue(evaluation.purpose) : undefined),
|
|
285
|
+
sensitivity_hash: evaluation.sensitivity_hash ?? (evaluation.sensitivity ? hashValue(evaluation.sensitivity) : undefined),
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export function minimizeEnterprisePolicy(policy = {}) {
|
|
290
|
+
return {
|
|
291
|
+
schema: policy.schema,
|
|
292
|
+
policy_id: policy.policy_id,
|
|
293
|
+
tenant_id: policy.tenant_id,
|
|
294
|
+
mode: policy.mode,
|
|
295
|
+
default_action: policy.default_action,
|
|
296
|
+
provider_native_memory: policy.provider_native_memory,
|
|
297
|
+
public_proof: policy.public_proof,
|
|
298
|
+
policy_hash: policy.policy_hash,
|
|
299
|
+
created_at: policy.created_at,
|
|
300
|
+
updated_at: policy.updated_at,
|
|
301
|
+
retention_days: policy.retention_days,
|
|
302
|
+
constraints: {
|
|
303
|
+
allowed_operations: Array.isArray(policy.allowed_operations) ? policy.allowed_operations.length : 0,
|
|
304
|
+
allowed_providers: Array.isArray(policy.allowed_providers) ? policy.allowed_providers.length : 0,
|
|
305
|
+
allowed_models: Array.isArray(policy.allowed_models) ? policy.allowed_models.length : 0,
|
|
306
|
+
allowed_regions: Array.isArray(policy.allowed_regions) ? policy.allowed_regions.length : 0,
|
|
307
|
+
allowed_purposes: Array.isArray(policy.allowed_purposes) ? policy.allowed_purposes.length : 0,
|
|
308
|
+
denied_sensitivities: Array.isArray(policy.denied_sensitivities) ? policy.denied_sensitivities.length : 0,
|
|
309
|
+
legal_holds: Array.isArray(policy.legal_holds) ? policy.legal_holds.length : 0,
|
|
310
|
+
},
|
|
311
|
+
kms_hash: hashValue(policy.kms ?? {}),
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export function createGatewayDecision(args = {}) {
|
|
316
|
+
if (!isPlainRecord(args.policy)) throw new TypeError('createGatewayDecision requires policy');
|
|
317
|
+
const request = requestFromArgs(args);
|
|
318
|
+
const evaluation = args.evaluation ?? evaluateEnterprisePolicy(args.policy, request);
|
|
319
|
+
const keyPair = args.signingKeyPair ?? args.keyPair ?? (args.privateKey ? undefined : generateSigningKeyPair({ key_id: args.key_id ?? args.keyId }));
|
|
320
|
+
const privateKey = args.privateKey ?? keyPair?.privateKey;
|
|
321
|
+
if (!privateKey) throw new TypeError('createGatewayDecision requires privateKey or signingKeyPair');
|
|
322
|
+
const publicKey = args.publicKey ?? args.public_key ?? keyPair?.publicKey ?? publicKeyFromPrivateKey(privateKey);
|
|
323
|
+
const signer = args.signer ?? keyPair?.signer ?? { key_id: args.key_id ?? args.keyId ?? keyIdFromPublicKey(publicKey), alg: 'Ed25519' };
|
|
324
|
+
const memoryAddr = request.memory_addr ?? request.memoryAddr;
|
|
325
|
+
const issuedAt = args.issued_at ?? args.issuedAt ?? nowIso(args.now);
|
|
326
|
+
const keyEvidence = sanitizeKeyEvidence(args.key_evidence ?? args.keyEvidence ?? args.policy.kms);
|
|
327
|
+
const policyHash = hashPolicy(args.policy);
|
|
328
|
+
const operation = evaluation.operation ?? request.operation;
|
|
329
|
+
const provider = request.provider ?? evaluation.provider;
|
|
330
|
+
const model = request.model ?? evaluation.model;
|
|
331
|
+
const region = request.region ?? evaluation.region;
|
|
332
|
+
const purpose = request.purpose ?? evaluation.purpose;
|
|
333
|
+
const sensitivity = request.sensitivity ?? evaluation.sensitivity;
|
|
334
|
+
const unsigned = withoutUndefinedFields({
|
|
335
|
+
schema: GATEWAY_DECISION_SCHEMA,
|
|
336
|
+
decision_id: args.decision_id ?? args.decisionId ?? `gwd_${randomUUID()}`,
|
|
337
|
+
issued_at: issuedAt,
|
|
338
|
+
gateway_id: args.gateway_id ?? args.gatewayId ?? 'gateway_local',
|
|
339
|
+
tenant_id: args.policy.tenant_id,
|
|
340
|
+
policy_id: args.policy.policy_id,
|
|
341
|
+
policy_hash: policyHash,
|
|
342
|
+
memory_addr_hash: memoryAddr ? hashValue(memoryAddr) : undefined,
|
|
343
|
+
operation_hash: operation ? hashValue(operation) : undefined,
|
|
344
|
+
provider_hash: provider ? hashValue(provider) : undefined,
|
|
345
|
+
model_hash: model ? hashValue(model) : undefined,
|
|
346
|
+
region_hash: region ? hashValue(region) : undefined,
|
|
347
|
+
purpose_hash: purpose ? hashValue(purpose) : undefined,
|
|
348
|
+
sensitivity_hash: sensitivity ? hashValue(sensitivity) : undefined,
|
|
349
|
+
decision: evaluation.decision,
|
|
350
|
+
allowed: evaluation.allowed,
|
|
351
|
+
reason_codes: evaluation.reason_codes,
|
|
352
|
+
active_root: args.active_root ?? args.activeRoot ?? DEFAULT_ACTIVE_ROOT,
|
|
353
|
+
key_evidence_hash: Object.keys(keyEvidence).length > 0 ? hashValue(keyEvidence) : undefined,
|
|
354
|
+
request_hash: hashValue(request),
|
|
355
|
+
evaluation_hash: hashValue(evaluation),
|
|
356
|
+
});
|
|
357
|
+
return {
|
|
358
|
+
...unsigned,
|
|
359
|
+
signer: { key_id: signer.key_id, alg: signer.alg ?? 'Ed25519' },
|
|
360
|
+
signature: {
|
|
361
|
+
alg: 'Ed25519',
|
|
362
|
+
key_id: signer.key_id,
|
|
363
|
+
value: signPayload(stableStringify(unsigned), privateKey),
|
|
364
|
+
},
|
|
365
|
+
public_key: publicKey,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export function verifyGatewayDecision(args = {}, maybePolicy, maybePublicKey) {
|
|
370
|
+
const decision = args.schema === GATEWAY_DECISION_SCHEMA ? args : args.decision;
|
|
371
|
+
const policy = maybePolicy ?? args.policy;
|
|
372
|
+
const publicKey = maybePublicKey ?? args.trustedPublicKey ?? args.trusted_public_key ?? args.publicKey ?? args.public_key;
|
|
373
|
+
const trustedKeyId = args.trustedKeyId ?? args.trusted_key_id;
|
|
374
|
+
const errors = [];
|
|
375
|
+
if (!isPlainRecord(decision) || decision.schema !== GATEWAY_DECISION_SCHEMA) errors.push('DECISION_INVALID');
|
|
376
|
+
if (isPlainRecord(decision) && decision.policy_hash !== hashPolicy(policy)) errors.push('POLICY_HASH_MISMATCH');
|
|
377
|
+
if (!publicKey) errors.push('TRUSTED_PUBLIC_KEY_REQUIRED');
|
|
378
|
+
if (trustedKeyId !== undefined && decision?.signer?.key_id !== trustedKeyId) errors.push('TRUSTED_KEY_ID_MISMATCH');
|
|
379
|
+
const signature = decision?.signature?.value ?? decision?.signature;
|
|
380
|
+
if (!signature) errors.push('SIGNATURE_MISSING');
|
|
381
|
+
if (errors.length === 0 && !verifySignature(stableStringify(unsignedGatewayDecision(decision)), signature, publicKey)) errors.push('SIGNATURE_INVALID');
|
|
382
|
+
const ok = errors.length === 0;
|
|
383
|
+
return {
|
|
384
|
+
ok,
|
|
385
|
+
valid: ok,
|
|
386
|
+
errors,
|
|
387
|
+
policy_hash: isPlainRecord(policy) ? hashPolicy(policy) : undefined,
|
|
388
|
+
decision_id: decision?.decision_id,
|
|
389
|
+
signer: decision?.signer,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export function exportSiemEvent(args = {}) {
|
|
394
|
+
const decision = args.schema === GATEWAY_DECISION_SCHEMA ? args : args.decision;
|
|
395
|
+
const evaluation = args.schema === EVALUATION_SCHEMA ? args : args.evaluation;
|
|
396
|
+
const policy = args.policy;
|
|
397
|
+
const source = decision ?? evaluation ?? {};
|
|
398
|
+
const provider = source.provider ?? args.provider;
|
|
399
|
+
const model = source.model ?? args.model;
|
|
400
|
+
const region = source.region ?? args.region;
|
|
401
|
+
const purpose = source.purpose ?? args.purpose;
|
|
402
|
+
const sensitivity = source.sensitivity ?? args.sensitivity;
|
|
403
|
+
return withoutUndefinedFields({
|
|
404
|
+
schema: SIEM_EVENT_SCHEMA,
|
|
405
|
+
event_id: args.event_id ?? args.eventId ?? `siem_${randomUUID()}`,
|
|
406
|
+
emitted_at: args.emitted_at ?? args.emittedAt ?? nowIso(args.now),
|
|
407
|
+
tenant_id: source.tenant_id ?? policy?.tenant_id,
|
|
408
|
+
gateway_id: source.gateway_id,
|
|
409
|
+
policy_id: source.policy_id ?? policy?.policy_id,
|
|
410
|
+
policy_hash: source.policy_hash ?? (policy ? hashPolicy(policy) : undefined),
|
|
411
|
+
decision_id: source.decision_id,
|
|
412
|
+
decision: source.decision,
|
|
413
|
+
allowed: source.allowed,
|
|
414
|
+
reason_codes: source.reason_codes ?? [],
|
|
415
|
+
operation_hash: source.operation_hash ?? (source.operation ? hashValue(source.operation) : undefined),
|
|
416
|
+
memory_addr_hash: source.memory_addr_hash ?? (source.memory_addr ? hashValue(source.memory_addr) : undefined),
|
|
417
|
+
active_root: source.active_root,
|
|
418
|
+
key_evidence_hash: source.key_evidence_hash ?? (source.key_evidence ? hashValue(source.key_evidence) : undefined),
|
|
419
|
+
provider_hash: source.provider_hash ?? (provider ? hashValue(provider) : undefined),
|
|
420
|
+
model_hash: source.model_hash ?? (model ? hashValue(model) : undefined),
|
|
421
|
+
region_hash: source.region_hash ?? (region ? hashValue(region) : undefined),
|
|
422
|
+
purpose_hash: source.purpose_hash ?? (purpose ? hashValue(purpose) : undefined),
|
|
423
|
+
sensitivity_hash: source.sensitivity_hash ?? (sensitivity ? hashValue(sensitivity) : undefined),
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function runEnterpriseDemo(options = {}) {
|
|
428
|
+
const signingKeyPair = generateSigningKeyPair({ key_id: 'enterprise-demo-key' });
|
|
429
|
+
const memoryAddr = createMemoryAddress({
|
|
430
|
+
secret: options.address_key ?? 'enterprise-demo-address-key',
|
|
431
|
+
tenant_id: 'tenant_acme',
|
|
432
|
+
subject_id: 'employee_123',
|
|
433
|
+
value: 'enterprise-demo-source-object',
|
|
434
|
+
});
|
|
435
|
+
const activeRoot = hashValue(['active', memoryAddr]);
|
|
436
|
+
const policy = createEnterprisePolicy({
|
|
437
|
+
policy_id: 'policy_acme_prod',
|
|
438
|
+
tenant_id: 'tenant_acme',
|
|
439
|
+
mode: 'byoc',
|
|
440
|
+
allowed_providers: ['anthropic', 'kimi'],
|
|
441
|
+
allowed_models: ['claude-3-5-sonnet', 'kimi-k2'],
|
|
442
|
+
allowed_regions: ['eu-central-1', 'us-east-1'],
|
|
443
|
+
allowed_purposes: ['agent_context', 'support_retrieval'],
|
|
444
|
+
denied_sensitivities: ['restricted', 'secret'],
|
|
445
|
+
legal_holds: [memoryAddr],
|
|
446
|
+
retention_days: 365,
|
|
447
|
+
kms: {
|
|
448
|
+
provider: 'aws_kms',
|
|
449
|
+
key_id: 'arn:aws:kms:us-east-1:111122223333:key/demo',
|
|
450
|
+
key_version: 'k-17',
|
|
451
|
+
region: 'us-east-1',
|
|
452
|
+
},
|
|
453
|
+
now: DEFAULT_CREATED_AT,
|
|
454
|
+
});
|
|
455
|
+
const baseRequest = {
|
|
456
|
+
operation: 'retrieve',
|
|
457
|
+
provider: 'anthropic',
|
|
458
|
+
model: 'claude-3-5-sonnet',
|
|
459
|
+
region: 'us-east-1',
|
|
460
|
+
purpose: 'support_retrieval',
|
|
461
|
+
sensitivity: 'internal',
|
|
462
|
+
memory_addr: memoryAddr,
|
|
463
|
+
};
|
|
464
|
+
const allowRetrieval = evaluateEnterprisePolicy(policy, baseRequest);
|
|
465
|
+
const denyDisallowedRegion = evaluateEnterprisePolicy(policy, { ...baseRequest, region: 'ap-south-1' });
|
|
466
|
+
const denyLegalHoldDelete = evaluateEnterprisePolicy(policy, { ...baseRequest, operation: 'delete' });
|
|
467
|
+
const gatewayDecision = createGatewayDecision({
|
|
468
|
+
policy,
|
|
469
|
+
request: baseRequest,
|
|
470
|
+
evaluation: allowRetrieval,
|
|
471
|
+
active_root: activeRoot,
|
|
472
|
+
signingKeyPair,
|
|
473
|
+
});
|
|
474
|
+
const verification = verifyGatewayDecision({ decision: gatewayDecision, policy, publicKey: signingKeyPair.publicKey });
|
|
475
|
+
const siemEvent = exportSiemEvent({
|
|
476
|
+
policy,
|
|
477
|
+
decision: createGatewayDecision({
|
|
478
|
+
policy,
|
|
479
|
+
request: { ...baseRequest, region: 'ap-south-1' },
|
|
480
|
+
evaluation: denyDisallowedRegion,
|
|
481
|
+
active_root: activeRoot,
|
|
482
|
+
signingKeyPair,
|
|
483
|
+
}),
|
|
484
|
+
});
|
|
485
|
+
return {
|
|
486
|
+
policy,
|
|
487
|
+
allow_retrieval: allowRetrieval,
|
|
488
|
+
deny_disallowed_region: denyDisallowedRegion,
|
|
489
|
+
deny_legal_hold_delete: denyLegalHoldDelete,
|
|
490
|
+
siem_event: siemEvent,
|
|
491
|
+
gateway_decision: gatewayDecision,
|
|
492
|
+
verification,
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
export default {
|
|
497
|
+
createEnterprisePolicy,
|
|
498
|
+
evaluateEnterprisePolicy,
|
|
499
|
+
createGatewayDecision,
|
|
500
|
+
verifyGatewayDecision,
|
|
501
|
+
minimizeEnterpriseEvaluation,
|
|
502
|
+
minimizeEnterprisePolicy,
|
|
503
|
+
exportSiemEvent,
|
|
504
|
+
runEnterpriseDemo,
|
|
505
|
+
};
|