@lanefoundry/gatelane-engine 0.0.0-stage → 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/dist/attack.d.ts +53 -0
- package/dist/attack.d.ts.map +1 -0
- package/dist/attack.js +75 -0
- package/dist/attack.js.map +1 -0
- package/dist/audit.d.ts +56 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +115 -0
- package/dist/audit.js.map +1 -0
- package/dist/compare.d.ts +45 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/compare.js +118 -0
- package/dist/compare.js.map +1 -0
- package/dist/evaluate.d.ts +40 -0
- package/dist/evaluate.d.ts.map +1 -0
- package/dist/evaluate.js +87 -0
- package/dist/evaluate.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/judge.d.ts +68 -0
- package/dist/judge.d.ts.map +1 -0
- package/dist/judge.js +102 -0
- package/dist/judge.js.map +1 -0
- package/dist/llm.d.ts +158 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +540 -0
- package/dist/llm.js.map +1 -0
- package/dist/otel-export.d.ts +36 -0
- package/dist/otel-export.d.ts.map +1 -0
- package/dist/otel-export.js +162 -0
- package/dist/otel-export.js.map +1 -0
- package/dist/replay.d.ts +51 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +260 -0
- package/dist/replay.js.map +1 -0
- package/dist/runner.d.ts +46 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +250 -0
- package/dist/runner.js.map +1 -0
- package/dist/scan.d.ts +172 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +260 -0
- package/dist/scan.js.map +1 -0
- package/dist/sign.d.ts +20 -0
- package/dist/sign.d.ts.map +1 -0
- package/dist/sign.js +64 -0
- package/dist/sign.js.map +1 -0
- package/dist/tracing.d.ts +20 -0
- package/dist/tracing.d.ts.map +1 -0
- package/dist/tracing.js +65 -0
- package/dist/tracing.js.map +1 -0
- package/package.json +89 -4
- package/README.md +0 -3
package/dist/scan.js
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security scan: attack report + patch-verify cycle.
|
|
3
|
+
* Built on top of the gate's replay + judge + compare primitives.
|
|
4
|
+
*/
|
|
5
|
+
import { parseProviderModel } from './llm.js';
|
|
6
|
+
import { LLMJudge } from './judge.js';
|
|
7
|
+
import { replay } from './replay.js';
|
|
8
|
+
import { compare } from './compare.js';
|
|
9
|
+
import { evaluate } from './evaluate.js';
|
|
10
|
+
import { signReport } from './sign.js';
|
|
11
|
+
import { shaOfCandidateRef } from '@lanefoundry/gatelane-sdk';
|
|
12
|
+
import { injectionMetaOf } from './attack.js';
|
|
13
|
+
/**
|
|
14
|
+
* Enrich raw JudgeVerdicts with attack taxonomy (category + ASI).
|
|
15
|
+
* Uses INJECTION_META from attack.ts to look up by item_id.
|
|
16
|
+
*/
|
|
17
|
+
export function enrichVerdicts(verdicts, _dataset) {
|
|
18
|
+
return verdicts.map((v) => {
|
|
19
|
+
const meta = injectionMetaOf(v.item_id);
|
|
20
|
+
return {
|
|
21
|
+
...v,
|
|
22
|
+
category: meta?.category ?? 'prompt_injection', // default
|
|
23
|
+
mapped_asi: meta?.mapped_asi ?? 'ASI01',
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build an AttackReport from enriched verdicts and the dataset.
|
|
29
|
+
*/
|
|
30
|
+
export function buildAttackReport(args) {
|
|
31
|
+
const { verdicts, dataset, candidate_refs, judges } = args;
|
|
32
|
+
// Group verdicts by candidate
|
|
33
|
+
const byCandidate = new Map();
|
|
34
|
+
for (const v of verdicts) {
|
|
35
|
+
const arr = byCandidate.get(v.candidate_ref) ?? [];
|
|
36
|
+
arr.push(v);
|
|
37
|
+
byCandidate.set(v.candidate_ref, arr);
|
|
38
|
+
}
|
|
39
|
+
// Build per-candidate summaries
|
|
40
|
+
const candidates = candidate_refs.map((ref) => {
|
|
41
|
+
const vlist = byCandidate.get(ref) ?? [];
|
|
42
|
+
const survived = vlist.filter((v) => v.outcome === 'pass').length;
|
|
43
|
+
const failed = vlist.filter((v) => v.outcome === 'fail').length;
|
|
44
|
+
const total = vlist.length;
|
|
45
|
+
// By category
|
|
46
|
+
const by_category = {
|
|
47
|
+
prompt_injection: { survived: 0, failed: 0, total: 0 },
|
|
48
|
+
privilege_escalation: { survived: 0, failed: 0, total: 0 },
|
|
49
|
+
code_injection: { survived: 0, failed: 0, total: 0 },
|
|
50
|
+
tool_misuse: { survived: 0, failed: 0, total: 0 },
|
|
51
|
+
};
|
|
52
|
+
for (const v of vlist) {
|
|
53
|
+
const bucket = by_category[v.category];
|
|
54
|
+
bucket.total += 1;
|
|
55
|
+
if (v.outcome === 'pass')
|
|
56
|
+
bucket.survived += 1;
|
|
57
|
+
else
|
|
58
|
+
bucket.failed += 1;
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
candidate_ref: ref,
|
|
62
|
+
total,
|
|
63
|
+
survived,
|
|
64
|
+
failed,
|
|
65
|
+
survival_rate: total === 0 ? 1 : survived / total,
|
|
66
|
+
by_category,
|
|
67
|
+
vulnerabilities: vlist.filter((v) => v.outcome === 'fail'),
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
// Per-ASI gaps across all candidates
|
|
71
|
+
const asiCounts = new Map();
|
|
72
|
+
for (const v of verdicts) {
|
|
73
|
+
const key = v.mapped_asi;
|
|
74
|
+
const cur = asiCounts.get(key) ?? { total: 0, failed: 0 };
|
|
75
|
+
cur.total += 1;
|
|
76
|
+
if (v.outcome === 'fail')
|
|
77
|
+
cur.failed += 1;
|
|
78
|
+
asiCounts.set(key, cur);
|
|
79
|
+
}
|
|
80
|
+
const by_asi = Array.from(asiCounts.entries())
|
|
81
|
+
.map(([asi, { total, failed }]) => ({
|
|
82
|
+
asi,
|
|
83
|
+
total,
|
|
84
|
+
failed,
|
|
85
|
+
fail_rate: total === 0 ? 0 : failed / total,
|
|
86
|
+
}))
|
|
87
|
+
.sort((a, b) => b.fail_rate - a.fail_rate); // highest-impact gaps first
|
|
88
|
+
return {
|
|
89
|
+
dataset_content_hash: dataset.content_hash,
|
|
90
|
+
dataset_version: dataset.version,
|
|
91
|
+
generated_at: new Date().toISOString(),
|
|
92
|
+
candidates,
|
|
93
|
+
by_asi,
|
|
94
|
+
candidate_refs: [...candidate_refs],
|
|
95
|
+
judges: [...judges],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Collect judge verdicts for a replay result.
|
|
100
|
+
* Extracted from runner logic so both runner and scan path share it.
|
|
101
|
+
*/
|
|
102
|
+
export async function collectVerdicts(args) {
|
|
103
|
+
const { replayResult, dataset, candidates, judges, caller, judge_callers } = args;
|
|
104
|
+
const verdicts = [];
|
|
105
|
+
for (const cand of candidates) {
|
|
106
|
+
for (const judgeRef of judges) {
|
|
107
|
+
const judgeCaller = judge_callers?.[judgeRef] ?? caller;
|
|
108
|
+
const { model: judgeModel } = parseProviderModel(judgeRef, 'mock');
|
|
109
|
+
const judge = new LLMJudge({ name: judgeRef, model: judgeModel, caller: judgeCaller });
|
|
110
|
+
const candRows = replayResult.rows.filter((r) => r.candidate_ref === cand.ref);
|
|
111
|
+
for (const row of candRows) {
|
|
112
|
+
const itemId = row.item_id;
|
|
113
|
+
const itemInput = dataset.items?.find((it) => (it.id ?? '<anonymous>') === itemId)?.input;
|
|
114
|
+
const v = await judge.judge({
|
|
115
|
+
candidate_ref: cand.ref,
|
|
116
|
+
item_id: itemId,
|
|
117
|
+
item_input: itemInput ?? null,
|
|
118
|
+
candidate_output: row.response.content,
|
|
119
|
+
});
|
|
120
|
+
verdicts.push(v);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return verdicts;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Resolve candidate refs to { ref, model } objects for replay.
|
|
128
|
+
* Mirrors runner logic: 'model:foo' → model 'foo', others → full ref.
|
|
129
|
+
*/
|
|
130
|
+
export function resolveCandidates(refs) {
|
|
131
|
+
return refs.map((ref) => ({
|
|
132
|
+
ref,
|
|
133
|
+
model: ref.startsWith('model:') ? ref.slice('model:'.length) : ref,
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Run a full security scan: replay + judge + compare + sign + evaluate + attack report.
|
|
138
|
+
* Returns both the attack report and the gate decision.
|
|
139
|
+
*/
|
|
140
|
+
export async function runScanGate(args) {
|
|
141
|
+
const { dataset, candidates, judges, caller, judge_callers, seed, policy, baseline, approver, signing_key, } = args;
|
|
142
|
+
// 1. Replay
|
|
143
|
+
const candidateObjs = resolveCandidates(candidates);
|
|
144
|
+
const replayArgs = {
|
|
145
|
+
dataset,
|
|
146
|
+
candidates: candidateObjs,
|
|
147
|
+
caller,
|
|
148
|
+
...(seed !== undefined ? { seed } : {}),
|
|
149
|
+
};
|
|
150
|
+
const replayResult = await replay(replayArgs);
|
|
151
|
+
// 2. Judge (collect verdicts)
|
|
152
|
+
const verdicts = await collectVerdicts({
|
|
153
|
+
replayResult,
|
|
154
|
+
dataset,
|
|
155
|
+
candidates: candidateObjs,
|
|
156
|
+
judges,
|
|
157
|
+
caller,
|
|
158
|
+
judge_callers,
|
|
159
|
+
});
|
|
160
|
+
// 3. Enrich verdicts with attack taxonomy
|
|
161
|
+
const enriched = enrichVerdicts(verdicts, dataset);
|
|
162
|
+
// 4. Build attack report
|
|
163
|
+
const attackReport = buildAttackReport({
|
|
164
|
+
verdicts: enriched,
|
|
165
|
+
dataset,
|
|
166
|
+
candidate_refs: candidates,
|
|
167
|
+
judges,
|
|
168
|
+
});
|
|
169
|
+
// 5. Compare + evaluate (gate decision)
|
|
170
|
+
const { perCandidate, judgeMatrix } = compare({
|
|
171
|
+
replay: replayResult,
|
|
172
|
+
verdicts,
|
|
173
|
+
...(baseline !== undefined ? { baseline_ref: baseline } : {}),
|
|
174
|
+
});
|
|
175
|
+
const candidate_shas = {};
|
|
176
|
+
for (const ref of candidates)
|
|
177
|
+
candidate_shas[ref] = await shaOfCandidateRef(ref);
|
|
178
|
+
const judge_shas = {};
|
|
179
|
+
for (const j of judges)
|
|
180
|
+
judge_shas[j] = await shaOfCandidateRef(j);
|
|
181
|
+
const scorerCodeSha = await shaOfCandidateRef('gatelane-engine:scan:v0.0.1-dev');
|
|
182
|
+
const candidate_metrics = {};
|
|
183
|
+
for (const [ref, m] of Object.entries(perCandidate)) {
|
|
184
|
+
candidate_metrics[ref] = {
|
|
185
|
+
aggregate_delta: m.aggregate_delta,
|
|
186
|
+
cost_delta: m.cost_delta,
|
|
187
|
+
latency_delta: m.latency_delta,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const baseline_metrics = {};
|
|
191
|
+
if (baseline !== undefined) {
|
|
192
|
+
const b = perCandidate[baseline];
|
|
193
|
+
if (b !== undefined) {
|
|
194
|
+
baseline_metrics.primary_score = b.mean_score;
|
|
195
|
+
baseline_metrics.cost = b.total_cost_usd;
|
|
196
|
+
baseline_metrics.latency = b.mean_latency_ms;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const runId = crypto.randomUUID();
|
|
200
|
+
const reportId = crypto.randomUUID();
|
|
201
|
+
const baseReport = {
|
|
202
|
+
id: reportId,
|
|
203
|
+
gate_run_id: runId,
|
|
204
|
+
dataset_content_hash: dataset.content_hash,
|
|
205
|
+
dataset_version: dataset.version,
|
|
206
|
+
candidate_shas,
|
|
207
|
+
judge_shas,
|
|
208
|
+
scorer_code_sha: scorerCodeSha,
|
|
209
|
+
baseline_metrics,
|
|
210
|
+
candidate_metrics,
|
|
211
|
+
judge_matrix: judgeMatrix,
|
|
212
|
+
policy: policy ?? (await import('@lanefoundry/gatelane-sdk')).DEFAULT_POLICY,
|
|
213
|
+
...(approver !== undefined ? { approver } : {}),
|
|
214
|
+
timestamp: new Date().toISOString(),
|
|
215
|
+
signature: 'pending',
|
|
216
|
+
};
|
|
217
|
+
let gateResult = null;
|
|
218
|
+
if (signing_key) {
|
|
219
|
+
const signature = await signReport(baseReport, signing_key);
|
|
220
|
+
const signed = { ...baseReport, signature };
|
|
221
|
+
const { decision, rule_results } = evaluate({
|
|
222
|
+
candidates,
|
|
223
|
+
perCandidate,
|
|
224
|
+
judgeMatrix,
|
|
225
|
+
policy: baseReport.policy,
|
|
226
|
+
...(approver !== undefined ? { approver } : {}),
|
|
227
|
+
});
|
|
228
|
+
gateResult = { report: signed, decision, evaluate: { decision, rule_results } };
|
|
229
|
+
}
|
|
230
|
+
return { attackReport, gateResult };
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Verify that a patch holds against the same scan dataset.
|
|
234
|
+
* Compares baseline candidate failures vs patched candidate outcomes.
|
|
235
|
+
*/
|
|
236
|
+
export function verifyPatchHolds(args) {
|
|
237
|
+
const { before, after, baselineCandidate, patchedCandidate } = args;
|
|
238
|
+
const beforeSummary = before.candidates.find((c) => c.candidate_ref === baselineCandidate);
|
|
239
|
+
const afterSummary = after.candidates.find((c) => c.candidate_ref === patchedCandidate);
|
|
240
|
+
if (!beforeSummary || !afterSummary) {
|
|
241
|
+
throw new Error(`Candidate not found in reports: baseline=${baselineCandidate}, patched=${patchedCandidate}`);
|
|
242
|
+
}
|
|
243
|
+
const beforeFails = new Set(beforeSummary.vulnerabilities.map((v) => v.item_id));
|
|
244
|
+
const afterFails = new Set(afterSummary.vulnerabilities.map((v) => v.item_id));
|
|
245
|
+
const resolved = Array.from(beforeFails).filter((id) => !afterFails.has(id));
|
|
246
|
+
const regressed = Array.from(afterFails).filter((id) => !beforeFails.has(id));
|
|
247
|
+
const stillVulnerable = Array.from(beforeFails).filter((id) => afterFails.has(id));
|
|
248
|
+
const holds = stillVulnerable.length === 0 && regressed.length === 0;
|
|
249
|
+
return {
|
|
250
|
+
holds,
|
|
251
|
+
resolved,
|
|
252
|
+
regressed,
|
|
253
|
+
stillVulnerable,
|
|
254
|
+
baselineCandidate,
|
|
255
|
+
patchedCandidate,
|
|
256
|
+
before,
|
|
257
|
+
after,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=scan.js.map
|
package/dist/scan.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"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"}
|
package/dist/sign.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMAC-SHA256 signing for GateReport.
|
|
3
|
+
*
|
|
4
|
+
* The signing key comes from the `GATELANE_REPORT_SIGNING_KEY` env var (32+ random
|
|
5
|
+
* bytes recommended). The signed payload is the canonical JSON of the report
|
|
6
|
+
* (without the `signature` field); the signature is base64url-encoded HMAC.
|
|
7
|
+
*
|
|
8
|
+
* The same key reproduces the same signature, so auditors can verify
|
|
9
|
+
* "did this report actually get signed at decision time?" by recomputing.
|
|
10
|
+
*
|
|
11
|
+
* @see docs/prd.md §5.1 — The gate
|
|
12
|
+
*/
|
|
13
|
+
import type { GateReport } from '@lanefoundry/gatelane-sdk';
|
|
14
|
+
/** Stable JSON for signing (excludes `signature`; recursively sorts keys). */
|
|
15
|
+
export declare function canonicalizeReport(report: GateReport): string;
|
|
16
|
+
/** Sign a GateReport with the given key. Returns the new signature string. */
|
|
17
|
+
export declare function signReport(report: GateReport, key: string): Promise<string>;
|
|
18
|
+
/** Verify a GateReport's signature. Returns true if signature matches. */
|
|
19
|
+
export declare function verifyReport(report: GateReport, key: string): Promise<boolean>;
|
|
20
|
+
//# sourceMappingURL=sign.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign.d.ts","sourceRoot":"","sources":["../src/sign.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAI7D;AAkBD,8EAA8E;AAC9E,wBAAsB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAejF;AAED,0EAA0E;AAC1E,wBAAsB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGpF"}
|
package/dist/sign.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMAC-SHA256 signing for GateReport.
|
|
3
|
+
*
|
|
4
|
+
* The signing key comes from the `GATELANE_REPORT_SIGNING_KEY` env var (32+ random
|
|
5
|
+
* bytes recommended). The signed payload is the canonical JSON of the report
|
|
6
|
+
* (without the `signature` field); the signature is base64url-encoded HMAC.
|
|
7
|
+
*
|
|
8
|
+
* The same key reproduces the same signature, so auditors can verify
|
|
9
|
+
* "did this report actually get signed at decision time?" by recomputing.
|
|
10
|
+
*
|
|
11
|
+
* @see docs/prd.md §5.1 — The gate
|
|
12
|
+
*/
|
|
13
|
+
/** Stable JSON for signing (excludes `signature`; recursively sorts keys). */
|
|
14
|
+
export function canonicalizeReport(report) {
|
|
15
|
+
const { signature: _sig, ...rest } = report;
|
|
16
|
+
void _sig; // excluded from canonical JSON; keep destructure for clarity
|
|
17
|
+
return JSON.stringify(sortKeysDeep(rest));
|
|
18
|
+
}
|
|
19
|
+
/** Recursively sort object keys for canonical JSON. Arrays preserve order. */
|
|
20
|
+
function sortKeysDeep(value) {
|
|
21
|
+
if (Array.isArray(value))
|
|
22
|
+
return value.map(sortKeysDeep);
|
|
23
|
+
if (value && typeof value === 'object') {
|
|
24
|
+
const obj = value;
|
|
25
|
+
const out = {};
|
|
26
|
+
for (const key of Object.keys(obj).sort()) {
|
|
27
|
+
out[key] = sortKeysDeep(obj[key]);
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
const encoder = new TextEncoder();
|
|
34
|
+
/** Sign a GateReport with the given key. Returns the new signature string. */
|
|
35
|
+
export async function signReport(report, key) {
|
|
36
|
+
if (key.length < 16) {
|
|
37
|
+
throw new Error('signing key must be ≥ 16 chars; got ' + key.length);
|
|
38
|
+
}
|
|
39
|
+
const keyBytes = encoder.encode(key);
|
|
40
|
+
const payloadBytes = encoder.encode(canonicalizeReport(report));
|
|
41
|
+
const cryptoKey = await crypto.subtle.importKey('raw', keyBytes, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
42
|
+
const sig = await crypto.subtle.sign('HMAC', cryptoKey, payloadBytes);
|
|
43
|
+
return base64url(new Uint8Array(sig));
|
|
44
|
+
}
|
|
45
|
+
/** Verify a GateReport's signature. Returns true if signature matches. */
|
|
46
|
+
export async function verifyReport(report, key) {
|
|
47
|
+
const expected = await signReport(report, key);
|
|
48
|
+
return constantTimeEqual(expected, report.signature);
|
|
49
|
+
}
|
|
50
|
+
function base64url(bytes) {
|
|
51
|
+
let bin = '';
|
|
52
|
+
for (const b of bytes)
|
|
53
|
+
bin += String.fromCharCode(b);
|
|
54
|
+
return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
55
|
+
}
|
|
56
|
+
function constantTimeEqual(a, b) {
|
|
57
|
+
if (a.length !== b.length)
|
|
58
|
+
return false;
|
|
59
|
+
let diff = 0;
|
|
60
|
+
for (let i = 0; i < a.length; i++)
|
|
61
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
62
|
+
return diff === 0;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=sign.js.map
|
package/dist/sign.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign.js","sourceRoot":"","sources":["../src/sign.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTelemetry tracing utilities for the gate engine.
|
|
3
|
+
*
|
|
4
|
+
* Provides a simple span wrapper that emits spans for each gate stage.
|
|
5
|
+
* Uses console exporter in development, Cloudflare Logpush in production.
|
|
6
|
+
*
|
|
7
|
+
* @see docs/prd.md §5.1 — The gate
|
|
8
|
+
*/
|
|
9
|
+
import { trace, Span } from '@opentelemetry/api';
|
|
10
|
+
import type { SpanExporter } from '@opentelemetry/sdk-trace-base';
|
|
11
|
+
export declare function initTracing(serviceName?: string, exporter?: SpanExporter): void;
|
|
12
|
+
/** Get a tracer for the gate engine. */
|
|
13
|
+
export declare function getTracer(): ReturnType<typeof trace.getTracer>;
|
|
14
|
+
/** Gate stage names for span naming. */
|
|
15
|
+
export type GateStage = 'replay' | 'judge' | 'compare' | 'sign' | 'evaluate';
|
|
16
|
+
/** Run a function within a named span for a gate stage. */
|
|
17
|
+
export declare function withGateSpan<T>(stage: GateStage, gateRunId: string, fn: (span: Span) => Promise<T>): Promise<T>;
|
|
18
|
+
/** Shutdown the tracer provider (for clean shutdown). */
|
|
19
|
+
export declare function shutdownTracing(): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=tracing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,EAA4B,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAMlE,wBAAgB,WAAW,CAAC,WAAW,SAAoB,EAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAgB1F;AAED,wCAAwC;AACxC,wBAAgB,SAAS,IAAI,UAAU,CAAC,OAAO,KAAK,CAAC,SAAS,CAAC,CAE9D;AAED,wCAAwC;AACxC,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,CAAC;AAE7E,2DAA2D;AAC3D,wBAAsB,YAAY,CAAC,CAAC,EAClC,KAAK,EAAE,SAAS,EAChB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAC7B,OAAO,CAAC,CAAC,CAAC,CAoBZ;AAED,yDAAyD;AACzD,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAMrD"}
|
package/dist/tracing.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTelemetry tracing utilities for the gate engine.
|
|
3
|
+
*
|
|
4
|
+
* Provides a simple span wrapper that emits spans for each gate stage.
|
|
5
|
+
* Uses console exporter in development, Cloudflare Logpush in production.
|
|
6
|
+
*
|
|
7
|
+
* @see docs/prd.md §5.1 — The gate
|
|
8
|
+
*/
|
|
9
|
+
import { trace, SpanStatusCode, SpanKind } from '@opentelemetry/api';
|
|
10
|
+
import { NodeTracerProvider, ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node';
|
|
11
|
+
import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
12
|
+
let tracerProvider = null;
|
|
13
|
+
let initialized = false;
|
|
14
|
+
export function initTracing(serviceName = 'gatelane-engine', exporter) {
|
|
15
|
+
if (initialized)
|
|
16
|
+
return;
|
|
17
|
+
const spanProcessor = exporter
|
|
18
|
+
? new SimpleSpanProcessor(exporter)
|
|
19
|
+
: new SimpleSpanProcessor(new ConsoleSpanExporter());
|
|
20
|
+
tracerProvider = new NodeTracerProvider({
|
|
21
|
+
resource: resourceFromAttributes({
|
|
22
|
+
'service.name': serviceName,
|
|
23
|
+
}),
|
|
24
|
+
spanProcessors: [spanProcessor],
|
|
25
|
+
});
|
|
26
|
+
tracerProvider.register();
|
|
27
|
+
initialized = true;
|
|
28
|
+
}
|
|
29
|
+
/** Get a tracer for the gate engine. */
|
|
30
|
+
export function getTracer() {
|
|
31
|
+
return trace.getTracer('gatelane-engine');
|
|
32
|
+
}
|
|
33
|
+
/** Run a function within a named span for a gate stage. */
|
|
34
|
+
export async function withGateSpan(stage, gateRunId, fn) {
|
|
35
|
+
const tracer = getTracer();
|
|
36
|
+
return tracer.startActiveSpan(`gate.${stage}`, { kind: SpanKind.INTERNAL }, async (span) => {
|
|
37
|
+
try {
|
|
38
|
+
span.setAttribute('gate.run_id', gateRunId);
|
|
39
|
+
span.setAttribute('gate.stage', stage);
|
|
40
|
+
const result = await fn(span);
|
|
41
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
span.setStatus({
|
|
46
|
+
code: SpanStatusCode.ERROR,
|
|
47
|
+
message: error instanceof Error ? error.message : String(error),
|
|
48
|
+
});
|
|
49
|
+
span.recordException(error instanceof Error ? error : new Error(String(error)));
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
span.end();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/** Shutdown the tracer provider (for clean shutdown). */
|
|
58
|
+
export async function shutdownTracing() {
|
|
59
|
+
if (tracerProvider) {
|
|
60
|
+
await tracerProvider.shutdown();
|
|
61
|
+
initialized = false;
|
|
62
|
+
tracerProvider = null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=tracing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing.js","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAQ,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAE7G,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,IAAI,cAAc,GAA8B,IAAI,CAAC;AACrD,IAAI,WAAW,GAAG,KAAK,CAAC;AAExB,MAAM,UAAU,WAAW,CAAC,WAAW,GAAG,iBAAiB,EAAE,QAAuB;IAClF,IAAI,WAAW;QAAE,OAAO;IAExB,MAAM,aAAa,GAAG,QAAQ;QAC5B,CAAC,CAAC,IAAI,mBAAmB,CAAC,QAAQ,CAAC;QACnC,CAAC,CAAC,IAAI,mBAAmB,CAAC,IAAI,mBAAmB,EAAE,CAAC,CAAC;IAEvD,cAAc,GAAG,IAAI,kBAAkB,CAAC;QACtC,QAAQ,EAAE,sBAAsB,CAAC;YAC/B,cAAc,EAAE,WAAW;SAC5B,CAAC;QACF,cAAc,EAAE,CAAC,aAAa,CAAC;KAChC,CAAC,CAAC;IAEH,cAAc,CAAC,QAAQ,EAAE,CAAC;IAC1B,WAAW,GAAG,IAAI,CAAC;AACrB,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,SAAS;IACvB,OAAO,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;AAC5C,CAAC;AAKD,2DAA2D;AAC3D,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAgB,EAChB,SAAiB,EACjB,EAA8B;IAE9B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,OAAO,MAAM,CAAC,eAAe,CAAC,QAAQ,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACzF,IAAI,CAAC;YACH,IAAI,CAAC,YAAY,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;YAC5C,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;YACvC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,EAAE,CAAC,CAAC;YAC5C,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,SAAS,CAAC;gBACb,IAAI,EAAE,cAAc,CAAC,KAAK;gBAC1B,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAChE,CAAC,CAAC;YACH,IAAI,CAAC,eAAe,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAChF,MAAM,KAAK,CAAC;QACd,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,yDAAyD;AACzD,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,cAAc,CAAC,QAAQ,EAAE,CAAC;QAChC,WAAW,GAAG,KAAK,CAAC;QACpB,cAAc,GAAG,IAAI,CAAC;IACxB,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,91 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lanefoundry/gatelane-engine",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Gate engine — replay, compare, judge, sign. Runs in the Worker.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/lanefoundry/gatelane.git",
|
|
9
|
+
"directory": "packages/gatelane-engine"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/lanefoundry/gatelane/tree/main/packages/gatelane-engine",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./attack": {
|
|
21
|
+
"types": "./dist/attack.d.ts",
|
|
22
|
+
"import": "./dist/attack.js"
|
|
23
|
+
},
|
|
24
|
+
"./scan": {
|
|
25
|
+
"types": "./dist/scan.d.ts",
|
|
26
|
+
"import": "./dist/scan.js"
|
|
27
|
+
},
|
|
28
|
+
"./llm": {
|
|
29
|
+
"types": "./dist/llm.d.ts",
|
|
30
|
+
"import": "./dist/llm.js"
|
|
31
|
+
},
|
|
32
|
+
"./replay": {
|
|
33
|
+
"types": "./dist/replay.d.ts",
|
|
34
|
+
"import": "./dist/replay.js"
|
|
35
|
+
},
|
|
36
|
+
"./judge": {
|
|
37
|
+
"types": "./dist/judge.d.ts",
|
|
38
|
+
"import": "./dist/judge.js"
|
|
39
|
+
},
|
|
40
|
+
"./compare": {
|
|
41
|
+
"types": "./dist/compare.d.ts",
|
|
42
|
+
"import": "./dist/compare.js"
|
|
43
|
+
},
|
|
44
|
+
"./sign": {
|
|
45
|
+
"types": "./dist/sign.d.ts",
|
|
46
|
+
"import": "./dist/sign.js"
|
|
47
|
+
},
|
|
48
|
+
"./evaluate": {
|
|
49
|
+
"types": "./dist/evaluate.d.ts",
|
|
50
|
+
"import": "./dist/evaluate.js"
|
|
51
|
+
},
|
|
52
|
+
"./runner": {
|
|
53
|
+
"types": "./dist/runner.d.ts",
|
|
54
|
+
"import": "./dist/runner.js"
|
|
55
|
+
},
|
|
56
|
+
"./audit": {
|
|
57
|
+
"types": "./dist/audit.d.ts",
|
|
58
|
+
"import": "./dist/audit.js"
|
|
59
|
+
},
|
|
60
|
+
"./tracing": {
|
|
61
|
+
"types": "./dist/tracing.d.ts",
|
|
62
|
+
"import": "./dist/tracing.js"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"files": [
|
|
66
|
+
"dist"
|
|
67
|
+
],
|
|
68
|
+
"publishConfig": {
|
|
69
|
+
"access": "public"
|
|
70
|
+
},
|
|
71
|
+
"scripts": {
|
|
72
|
+
"build": "tsc -p tsconfig.json",
|
|
73
|
+
"test": "vitest run"
|
|
74
|
+
},
|
|
75
|
+
"dependencies": {
|
|
76
|
+
"@lanefoundry/gatelane-sdk": "0.1.0",
|
|
77
|
+
"@opentelemetry/api": "^1.9.1",
|
|
78
|
+
"@opentelemetry/exporter-collector-grpc": "^0.25.0",
|
|
79
|
+
"@opentelemetry/exporter-trace-otlp-grpc": "^0.221.0",
|
|
80
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.222.0",
|
|
81
|
+
"@opentelemetry/resources": "^2.10.0",
|
|
82
|
+
"@opentelemetry/sdk-trace-base": "^2.10.0",
|
|
83
|
+
"@opentelemetry/sdk-trace-node": "^2.10.0",
|
|
84
|
+
"@opentelemetry/semantic-conventions": "^1.43.0"
|
|
85
|
+
},
|
|
86
|
+
"devDependencies": {
|
|
87
|
+
"@types/node": "^22.0.0",
|
|
88
|
+
"typescript": "^5.5.4",
|
|
89
|
+
"vitest": "^2.0.5"
|
|
90
|
+
}
|
|
91
|
+
}
|
package/README.md
DELETED