agentcheck-sdk 1.0.0 → 2.0.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/cache.d.ts +78 -0
- package/dist/cache.js +155 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +24 -1
- package/dist/integrations/autogen.d.ts +50 -0
- package/dist/integrations/autogen.js +93 -0
- package/dist/integrations/crewai.d.ts +49 -0
- package/dist/integrations/crewai.js +107 -0
- package/dist/integrations/index.d.ts +8 -0
- package/dist/integrations/index.js +17 -0
- package/dist/integrations/langchain.d.ts +63 -0
- package/dist/integrations/langchain.js +104 -0
- package/dist/pipeline.d.ts +24 -0
- package/dist/pipeline.js +84 -48
- package/dist/pqc/dsse.d.ts +47 -0
- package/dist/pqc/dsse.js +113 -0
- package/dist/pqc/index.d.ts +11 -0
- package/dist/pqc/index.js +18 -0
- package/dist/pqc/signer.d.ts +44 -0
- package/dist/pqc/signer.js +97 -0
- package/dist/pqc/verifier.d.ts +49 -0
- package/dist/pqc/verifier.js +93 -0
- package/dist/router.d.ts +78 -0
- package/dist/router.js +102 -0
- package/dist/safety.d.ts +66 -7
- package/dist/safety.js +89 -3
- package/dist/scope-engine.js +2 -2
- package/dist/semantic.d.ts +14 -2
- package/dist/semantic.js +22 -10
- package/dist/templates.d.ts +130 -1
- package/dist/templates.js +275 -12
- package/dist/trust.d.ts +97 -0
- package/dist/trust.js +146 -0
- package/package.json +2 -2
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LangChain integration for AgentCheck.
|
|
3
|
+
*
|
|
4
|
+
* Wraps LangChain tools with delegation checking and provides a callback
|
|
5
|
+
* handler that logs all tool calls to the AgentCheck audit trail.
|
|
6
|
+
*
|
|
7
|
+
* Requires: npm install langchain @langchain/core
|
|
8
|
+
*
|
|
9
|
+
* Tier: Molecule (be-mol-agentcheck-langchain-ts) - composes Client + LangChain.
|
|
10
|
+
*/
|
|
11
|
+
import type { AgentCheckClient } from "../client";
|
|
12
|
+
/** Minimal BaseTool interface - duck-typed to avoid a hard langchain-core dependency. */
|
|
13
|
+
interface LangChainTool {
|
|
14
|
+
name: string;
|
|
15
|
+
_run?: (...args: unknown[]) => unknown | Promise<unknown>;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
/** Minimal BaseCallbackHandler shape from langchain-core. */
|
|
19
|
+
interface LangChainCallbackHandlerBase {
|
|
20
|
+
on_tool_start?: (serialized: Record<string, unknown>, inputStr: string, ...args: unknown[]) => void | Promise<void>;
|
|
21
|
+
on_tool_end?: (output: string, ...args: unknown[]) => void | Promise<void>;
|
|
22
|
+
on_tool_error?: (error: Error, ...args: unknown[]) => void | Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export declare class AgentCheckToolkit {
|
|
25
|
+
private readonly client;
|
|
26
|
+
private readonly delegationId;
|
|
27
|
+
/**
|
|
28
|
+
* @param client AgentCheck Client used to verify delegations.
|
|
29
|
+
* @param delegationId The agreement ID that authorizes tool usage.
|
|
30
|
+
*/
|
|
31
|
+
constructor(client: AgentCheckClient, delegationId: string);
|
|
32
|
+
/**
|
|
33
|
+
* Wrap a single LangChain tool with a delegation guard.
|
|
34
|
+
*
|
|
35
|
+
* Replaces the tool's _run method so that the delegation is checked
|
|
36
|
+
* before every invocation.
|
|
37
|
+
*
|
|
38
|
+
* @param tool LangChain BaseTool instance.
|
|
39
|
+
* @returns The same tool with _run replaced.
|
|
40
|
+
*/
|
|
41
|
+
wrapTool<T extends LangChainTool>(tool: T): T;
|
|
42
|
+
/**
|
|
43
|
+
* Wrap multiple tools with delegation guards.
|
|
44
|
+
*
|
|
45
|
+
* @param tools Array of LangChain BaseTool instances.
|
|
46
|
+
* @returns Array of wrapped tools.
|
|
47
|
+
*/
|
|
48
|
+
wrapTools<T extends LangChainTool>(tools: T[]): T[];
|
|
49
|
+
}
|
|
50
|
+
export declare class AgentCheckCallbackHandler implements LangChainCallbackHandlerBase {
|
|
51
|
+
private readonly client;
|
|
52
|
+
private readonly delegationId;
|
|
53
|
+
/**
|
|
54
|
+
* @param client AgentCheck Client used for logging executions.
|
|
55
|
+
* @param delegationId The agreement ID to associate tool calls with.
|
|
56
|
+
*/
|
|
57
|
+
constructor(client: AgentCheckClient, delegationId: string);
|
|
58
|
+
on_tool_start(serialized: Record<string, unknown>, inputStr: string): Promise<void>;
|
|
59
|
+
on_tool_end(output: string, ...args: unknown[]): Promise<void>;
|
|
60
|
+
on_tool_error(error: Error, ...args: unknown[]): Promise<void>;
|
|
61
|
+
private logExecution;
|
|
62
|
+
}
|
|
63
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* LangChain integration for AgentCheck.
|
|
4
|
+
*
|
|
5
|
+
* Wraps LangChain tools with delegation checking and provides a callback
|
|
6
|
+
* handler that logs all tool calls to the AgentCheck audit trail.
|
|
7
|
+
*
|
|
8
|
+
* Requires: npm install langchain @langchain/core
|
|
9
|
+
*
|
|
10
|
+
* Tier: Molecule (be-mol-agentcheck-langchain-ts) - composes Client + LangChain.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.AgentCheckCallbackHandler = exports.AgentCheckToolkit = void 0;
|
|
14
|
+
class AgentCheckToolkit {
|
|
15
|
+
/**
|
|
16
|
+
* @param client AgentCheck Client used to verify delegations.
|
|
17
|
+
* @param delegationId The agreement ID that authorizes tool usage.
|
|
18
|
+
*/
|
|
19
|
+
constructor(client, delegationId) {
|
|
20
|
+
this.client = client;
|
|
21
|
+
this.delegationId = delegationId;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Wrap a single LangChain tool with a delegation guard.
|
|
25
|
+
*
|
|
26
|
+
* Replaces the tool's _run method so that the delegation is checked
|
|
27
|
+
* before every invocation.
|
|
28
|
+
*
|
|
29
|
+
* @param tool LangChain BaseTool instance.
|
|
30
|
+
* @returns The same tool with _run replaced.
|
|
31
|
+
*/
|
|
32
|
+
wrapTool(tool) {
|
|
33
|
+
const originalRun = tool._run?.bind(tool);
|
|
34
|
+
if (!originalRun)
|
|
35
|
+
return tool;
|
|
36
|
+
const client = this.client;
|
|
37
|
+
const delegationId = this.delegationId;
|
|
38
|
+
tool._run = async (...args) => {
|
|
39
|
+
await checkDelegation(client, delegationId);
|
|
40
|
+
return originalRun(...args);
|
|
41
|
+
};
|
|
42
|
+
return tool;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Wrap multiple tools with delegation guards.
|
|
46
|
+
*
|
|
47
|
+
* @param tools Array of LangChain BaseTool instances.
|
|
48
|
+
* @returns Array of wrapped tools.
|
|
49
|
+
*/
|
|
50
|
+
wrapTools(tools) {
|
|
51
|
+
return tools.map((t) => this.wrapTool(t));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.AgentCheckToolkit = AgentCheckToolkit;
|
|
55
|
+
class AgentCheckCallbackHandler {
|
|
56
|
+
/**
|
|
57
|
+
* @param client AgentCheck Client used for logging executions.
|
|
58
|
+
* @param delegationId The agreement ID to associate tool calls with.
|
|
59
|
+
*/
|
|
60
|
+
constructor(client, delegationId) {
|
|
61
|
+
this.client = client;
|
|
62
|
+
this.delegationId = delegationId;
|
|
63
|
+
}
|
|
64
|
+
async on_tool_start(serialized, inputStr) {
|
|
65
|
+
const toolName = serialized.name ?? "unknown_tool";
|
|
66
|
+
await this.logExecution(toolName, "started", { input: inputStr });
|
|
67
|
+
}
|
|
68
|
+
async on_tool_end(output, ...args) {
|
|
69
|
+
const kwargs = args[0] ?? {};
|
|
70
|
+
const toolName = kwargs.name ?? "unknown_tool";
|
|
71
|
+
await this.logExecution(toolName, "success", {
|
|
72
|
+
output: String(output).slice(0, 200),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
async on_tool_error(error, ...args) {
|
|
76
|
+
const kwargs = args[0] ?? {};
|
|
77
|
+
const toolName = kwargs.name ?? "unknown_tool";
|
|
78
|
+
await this.logExecution(toolName, "error", { error: error.message });
|
|
79
|
+
}
|
|
80
|
+
async logExecution(toolName, result, metadata) {
|
|
81
|
+
try {
|
|
82
|
+
await this.client.request("POST", "/api/v1/executions", {
|
|
83
|
+
agent: "langchain",
|
|
84
|
+
action: toolName,
|
|
85
|
+
agreement_id: this.delegationId,
|
|
86
|
+
result,
|
|
87
|
+
metadata,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Best-effort logging - do not propagate errors
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.AgentCheckCallbackHandler = AgentCheckCallbackHandler;
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// Internal helpers
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
async function checkDelegation(client, delegationId) {
|
|
100
|
+
const agreement = await client.get(delegationId);
|
|
101
|
+
if (agreement.status !== "approved") {
|
|
102
|
+
throw new Error(`Delegation ${delegationId} is not approved (status=${agreement.status})`);
|
|
103
|
+
}
|
|
104
|
+
}
|
package/dist/pipeline.d.ts
CHANGED
|
@@ -3,10 +3,18 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Connects all modules into a single chain:
|
|
5
5
|
* delegation check -> scope -> semantic(LLM) -> budget -> pattern -> human -> execute -> log
|
|
6
|
+
*
|
|
7
|
+
* Phase 3 addition: VerificationPipeline accepts optional TrustEngine and
|
|
8
|
+
* LayerRouter. When provided, each verify() call consults the agent's trust
|
|
9
|
+
* score via the router to decide which layers to run. After execution the
|
|
10
|
+
* outcome is recorded back to TrustEngine. When omitted the original
|
|
11
|
+
* fixed-order behaviour is preserved (backwards compatible).
|
|
6
12
|
*/
|
|
7
13
|
import { AgentCheckClient } from "./client";
|
|
8
14
|
import { BudgetTracker, HumanEscalation, PatternMonitor } from "./safety";
|
|
9
15
|
import { LLMProvider } from "./semantic";
|
|
16
|
+
import { TrustEngine } from "./trust";
|
|
17
|
+
import { LayerRouter } from "./router";
|
|
10
18
|
export interface CheckResult {
|
|
11
19
|
layer: string;
|
|
12
20
|
passed: boolean;
|
|
@@ -28,22 +36,38 @@ export interface PipelineConfig {
|
|
|
28
36
|
pattern?: PatternMonitor;
|
|
29
37
|
escalation?: HumanEscalation;
|
|
30
38
|
semanticThreshold?: number;
|
|
39
|
+
/** Optional trust engine for agent trust tracking (Phase 3). */
|
|
40
|
+
trustEngine?: TrustEngine;
|
|
41
|
+
/** Optional router for dynamic layer selection (Phase 3). */
|
|
42
|
+
router?: LayerRouter;
|
|
31
43
|
}
|
|
32
44
|
export declare class VerificationPipeline {
|
|
45
|
+
/**
|
|
46
|
+
* Complete delegation verification pipeline.
|
|
47
|
+
*
|
|
48
|
+
* Phase 3: When trustEngine and router are supplied, only the layers
|
|
49
|
+
* selected by the router for the agent's current trust tier are executed.
|
|
50
|
+
* After each verifyAndExecute() call the outcome is recorded back to
|
|
51
|
+
* the TrustEngine so trust scores evolve over time.
|
|
52
|
+
*/
|
|
33
53
|
private client;
|
|
34
54
|
private scopeEngine;
|
|
35
55
|
private semantic?;
|
|
36
56
|
private budget?;
|
|
37
57
|
private pattern;
|
|
38
58
|
private escalation?;
|
|
59
|
+
private trustEngine?;
|
|
60
|
+
private router?;
|
|
39
61
|
constructor(client: AgentCheckClient, config?: PipelineConfig);
|
|
40
62
|
verify(agent: string, action: string, opts?: {
|
|
41
63
|
amount?: number;
|
|
42
64
|
context?: Record<string, unknown>;
|
|
65
|
+
riskLevel?: string;
|
|
43
66
|
}): Promise<PipelineResult>;
|
|
44
67
|
verifyAndExecute<T>(agent: string, action: string, executeFn: () => Promise<T>, opts?: {
|
|
45
68
|
amount?: number;
|
|
46
69
|
context?: Record<string, unknown>;
|
|
70
|
+
riskLevel?: string;
|
|
47
71
|
}): Promise<PipelineResult>;
|
|
48
72
|
private logExecution;
|
|
49
73
|
}
|
package/dist/pipeline.js
CHANGED
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Connects all modules into a single chain:
|
|
6
6
|
* delegation check -> scope -> semantic(LLM) -> budget -> pattern -> human -> execute -> log
|
|
7
|
+
*
|
|
8
|
+
* Phase 3 addition: VerificationPipeline accepts optional TrustEngine and
|
|
9
|
+
* LayerRouter. When provided, each verify() call consults the agent's trust
|
|
10
|
+
* score via the router to decide which layers to run. After execution the
|
|
11
|
+
* outcome is recorded back to TrustEngine. When omitted the original
|
|
12
|
+
* fixed-order behaviour is preserved (backwards compatible).
|
|
7
13
|
*/
|
|
8
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
15
|
exports.VerificationPipeline = void 0;
|
|
@@ -18,11 +24,13 @@ class VerificationPipeline {
|
|
|
18
24
|
this.budget = config.budget;
|
|
19
25
|
this.pattern = config.pattern || new safety_1.PatternMonitor();
|
|
20
26
|
this.escalation = config.escalation;
|
|
27
|
+
this.trustEngine = config.trustEngine;
|
|
28
|
+
this.router = config.router;
|
|
21
29
|
}
|
|
22
30
|
async verify(agent, action, opts = {}) {
|
|
23
31
|
const checks = [];
|
|
24
32
|
const amount = opts.amount || 0;
|
|
25
|
-
// Layer 1: Delegation exists
|
|
33
|
+
// Layer 1: Delegation exists on AgentCheck server
|
|
26
34
|
let agreement;
|
|
27
35
|
let t = Date.now();
|
|
28
36
|
try {
|
|
@@ -38,57 +46,81 @@ class VerificationPipeline {
|
|
|
38
46
|
checks.push({ layer: "delegation_check", passed: false, reason: `Server error: ${e.message}`, durationMs: Date.now() - t });
|
|
39
47
|
return { allowed: false, executed: false, action, agent, checks, error: e.message };
|
|
40
48
|
}
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (!scopeResult.allowed) {
|
|
47
|
-
return { allowed: false, executed: false, action, agent, checks, error: scopeResult.reason };
|
|
49
|
+
// Determine which local layers to run
|
|
50
|
+
let activeLayers;
|
|
51
|
+
if (this.trustEngine && this.router) {
|
|
52
|
+
const trust = this.trustEngine.getScore(agent);
|
|
53
|
+
activeLayers = this.router.route(trust, action, { amount, riskLevel: opts.riskLevel });
|
|
48
54
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
else {
|
|
56
|
+
// Fixed order: run every configured layer (original behaviour)
|
|
57
|
+
activeLayers = ["scope_engine"];
|
|
58
|
+
if (this.semantic)
|
|
59
|
+
activeLayers.push("semantic_verifier");
|
|
60
|
+
if (this.budget)
|
|
61
|
+
activeLayers.push("budget_tracker");
|
|
62
|
+
activeLayers.push("pattern_monitor");
|
|
63
|
+
if (this.escalation)
|
|
64
|
+
activeLayers.push("human_escalation");
|
|
65
|
+
}
|
|
66
|
+
const scope = this.scopeEngine.parse(agreement.scope);
|
|
67
|
+
for (const layerName of activeLayers) {
|
|
68
|
+
if (layerName === "scope_engine") {
|
|
69
|
+
t = Date.now();
|
|
70
|
+
const r = this.scopeEngine.verify(scope, action, { amount });
|
|
71
|
+
checks.push({ layer: "scope_engine", passed: r.allowed, reason: r.reason, durationMs: Date.now() - t });
|
|
72
|
+
if (!r.allowed)
|
|
73
|
+
return { allowed: false, executed: false, action, agent, checks, error: r.reason };
|
|
62
74
|
}
|
|
63
|
-
if (
|
|
64
|
-
|
|
75
|
+
else if (layerName === "semantic_verifier") {
|
|
76
|
+
if (this.semantic && typeof scope === "string") {
|
|
77
|
+
t = Date.now();
|
|
78
|
+
const sem = await this.semantic.verify(agreement.scope, action, opts.context || { amount });
|
|
79
|
+
const isOk = sem.assessment !== "denied";
|
|
80
|
+
checks.push({
|
|
81
|
+
layer: "semantic_verifier",
|
|
82
|
+
passed: isOk,
|
|
83
|
+
reason: `[${sem.assessment}] ${sem.reasoning} (confidence: ${(sem.confidence * 100).toFixed(0)}%)`,
|
|
84
|
+
durationMs: Date.now() - t,
|
|
85
|
+
});
|
|
86
|
+
if (sem.assessment === "denied") {
|
|
87
|
+
return { allowed: false, executed: false, action, agent, checks, error: sem.reasoning };
|
|
88
|
+
}
|
|
89
|
+
if (sem.assessment === "suspicious") {
|
|
90
|
+
checks.push({ layer: "semantic_flag", passed: true, reason: "LLM flagged suspicious - recommend human review", durationMs: 0 });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
65
93
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
94
|
+
else if (layerName === "budget_tracker") {
|
|
95
|
+
if (this.budget) {
|
|
96
|
+
t = Date.now();
|
|
97
|
+
const br = this.budget.check(action, amount);
|
|
98
|
+
checks.push({ layer: "budget_tracker", passed: br.allowed, reason: br.reason, durationMs: Date.now() - t });
|
|
99
|
+
if (!br.allowed)
|
|
100
|
+
return { allowed: false, executed: false, action, agent, checks, error: br.reason };
|
|
101
|
+
}
|
|
74
102
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
103
|
+
else if (layerName === "pattern_monitor") {
|
|
104
|
+
t = Date.now();
|
|
105
|
+
const alerts = this.pattern.check(action, amount);
|
|
106
|
+
const hasCritical = alerts.some(a => a.level === "critical");
|
|
107
|
+
const patternMsg = alerts.length ? alerts.map(a => a.message).join("; ") : "Normal pattern";
|
|
108
|
+
checks.push({ layer: "pattern_monitor", passed: !hasCritical, reason: patternMsg, durationMs: Date.now() - t });
|
|
109
|
+
if (hasCritical) {
|
|
110
|
+
return { allowed: false, executed: false, action, agent, checks, error: alerts[0].message };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else if (layerName === "human_escalation") {
|
|
114
|
+
if (this.escalation) {
|
|
115
|
+
t = Date.now();
|
|
116
|
+
const er = this.escalation.check(action, amount);
|
|
117
|
+
checks.push({ layer: "human_escalation", passed: er.allowed, reason: er.reason, durationMs: Date.now() - t });
|
|
118
|
+
if (!er.allowed)
|
|
119
|
+
return { allowed: false, executed: false, action, agent, checks, error: er.reason };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else if (layerName === "pqc_signing") {
|
|
123
|
+
checks.push({ layer: "pqc_signing", passed: true, reason: `PQC signing required (amount=${amount} > threshold)`, durationMs: 0 });
|
|
92
124
|
}
|
|
93
125
|
}
|
|
94
126
|
return { allowed: true, executed: false, action, agent, checks };
|
|
@@ -96,6 +128,8 @@ class VerificationPipeline {
|
|
|
96
128
|
async verifyAndExecute(agent, action, executeFn, opts = {}) {
|
|
97
129
|
const result = await this.verify(agent, action, opts);
|
|
98
130
|
if (!result.allowed) {
|
|
131
|
+
// Record failed attempt to trust engine
|
|
132
|
+
this.trustEngine?.recordOutcome(agent, false);
|
|
99
133
|
this.logExecution(agent, action, opts.amount || 0, "blocked", result.error);
|
|
100
134
|
return result;
|
|
101
135
|
}
|
|
@@ -111,6 +145,8 @@ class VerificationPipeline {
|
|
|
111
145
|
}
|
|
112
146
|
this.budget?.recordUsage(action, opts.amount || 0);
|
|
113
147
|
this.pattern.record(action, opts.amount || 0);
|
|
148
|
+
// Record outcome to trust engine
|
|
149
|
+
this.trustEngine?.recordOutcome(agent, result.executed);
|
|
114
150
|
this.logExecution(agent, action, opts.amount || 0, result.executed ? "success" : "failed", result.error);
|
|
115
151
|
return result;
|
|
116
152
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSSE (Dead Simple Signing Envelope) structures for AgentCheck PQC integration.
|
|
3
|
+
*
|
|
4
|
+
* Implements the DSSE envelope format per https://github.com/secure-systems-lab/dsse
|
|
5
|
+
* with Pre-Authentication Encoding (PAE) for ML-DSA-87 / HMAC-SHA256 signatures.
|
|
6
|
+
*
|
|
7
|
+
* Tier: Atom (be-atom-agentcheck-dsse-ts) - single responsibility: envelope data model.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DSSE_PAYLOAD_TYPE = "application/vnd.agentcheck.audit+json";
|
|
10
|
+
export interface DsseSignatureData {
|
|
11
|
+
keyid: string;
|
|
12
|
+
sig: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class DsseSignature {
|
|
15
|
+
readonly keyid: string;
|
|
16
|
+
readonly sig: string;
|
|
17
|
+
constructor(keyid: string, sig: string);
|
|
18
|
+
/** Decode signature from base64 to Uint8Array. */
|
|
19
|
+
sigBytes(): Uint8Array;
|
|
20
|
+
toDict(): DsseSignatureData;
|
|
21
|
+
static fromDict(d: DsseSignatureData): DsseSignature;
|
|
22
|
+
}
|
|
23
|
+
export interface DsseEnvelopeData {
|
|
24
|
+
payloadType: string;
|
|
25
|
+
payload: string;
|
|
26
|
+
signatures: DsseSignatureData[];
|
|
27
|
+
}
|
|
28
|
+
export declare class DsseEnvelope {
|
|
29
|
+
readonly payloadType: string;
|
|
30
|
+
readonly payload: string;
|
|
31
|
+
readonly signatures: DsseSignature[];
|
|
32
|
+
constructor(payloadType: string, payload: string, signatures?: DsseSignature[]);
|
|
33
|
+
/** Decode base64 payload to object. */
|
|
34
|
+
decodePayload(): Record<string, unknown>;
|
|
35
|
+
/**
|
|
36
|
+
* Pre-Authentication Encoding per DSSE spec.
|
|
37
|
+
*
|
|
38
|
+
* Format: DSSEv1 SP len(payloadType) SP payloadType SP len(payload) SP payload
|
|
39
|
+
*/
|
|
40
|
+
pae(): Uint8Array;
|
|
41
|
+
toDict(): DsseEnvelopeData;
|
|
42
|
+
static fromDict(d: DsseEnvelopeData): DsseEnvelope;
|
|
43
|
+
/** Create a new unsigned DSSE envelope from a payload object. */
|
|
44
|
+
static create(payload: Record<string, unknown>, payloadType?: string): DsseEnvelope;
|
|
45
|
+
/** Return a new envelope with an additional signature appended. */
|
|
46
|
+
withSignature(sig: DsseSignature): DsseEnvelope;
|
|
47
|
+
}
|
package/dist/pqc/dsse.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* DSSE (Dead Simple Signing Envelope) structures for AgentCheck PQC integration.
|
|
4
|
+
*
|
|
5
|
+
* Implements the DSSE envelope format per https://github.com/secure-systems-lab/dsse
|
|
6
|
+
* with Pre-Authentication Encoding (PAE) for ML-DSA-87 / HMAC-SHA256 signatures.
|
|
7
|
+
*
|
|
8
|
+
* Tier: Atom (be-atom-agentcheck-dsse-ts) - single responsibility: envelope data model.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.DsseEnvelope = exports.DsseSignature = exports.DSSE_PAYLOAD_TYPE = void 0;
|
|
12
|
+
exports.DSSE_PAYLOAD_TYPE = "application/vnd.agentcheck.audit+json";
|
|
13
|
+
class DsseSignature {
|
|
14
|
+
constructor(keyid, sig) {
|
|
15
|
+
this.keyid = keyid;
|
|
16
|
+
this.sig = sig;
|
|
17
|
+
}
|
|
18
|
+
/** Decode signature from base64 to Uint8Array. */
|
|
19
|
+
sigBytes() {
|
|
20
|
+
return base64Decode(this.sig);
|
|
21
|
+
}
|
|
22
|
+
toDict() {
|
|
23
|
+
return { keyid: this.keyid, sig: this.sig };
|
|
24
|
+
}
|
|
25
|
+
static fromDict(d) {
|
|
26
|
+
return new DsseSignature(d.keyid, d.sig);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.DsseSignature = DsseSignature;
|
|
30
|
+
class DsseEnvelope {
|
|
31
|
+
constructor(payloadType, payload, signatures = []) {
|
|
32
|
+
this.payloadType = payloadType;
|
|
33
|
+
this.payload = payload;
|
|
34
|
+
this.signatures = signatures;
|
|
35
|
+
}
|
|
36
|
+
/** Decode base64 payload to object. */
|
|
37
|
+
decodePayload() {
|
|
38
|
+
const raw = base64Decode(this.payload);
|
|
39
|
+
const json = new TextDecoder().decode(raw);
|
|
40
|
+
return JSON.parse(json);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Pre-Authentication Encoding per DSSE spec.
|
|
44
|
+
*
|
|
45
|
+
* Format: DSSEv1 SP len(payloadType) SP payloadType SP len(payload) SP payload
|
|
46
|
+
*/
|
|
47
|
+
pae() {
|
|
48
|
+
const enc = new TextEncoder();
|
|
49
|
+
const payloadTypeBytes = enc.encode(this.payloadType);
|
|
50
|
+
const payloadBytes = base64Decode(this.payload);
|
|
51
|
+
const prefix = enc.encode(`DSSEv1 ${payloadTypeBytes.length} `);
|
|
52
|
+
const middle = enc.encode(` ${payloadBytes.length} `);
|
|
53
|
+
const total = prefix.length +
|
|
54
|
+
payloadTypeBytes.length +
|
|
55
|
+
middle.length +
|
|
56
|
+
payloadBytes.length;
|
|
57
|
+
const result = new Uint8Array(total);
|
|
58
|
+
let offset = 0;
|
|
59
|
+
result.set(prefix, offset);
|
|
60
|
+
offset += prefix.length;
|
|
61
|
+
result.set(payloadTypeBytes, offset);
|
|
62
|
+
offset += payloadTypeBytes.length;
|
|
63
|
+
result.set(middle, offset);
|
|
64
|
+
offset += middle.length;
|
|
65
|
+
result.set(payloadBytes, offset);
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
toDict() {
|
|
69
|
+
return {
|
|
70
|
+
payloadType: this.payloadType,
|
|
71
|
+
payload: this.payload,
|
|
72
|
+
signatures: this.signatures.map((s) => s.toDict()),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
static fromDict(d) {
|
|
76
|
+
const sigs = (d.signatures ?? []).map(DsseSignature.fromDict);
|
|
77
|
+
return new DsseEnvelope(d.payloadType ?? exports.DSSE_PAYLOAD_TYPE, d.payload, sigs);
|
|
78
|
+
}
|
|
79
|
+
/** Create a new unsigned DSSE envelope from a payload object. */
|
|
80
|
+
static create(payload, payloadType = exports.DSSE_PAYLOAD_TYPE) {
|
|
81
|
+
const json = JSON.stringify(payload);
|
|
82
|
+
const payloadB64 = base64Encode(new TextEncoder().encode(json));
|
|
83
|
+
return new DsseEnvelope(payloadType, payloadB64, []);
|
|
84
|
+
}
|
|
85
|
+
/** Return a new envelope with an additional signature appended. */
|
|
86
|
+
withSignature(sig) {
|
|
87
|
+
return new DsseEnvelope(this.payloadType, this.payload, [
|
|
88
|
+
...this.signatures,
|
|
89
|
+
sig,
|
|
90
|
+
]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.DsseEnvelope = DsseEnvelope;
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Base64 utilities (Node.js + browser compatible)
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
function base64Encode(bytes) {
|
|
98
|
+
if (typeof Buffer !== "undefined") {
|
|
99
|
+
return Buffer.from(bytes).toString("base64");
|
|
100
|
+
}
|
|
101
|
+
return btoa(String.fromCharCode(...bytes));
|
|
102
|
+
}
|
|
103
|
+
function base64Decode(b64) {
|
|
104
|
+
if (typeof Buffer !== "undefined") {
|
|
105
|
+
return new Uint8Array(Buffer.from(b64, "base64"));
|
|
106
|
+
}
|
|
107
|
+
const bin = atob(b64);
|
|
108
|
+
const bytes = new Uint8Array(bin.length);
|
|
109
|
+
for (let i = 0; i < bin.length; i++) {
|
|
110
|
+
bytes[i] = bin.charCodeAt(i);
|
|
111
|
+
}
|
|
112
|
+
return bytes;
|
|
113
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentCheck PQC (Post-Quantum Cryptography) integration module.
|
|
3
|
+
*
|
|
4
|
+
* Provides DSSE envelope structures and ML-DSA-87 signature verification/signing.
|
|
5
|
+
* HMAC-SHA256 operations use the Web Crypto API (Node 18+, all modern browsers).
|
|
6
|
+
* ML-DSA-87 verification/signing is delegated to the fides-rs server.
|
|
7
|
+
*/
|
|
8
|
+
export { DSSE_PAYLOAD_TYPE, DsseEnvelope, DsseSignature } from "./dsse";
|
|
9
|
+
export type { DsseEnvelopeData, DsseSignatureData } from "./dsse";
|
|
10
|
+
export { PqcVerifier } from "./verifier";
|
|
11
|
+
export { PqcSigner } from "./signer";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* AgentCheck PQC (Post-Quantum Cryptography) integration module.
|
|
4
|
+
*
|
|
5
|
+
* Provides DSSE envelope structures and ML-DSA-87 signature verification/signing.
|
|
6
|
+
* HMAC-SHA256 operations use the Web Crypto API (Node 18+, all modern browsers).
|
|
7
|
+
* ML-DSA-87 verification/signing is delegated to the fides-rs server.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.PqcSigner = exports.PqcVerifier = exports.DsseSignature = exports.DsseEnvelope = exports.DSSE_PAYLOAD_TYPE = void 0;
|
|
11
|
+
var dsse_1 = require("./dsse");
|
|
12
|
+
Object.defineProperty(exports, "DSSE_PAYLOAD_TYPE", { enumerable: true, get: function () { return dsse_1.DSSE_PAYLOAD_TYPE; } });
|
|
13
|
+
Object.defineProperty(exports, "DsseEnvelope", { enumerable: true, get: function () { return dsse_1.DsseEnvelope; } });
|
|
14
|
+
Object.defineProperty(exports, "DsseSignature", { enumerable: true, get: function () { return dsse_1.DsseSignature; } });
|
|
15
|
+
var verifier_1 = require("./verifier");
|
|
16
|
+
Object.defineProperty(exports, "PqcVerifier", { enumerable: true, get: function () { return verifier_1.PqcVerifier; } });
|
|
17
|
+
var signer_1 = require("./signer");
|
|
18
|
+
Object.defineProperty(exports, "PqcSigner", { enumerable: true, get: function () { return signer_1.PqcSigner; } });
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PQC signer for DSSE envelopes.
|
|
3
|
+
*
|
|
4
|
+
* Signs audit records with HMAC-SHA256 locally via Web Crypto API.
|
|
5
|
+
* ML-DSA-87 signing is delegated to the fides-rs server so that
|
|
6
|
+
* private keys never exist in JavaScript memory.
|
|
7
|
+
*
|
|
8
|
+
* Tier: Atom (be-atom-agentcheck-pqc-signer-ts) - single responsibility: signing.
|
|
9
|
+
*/
|
|
10
|
+
import { DsseEnvelope } from "./dsse";
|
|
11
|
+
export declare class PqcSigner {
|
|
12
|
+
private readonly hmacKey;
|
|
13
|
+
private readonly serverUrl;
|
|
14
|
+
private readonly apiKey;
|
|
15
|
+
/**
|
|
16
|
+
* @param hmacKey Optional HMAC-SHA256 secret for local signing.
|
|
17
|
+
* @param serverUrl Optional fides-rs base URL for ML-DSA-87 server-side signing.
|
|
18
|
+
* @param apiKey Optional API key for authenticating with the signing server.
|
|
19
|
+
*/
|
|
20
|
+
constructor(opts?: {
|
|
21
|
+
hmacKey?: string;
|
|
22
|
+
serverUrl?: string;
|
|
23
|
+
apiKey?: string;
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* Create a DSSE envelope with HMAC-SHA256 signature.
|
|
27
|
+
*
|
|
28
|
+
* If no hmacKey is configured, the envelope is returned unsigned.
|
|
29
|
+
*
|
|
30
|
+
* @param payload Object to sign and encode into the envelope.
|
|
31
|
+
* @param keyId Key identifier to embed in the signature.
|
|
32
|
+
* @returns Signed DsseEnvelope (or unsigned if no key).
|
|
33
|
+
*/
|
|
34
|
+
signHmac(payload: Record<string, unknown>, keyId?: string): Promise<DsseEnvelope>;
|
|
35
|
+
/**
|
|
36
|
+
* Request ML-DSA-87 signing from the fides-rs server.
|
|
37
|
+
*
|
|
38
|
+
* Returns null if the server is unavailable or not configured.
|
|
39
|
+
*
|
|
40
|
+
* @param payload Object to sign with ML-DSA-87 on the server.
|
|
41
|
+
* @returns Signed DsseEnvelope, or null if server signing is unavailable.
|
|
42
|
+
*/
|
|
43
|
+
signPqc(payload: Record<string, unknown>): Promise<DsseEnvelope | null>;
|
|
44
|
+
}
|