@attested-intelligence/aga-mcp-server 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/AGA_MCP_SERVER_SPEC.md +632 -0
- package/LICENSE +21 -0
- package/README.md +42 -0
- package/dist/core/artifact.d.ts +19 -0
- package/dist/core/artifact.d.ts.map +1 -0
- package/dist/core/artifact.js +27 -0
- package/dist/core/artifact.js.map +1 -0
- package/dist/core/attestation.d.ts +19 -0
- package/dist/core/attestation.d.ts.map +1 -0
- package/dist/core/attestation.js +12 -0
- package/dist/core/attestation.js.map +1 -0
- package/dist/core/behavioral.d.ts +45 -0
- package/dist/core/behavioral.d.ts.map +1 -0
- package/dist/core/behavioral.js +88 -0
- package/dist/core/behavioral.js.map +1 -0
- package/dist/core/bundle.d.ts +13 -0
- package/dist/core/bundle.d.ts.map +1 -0
- package/dist/core/bundle.js +31 -0
- package/dist/core/bundle.js.map +1 -0
- package/dist/core/chain.d.ts +13 -0
- package/dist/core/chain.d.ts.map +1 -0
- package/dist/core/chain.js +63 -0
- package/dist/core/chain.js.map +1 -0
- package/dist/core/checkpoint.d.ts +8 -0
- package/dist/core/checkpoint.d.ts.map +1 -0
- package/dist/core/checkpoint.js +21 -0
- package/dist/core/checkpoint.js.map +1 -0
- package/dist/core/delegation.d.ts +37 -0
- package/dist/core/delegation.d.ts.map +1 -0
- package/dist/core/delegation.js +104 -0
- package/dist/core/delegation.js.map +1 -0
- package/dist/core/disclosure.d.ts +12 -0
- package/dist/core/disclosure.d.ts.map +1 -0
- package/dist/core/disclosure.js +25 -0
- package/dist/core/disclosure.js.map +1 -0
- package/dist/core/index.d.ts +12 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +12 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/portal.d.ts +28 -0
- package/dist/core/portal.d.ts.map +1 -0
- package/dist/core/portal.js +95 -0
- package/dist/core/portal.js.map +1 -0
- package/dist/core/quarantine.d.ts +8 -0
- package/dist/core/quarantine.d.ts.map +1 -0
- package/dist/core/quarantine.js +13 -0
- package/dist/core/quarantine.js.map +1 -0
- package/dist/core/receipt.d.ts +17 -0
- package/dist/core/receipt.d.ts.map +1 -0
- package/dist/core/receipt.js +17 -0
- package/dist/core/receipt.js.map +1 -0
- package/dist/core/subject.d.ts +4 -0
- package/dist/core/subject.d.ts.map +1 -0
- package/dist/core/subject.js +9 -0
- package/dist/core/subject.js.map +1 -0
- package/dist/core/types.d.ts +167 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/crypto/hash.d.ts +9 -0
- package/dist/crypto/hash.d.ts.map +1 -0
- package/dist/crypto/hash.js +30 -0
- package/dist/crypto/hash.js.map +1 -0
- package/dist/crypto/index.d.ts +6 -0
- package/dist/crypto/index.d.ts.map +1 -0
- package/dist/crypto/index.js +6 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/crypto/merkle.d.ts +8 -0
- package/dist/crypto/merkle.d.ts.map +1 -0
- package/dist/crypto/merkle.js +42 -0
- package/dist/crypto/merkle.js.map +1 -0
- package/dist/crypto/salt.d.ts +5 -0
- package/dist/crypto/salt.d.ts.map +1 -0
- package/dist/crypto/salt.js +14 -0
- package/dist/crypto/salt.js.map +1 -0
- package/dist/crypto/sign.d.ts +11 -0
- package/dist/crypto/sign.d.ts.map +1 -0
- package/dist/crypto/sign.js +37 -0
- package/dist/crypto/sign.js.map +1 -0
- package/dist/crypto/types.d.ts +24 -0
- package/dist/crypto/types.d.ts.map +1 -0
- package/dist/crypto/types.js +2 -0
- package/dist/crypto/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/governance.d.ts +27 -0
- package/dist/middleware/governance.d.ts.map +1 -0
- package/dist/middleware/governance.js +65 -0
- package/dist/middleware/governance.js.map +1 -0
- package/dist/middleware/index.d.ts +2 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +2 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +369 -0
- package/dist/server.js.map +1 -0
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +3 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/interface.d.ts +21 -0
- package/dist/storage/interface.d.ts.map +1 -0
- package/dist/storage/interface.js +2 -0
- package/dist/storage/interface.js.map +1 -0
- package/dist/storage/memory.d.ts +26 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +24 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/storage/sqlite.d.ts +25 -0
- package/dist/storage/sqlite.d.ts.map +1 -0
- package/dist/storage/sqlite.js +44 -0
- package/dist/storage/sqlite.js.map +1 -0
- package/dist/utils/canonical.d.ts +3 -0
- package/dist/utils/canonical.d.ts.map +1 -0
- package/dist/utils/canonical.js +17 -0
- package/dist/utils/canonical.js.map +1 -0
- package/dist/utils/constants.d.ts +4 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +4 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/timestamp.d.ts +4 -0
- package/dist/utils/timestamp.d.ts.map +1 -0
- package/dist/utils/timestamp.js +13 -0
- package/dist/utils/timestamp.js.map +1 -0
- package/dist/utils/uuid.d.ts +2 -0
- package/dist/utils/uuid.d.ts.map +1 -0
- package/dist/utils/uuid.js +3 -0
- package/dist/utils/uuid.js.map +1 -0
- package/package.json +45 -0
- package/src/core/artifact.ts +45 -0
- package/src/core/attestation.ts +33 -0
- package/src/core/behavioral.ts +132 -0
- package/src/core/bundle.ts +31 -0
- package/src/core/chain.ts +72 -0
- package/src/core/checkpoint.ts +22 -0
- package/src/core/delegation.ts +146 -0
- package/src/core/disclosure.ts +32 -0
- package/src/core/index.ts +11 -0
- package/src/core/portal.ts +96 -0
- package/src/core/quarantine.ts +16 -0
- package/src/core/receipt.ts +33 -0
- package/src/core/subject.ts +11 -0
- package/src/core/types.ts +244 -0
- package/src/crypto/hash.ts +33 -0
- package/src/crypto/index.ts +5 -0
- package/src/crypto/merkle.ts +43 -0
- package/src/crypto/salt.ts +18 -0
- package/src/crypto/sign.ts +35 -0
- package/src/crypto/types.ts +19 -0
- package/src/index.ts +12 -0
- package/src/middleware/governance.ts +95 -0
- package/src/middleware/index.ts +1 -0
- package/src/server.ts +436 -0
- package/src/storage/index.ts +3 -0
- package/src/storage/interface.ts +21 -0
- package/src/storage/memory.ts +27 -0
- package/src/storage/sqlite.ts +45 -0
- package/src/tools/README.md +13 -0
- package/src/utils/canonical.ts +14 -0
- package/src/utils/constants.ts +3 -0
- package/src/utils/timestamp.ts +12 -0
- package/src/utils/uuid.ts +2 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export * from './subject.js';
|
|
3
|
+
export * from './attestation.js';
|
|
4
|
+
export * from './artifact.js';
|
|
5
|
+
export * from './receipt.js';
|
|
6
|
+
export * from './chain.js';
|
|
7
|
+
export * from './portal.js';
|
|
8
|
+
export * from './quarantine.js';
|
|
9
|
+
export * from './checkpoint.js';
|
|
10
|
+
export * from './bundle.js';
|
|
11
|
+
export * from './disclosure.js';
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Portal (Sentinel) — Runtime Enforcement Boundary. Ref 150, 270-280.
|
|
3
|
+
* V3: TTL + revocation checked every measurement. Fail-closed semantics.
|
|
4
|
+
* Aligned with NCCoE filing Sections 3-4 and NIST-2025-0035.
|
|
5
|
+
*/
|
|
6
|
+
import { sha256Bytes, sha256Str } from '../crypto/hash.js';
|
|
7
|
+
import { b64ToSig, hexToPk, verifyStr } from '../crypto/sign.js';
|
|
8
|
+
import { canonicalize } from '../utils/canonical.js';
|
|
9
|
+
import { isWithinPeriod, isExpired, utcNow } from '../utils/timestamp.js';
|
|
10
|
+
import type { PolicyArtifact, PortalState, EnforcementAction, SubjectMetadata } from './types.js';
|
|
11
|
+
import type { HashHex } from '../crypto/types.js';
|
|
12
|
+
|
|
13
|
+
export interface MeasurementResult {
|
|
14
|
+
match: boolean;
|
|
15
|
+
currentBytesHash: HashHex;
|
|
16
|
+
currentMetaHash: HashHex;
|
|
17
|
+
expectedBytesHash: HashHex;
|
|
18
|
+
expectedMetaHash: HashHex;
|
|
19
|
+
ttl_ok: boolean;
|
|
20
|
+
revoked: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class Portal {
|
|
24
|
+
state: PortalState = 'INITIALIZATION';
|
|
25
|
+
artifact: PolicyArtifact | null = null;
|
|
26
|
+
sequenceCounter = 0;
|
|
27
|
+
lastLeafHash: HashHex | null = null;
|
|
28
|
+
revocations: Set<string> = new Set();
|
|
29
|
+
|
|
30
|
+
loadArtifact(artifact: PolicyArtifact, pinnedPkHex: string): { ok: boolean; error?: string } {
|
|
31
|
+
this.state = 'ARTIFACT_VERIFICATION';
|
|
32
|
+
const { signature, ...unsigned } = artifact;
|
|
33
|
+
if (!verifyStr(b64ToSig(signature), canonicalize(unsigned), hexToPk(pinnedPkHex))) {
|
|
34
|
+
this.state = 'TERMINATED'; return { ok: false, error: 'Signature verification failed' };
|
|
35
|
+
}
|
|
36
|
+
if (!isWithinPeriod(utcNow(), artifact.effective_timestamp, artifact.expiration_timestamp)) {
|
|
37
|
+
this.state = 'TERMINATED'; return { ok: false, error: 'Artifact outside effective period' };
|
|
38
|
+
}
|
|
39
|
+
if (this.revocations.has(artifact.sealed_hash)) {
|
|
40
|
+
this.state = 'TERMINATED'; return { ok: false, error: 'Artifact has been revoked' };
|
|
41
|
+
}
|
|
42
|
+
this.artifact = artifact;
|
|
43
|
+
this.state = 'ACTIVE_MONITORING';
|
|
44
|
+
return { ok: true };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
measure(subjectBytes: Uint8Array, meta: SubjectMetadata): MeasurementResult {
|
|
48
|
+
if (!this.artifact) throw new Error('No artifact loaded');
|
|
49
|
+
if (this.state === 'TERMINATED') throw new Error('Portal is terminated');
|
|
50
|
+
const empty = { currentBytesHash: '', currentMetaHash: '',
|
|
51
|
+
expectedBytesHash: this.artifact.subject_identifier.bytes_hash,
|
|
52
|
+
expectedMetaHash: this.artifact.subject_identifier.metadata_hash };
|
|
53
|
+
|
|
54
|
+
// Fail-closed: TTL check
|
|
55
|
+
const ttl_ok = !isExpired(this.artifact.issued_timestamp, this.artifact.enforcement_parameters.ttl_seconds);
|
|
56
|
+
if (!ttl_ok) { this.state = 'TERMINATED'; return { match: false, ttl_ok: false, revoked: false, ...empty }; }
|
|
57
|
+
|
|
58
|
+
// Fail-closed: revocation check
|
|
59
|
+
if (this.revocations.has(this.artifact.sealed_hash)) {
|
|
60
|
+
this.state = 'TERMINATED'; return { match: false, ttl_ok: true, revoked: true, ...empty };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const currentBytesHash = sha256Bytes(subjectBytes);
|
|
64
|
+
const currentMetaHash = sha256Str(canonicalize(meta));
|
|
65
|
+
const match = currentBytesHash === this.artifact.subject_identifier.bytes_hash &&
|
|
66
|
+
currentMetaHash === this.artifact.subject_identifier.metadata_hash;
|
|
67
|
+
|
|
68
|
+
if (!match && this.state === 'ACTIVE_MONITORING') this.state = 'DRIFT_DETECTED';
|
|
69
|
+
return { match, currentBytesHash, currentMetaHash,
|
|
70
|
+
expectedBytesHash: this.artifact.subject_identifier.bytes_hash,
|
|
71
|
+
expectedMetaHash: this.artifact.subject_identifier.metadata_hash,
|
|
72
|
+
ttl_ok: true, revoked: false };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
enforce(action: EnforcementAction): void {
|
|
76
|
+
if (this.state !== 'DRIFT_DETECTED') throw new Error(`Cannot enforce in state ${this.state}`);
|
|
77
|
+
switch (action) {
|
|
78
|
+
case 'TERMINATE': case 'SAFE_STATE': this.state = 'TERMINATED'; break;
|
|
79
|
+
case 'QUARANTINE': this.state = 'PHANTOM_QUARANTINE'; break;
|
|
80
|
+
case 'ALERT_ONLY': this.state = 'ACTIVE_MONITORING'; break;
|
|
81
|
+
default: break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
revoke(sealedHash: string): void {
|
|
86
|
+
this.revocations.add(sealedHash);
|
|
87
|
+
if (this.artifact?.sealed_hash === sealedHash) this.state = 'TERMINATED';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
isRevoked(sealedHash: string): boolean { return this.revocations.has(sealedHash); }
|
|
91
|
+
|
|
92
|
+
reset(): void {
|
|
93
|
+
this.state = 'INITIALIZATION'; this.artifact = null;
|
|
94
|
+
this.sequenceCounter = 0; this.lastLeafHash = null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { utcNow } from '../utils/timestamp.js';
|
|
2
|
+
import type { QuarantineState } from './types.js';
|
|
3
|
+
|
|
4
|
+
export function initQuarantine(): QuarantineState {
|
|
5
|
+
return { active: true, started_at: utcNow(), inputs_captured: 0, outputs_severed: true, forensic_buffer: [] };
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function captureInput(q: QuarantineState, inputType: string, data: unknown): void {
|
|
9
|
+
q.forensic_buffer.push({ timestamp: utcNow(), type: inputType, data });
|
|
10
|
+
q.inputs_captured++;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function releaseQuarantine(q: QuarantineState): { duration_ms: number; total_captures: number } {
|
|
14
|
+
q.active = false;
|
|
15
|
+
return { duration_ms: q.started_at ? Date.now() - Date.parse(q.started_at) : 0, total_captures: q.inputs_captured };
|
|
16
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** V3: measurement_type field. Receipts generated for EVERY measurement. */
|
|
2
|
+
import { signStr, sigToB64 } from '../crypto/sign.js';
|
|
3
|
+
import { canonicalize } from '../utils/canonical.js';
|
|
4
|
+
import { utcNow } from '../utils/timestamp.js';
|
|
5
|
+
import { uuid } from '../utils/uuid.js';
|
|
6
|
+
import type { KeyPair, HashHex } from '../crypto/types.js';
|
|
7
|
+
import type { SignedReceipt, SubjectIdentifier, EnforcementAction } from './types.js';
|
|
8
|
+
|
|
9
|
+
export interface ReceiptInput {
|
|
10
|
+
subjectId: SubjectIdentifier;
|
|
11
|
+
artifactRef: HashHex;
|
|
12
|
+
currentHash: string;
|
|
13
|
+
sealedHash: string;
|
|
14
|
+
driftDetected: boolean;
|
|
15
|
+
driftDescription: string | null;
|
|
16
|
+
action: EnforcementAction | null;
|
|
17
|
+
measurementType: string;
|
|
18
|
+
seq: number;
|
|
19
|
+
prevLeaf: HashHex | null;
|
|
20
|
+
portalKP: KeyPair;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function generateReceipt(input: ReceiptInput): SignedReceipt {
|
|
24
|
+
const unsigned = {
|
|
25
|
+
receipt_id: uuid(), subject_identifier: input.subjectId,
|
|
26
|
+
artifact_reference: input.artifactRef, current_hash: input.currentHash,
|
|
27
|
+
sealed_hash: input.sealedHash, drift_detected: input.driftDetected,
|
|
28
|
+
drift_description: input.driftDescription, enforcement_action: input.action,
|
|
29
|
+
measurement_type: input.measurementType, timestamp: utcNow(),
|
|
30
|
+
sequence_number: input.seq, previous_leaf_hash: input.prevLeaf,
|
|
31
|
+
};
|
|
32
|
+
return { ...unsigned, portal_signature: sigToB64(signStr(canonicalize(unsigned), input.portalKP.secretKey)) };
|
|
33
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { sha256Bytes, sha256Str } from '../crypto/hash.js';
|
|
2
|
+
import { canonicalize } from '../utils/canonical.js';
|
|
3
|
+
import type { SubjectIdentifier, SubjectMetadata } from './types.js';
|
|
4
|
+
|
|
5
|
+
export function computeSubjectId(bytes: Uint8Array, meta: SubjectMetadata): SubjectIdentifier {
|
|
6
|
+
return { bytes_hash: sha256Bytes(bytes), metadata_hash: sha256Str(canonicalize(meta)) };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function computeSubjectIdFromString(content: string, meta: SubjectMetadata): SubjectIdentifier {
|
|
10
|
+
return computeSubjectId(new TextEncoder().encode(content), meta);
|
|
11
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V3: Aligned with NIST-2025-0035 and NCCoE AI Agent Identity filings.
|
|
3
|
+
* Every interface annotated with patent reference numeral.
|
|
4
|
+
*/
|
|
5
|
+
import type { HashHex, SignatureBase64, SaltHex, MerkleInclusionProof } from '../crypto/types.js';
|
|
6
|
+
|
|
7
|
+
// ── Subject (100, 102, 104, 106, 126) ──────────────────────────
|
|
8
|
+
|
|
9
|
+
export interface SubjectIdentifier { // Ref 126
|
|
10
|
+
bytes_hash: HashHex; // Ref 104
|
|
11
|
+
metadata_hash: HashHex; // Ref 106
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface SubjectMetadata {
|
|
15
|
+
filename?: string;
|
|
16
|
+
creation_timestamp?: string;
|
|
17
|
+
author?: string;
|
|
18
|
+
version?: string;
|
|
19
|
+
content_type?: string;
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ── Enforcement (130–136, 162–168) ──────────────────────────────
|
|
24
|
+
|
|
25
|
+
export type EnforcementAction =
|
|
26
|
+
| 'TERMINATE' // Ref 162: immediate kill
|
|
27
|
+
| 'QUARANTINE' // Ref 164: phantom execution
|
|
28
|
+
| 'NETWORK_ISOLATE' // Ref 166: sever network, continue local
|
|
29
|
+
| 'SAFE_STATE' // Ref 168: return-to-home / controlled shutdown
|
|
30
|
+
| 'KEY_REVOKE' // invalidate crypto keys
|
|
31
|
+
| 'TOKEN_INVALIDATE' // revoke access tokens
|
|
32
|
+
| 'ACTUATOR_DISCONNECT' // sever physical actuator connections
|
|
33
|
+
| 'ALERT_ONLY'; // log without enforcement (gradual deployment)
|
|
34
|
+
|
|
35
|
+
export type MeasurementType =
|
|
36
|
+
| 'EXECUTABLE_IMAGE' | 'LOADED_MODULES' | 'CONTAINER_IMAGE'
|
|
37
|
+
| 'CONFIG_MANIFEST' | 'SBOM' | 'TEE_QUOTE'
|
|
38
|
+
| 'MEMORY_REGIONS' | 'CONTROL_FLOW' | 'FILE_SYSTEM_STATE'
|
|
39
|
+
| 'NETWORK_CONFIG';
|
|
40
|
+
|
|
41
|
+
export interface EnforcementParams { // Ref 130
|
|
42
|
+
measurement_cadence_ms: number; // Ref 132
|
|
43
|
+
ttl_seconds: number; // Ref 134
|
|
44
|
+
enforcement_triggers: EnforcementAction[]; // Ref 136
|
|
45
|
+
re_attestation_required: boolean;
|
|
46
|
+
measurement_types: MeasurementType[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ── Policy & Disclosure (112, 138–142) ──────────────────────────
|
|
50
|
+
|
|
51
|
+
export type Sensitivity = 'S1_LOW' | 'S2_MODERATE' | 'S3_HIGH' | 'S4_CRITICAL';
|
|
52
|
+
export type DisclosureMode = 'PROOF_ONLY' | 'REVEAL_MIN' | 'REVEAL_FULL';
|
|
53
|
+
|
|
54
|
+
export interface ClaimRecord { // Ref 140
|
|
55
|
+
claim_id: string;
|
|
56
|
+
sensitivity: Sensitivity;
|
|
57
|
+
substitutes: string[];
|
|
58
|
+
inference_risks: string[];
|
|
59
|
+
permitted_modes: DisclosureMode[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface SubstitutionRule { // Ref 142
|
|
63
|
+
original_claim_id: string;
|
|
64
|
+
substitute_claim_id: string;
|
|
65
|
+
conditions: Record<string, unknown>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface DisclosurePolicy { // Ref 138
|
|
69
|
+
claims_taxonomy: ClaimRecord[];
|
|
70
|
+
substitution_rules: SubstitutionRule[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ── Evidence Commitment (114) ───────────────────────────────────
|
|
74
|
+
|
|
75
|
+
export interface EvidenceCommitmentRecord {
|
|
76
|
+
commitment: HashHex;
|
|
77
|
+
salt: SaltHex;
|
|
78
|
+
label: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ── Policy Artifact (120, 122, 124, 144) ────────────────────────
|
|
82
|
+
|
|
83
|
+
export interface PolicyArtifact { // Ref 122 (Weave Piece)
|
|
84
|
+
schema_version: string;
|
|
85
|
+
protocol_version: string;
|
|
86
|
+
subject_identifier: SubjectIdentifier;
|
|
87
|
+
policy_reference: HashHex; // Ref 128
|
|
88
|
+
policy_version: number;
|
|
89
|
+
sealed_hash: HashHex; // Ref 124
|
|
90
|
+
seal_salt: SaltHex; // stored for audit
|
|
91
|
+
issued_timestamp: string;
|
|
92
|
+
effective_timestamp: string;
|
|
93
|
+
expiration_timestamp: string | null;
|
|
94
|
+
issuer_identifier: string;
|
|
95
|
+
enforcement_parameters: EnforcementParams;
|
|
96
|
+
disclosure_policy: DisclosurePolicy;
|
|
97
|
+
evidence_commitments: EvidenceCommitmentRecord[];
|
|
98
|
+
signature: SignatureBase64; // Ref 144
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── Receipts (170, 172) ─────────────────────────────────────────
|
|
102
|
+
// V3: Generated for EVERY measurement, not just drift. Per NIST filing:
|
|
103
|
+
// "each measurement generates a signed receipt — match or mismatch"
|
|
104
|
+
|
|
105
|
+
export interface SignedReceipt { // Ref 172
|
|
106
|
+
receipt_id: string;
|
|
107
|
+
subject_identifier: SubjectIdentifier;
|
|
108
|
+
artifact_reference: HashHex;
|
|
109
|
+
current_hash: string;
|
|
110
|
+
sealed_hash: string;
|
|
111
|
+
drift_detected: boolean;
|
|
112
|
+
drift_description: string | null;
|
|
113
|
+
enforcement_action: EnforcementAction | null;
|
|
114
|
+
measurement_type: string; // V3: which measurement was performed
|
|
115
|
+
timestamp: string;
|
|
116
|
+
sequence_number: number;
|
|
117
|
+
previous_leaf_hash: HashHex | null;
|
|
118
|
+
portal_signature: SignatureBase64;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── Continuity Chain (180–196) ──────────────────────────────────
|
|
122
|
+
|
|
123
|
+
export type EventType =
|
|
124
|
+
| 'GENESIS'
|
|
125
|
+
| 'POLICY_ISSUANCE'
|
|
126
|
+
| 'INTERACTION_RECEIPT'
|
|
127
|
+
| 'REVOCATION' // V3: mid-session revocation
|
|
128
|
+
| 'ATTESTATION'
|
|
129
|
+
| 'ANCHOR_BATCH'
|
|
130
|
+
| 'DISCLOSURE'
|
|
131
|
+
| 'SUBSTITUTION'
|
|
132
|
+
| 'KEY_ROTATION'; // V3: key lifecycle event
|
|
133
|
+
|
|
134
|
+
export interface GenesisPayload {
|
|
135
|
+
protocol_version: string;
|
|
136
|
+
taxonomy_version: string;
|
|
137
|
+
root_fingerprint: string;
|
|
138
|
+
specification_hash: HashHex;
|
|
139
|
+
marker: 'GENESIS';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface ContinuityEvent { // Ref 184
|
|
143
|
+
schema_version: string;
|
|
144
|
+
protocol_version: string;
|
|
145
|
+
event_type: EventType;
|
|
146
|
+
event_id: string;
|
|
147
|
+
sequence_number: number;
|
|
148
|
+
timestamp: string;
|
|
149
|
+
previous_leaf_hash: HashHex | null;
|
|
150
|
+
leaf_hash: HashHex; // Ref 186
|
|
151
|
+
payload: unknown; // Ref 192
|
|
152
|
+
payload_hash: HashHex; // Ref 194
|
|
153
|
+
event_signature: SignatureBase64; // Ref 196
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface StructuralMetadata { // Ref 190
|
|
157
|
+
schema_version: string;
|
|
158
|
+
protocol_version: string;
|
|
159
|
+
event_type: EventType;
|
|
160
|
+
event_id: string;
|
|
161
|
+
sequence_number: number;
|
|
162
|
+
timestamp: string;
|
|
163
|
+
previous_leaf_hash: HashHex | null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ── Checkpoints (200–214) ───────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
export interface CheckpointReference {
|
|
169
|
+
merkle_root: HashHex;
|
|
170
|
+
batch_start_sequence: number;
|
|
171
|
+
batch_end_sequence: number;
|
|
172
|
+
anchor_network: string;
|
|
173
|
+
transaction_id: string;
|
|
174
|
+
timestamp: string;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface AnchorBatchPayload {
|
|
178
|
+
checkpoint_reference: CheckpointReference;
|
|
179
|
+
leaf_count: number;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ── Evidence Bundle (240–246) ───────────────────────────────────
|
|
183
|
+
|
|
184
|
+
export interface EvidenceBundle {
|
|
185
|
+
artifact: PolicyArtifact;
|
|
186
|
+
receipts: SignedReceipt[];
|
|
187
|
+
merkle_proofs: MerkleInclusionProof[];
|
|
188
|
+
checkpoint_reference: CheckpointReference;
|
|
189
|
+
public_key: string;
|
|
190
|
+
bundle_signature: SignatureBase64;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ── Disclosure (250–256) ────────────────────────────────────────
|
|
194
|
+
|
|
195
|
+
export interface DisclosureRequest {
|
|
196
|
+
requested_claim_id: string;
|
|
197
|
+
requester_id: string;
|
|
198
|
+
mode: DisclosureMode;
|
|
199
|
+
timestamp: string;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface SubstitutionReceipt {
|
|
203
|
+
receipt_id: string;
|
|
204
|
+
original_claim_id: string;
|
|
205
|
+
substitute_claim_id: string | null;
|
|
206
|
+
policy_version: number;
|
|
207
|
+
reason_code: string;
|
|
208
|
+
timestamp: string;
|
|
209
|
+
chain_sequence_ref: number;
|
|
210
|
+
signature: SignatureBase64;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ── Portal State Machine (150, 270–280) ─────────────────────────
|
|
214
|
+
|
|
215
|
+
export type PortalState =
|
|
216
|
+
| 'INITIALIZATION' // Ref 270
|
|
217
|
+
| 'ARTIFACT_VERIFICATION' // Ref 272
|
|
218
|
+
| 'ACTIVE_MONITORING' // Ref 274
|
|
219
|
+
| 'DRIFT_DETECTED' // Ref 276
|
|
220
|
+
| 'PHANTOM_QUARANTINE' // Ref 278
|
|
221
|
+
| 'TERMINATED'; // Ref 280
|
|
222
|
+
|
|
223
|
+
export type VerificationTier = 'BRONZE' | 'SILVER' | 'GOLD';
|
|
224
|
+
|
|
225
|
+
// ── Revocation (V3) ────────────────────────────────────────────
|
|
226
|
+
// Per NCCoE filing Phase 3b: "An administrator pushes a REVOCATION event
|
|
227
|
+
// to the continuity chain, invalidating the agent's active policy artifact."
|
|
228
|
+
|
|
229
|
+
export interface RevocationRecord {
|
|
230
|
+
artifact_sealed_hash: HashHex;
|
|
231
|
+
reason: string;
|
|
232
|
+
revoked_by: string; // issuer pk hex
|
|
233
|
+
timestamp: string;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ── Quarantine (220–230) ────────────────────────────────────────
|
|
237
|
+
|
|
238
|
+
export interface QuarantineState {
|
|
239
|
+
active: boolean;
|
|
240
|
+
started_at: string | null;
|
|
241
|
+
inputs_captured: number;
|
|
242
|
+
outputs_severed: boolean;
|
|
243
|
+
forensic_buffer: Array<{ timestamp: string; type: string; data: unknown }>;
|
|
244
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
2
|
+
import { blake2b } from '@noble/hashes/blake2b';
|
|
3
|
+
import { bytesToHex } from '@noble/hashes/utils';
|
|
4
|
+
import type { HashHex } from './types.js';
|
|
5
|
+
|
|
6
|
+
const enc = new TextEncoder();
|
|
7
|
+
|
|
8
|
+
export function sha256Bytes(data: Uint8Array): HashHex {
|
|
9
|
+
return bytesToHex(sha256(data));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function sha256Str(data: string): HashHex {
|
|
13
|
+
return sha256Bytes(enc.encode(data));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function blake2b256(data: Uint8Array): HashHex {
|
|
17
|
+
return bytesToHex(blake2b(data, { dkLen: 32 }));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Concatenate inputs (NO delimiter) and SHA-256. Patent Section D: "no delimiters." */
|
|
21
|
+
export function sha256Cat(...parts: (Uint8Array | string)[]): HashHex {
|
|
22
|
+
const bufs = parts.map(p => typeof p === 'string' ? enc.encode(p) : p);
|
|
23
|
+
const total = bufs.reduce((n, b) => n + b.length, 0);
|
|
24
|
+
const combined = new Uint8Array(total);
|
|
25
|
+
let off = 0;
|
|
26
|
+
for (const b of bufs) { combined.set(b, off); off += b.length; }
|
|
27
|
+
return sha256Bytes(combined);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Concatenate hex strings as text (no decode) and hash. For sealed_hash computation. */
|
|
31
|
+
export function sha256HexCat(...hexes: string[]): HashHex {
|
|
32
|
+
return sha256Str(hexes.join(''));
|
|
33
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { sha256Str } from './hash.js';
|
|
2
|
+
import type { HashHex, MerkleInclusionProof } from './types.js';
|
|
3
|
+
|
|
4
|
+
function pair(l: HashHex, r: HashHex): HashHex { return sha256Str(l + r); }
|
|
5
|
+
|
|
6
|
+
export function buildMerkleTree(leaves: HashHex[]): { root: HashHex; layers: HashHex[][] } {
|
|
7
|
+
if (!leaves.length) throw new Error('Empty leaf set');
|
|
8
|
+
if (leaves.length === 1) return { root: leaves[0], layers: [leaves] };
|
|
9
|
+
const layers: HashHex[][] = [[...leaves]];
|
|
10
|
+
let cur = leaves;
|
|
11
|
+
while (cur.length > 1) {
|
|
12
|
+
const next: HashHex[] = [];
|
|
13
|
+
for (let i = 0; i < cur.length; i += 2) {
|
|
14
|
+
next.push(pair(cur[i], i + 1 < cur.length ? cur[i + 1] : cur[i]));
|
|
15
|
+
}
|
|
16
|
+
layers.push(next);
|
|
17
|
+
cur = next;
|
|
18
|
+
}
|
|
19
|
+
return { root: cur[0], layers };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function inclusionProof(leaves: HashHex[], idx: number): MerkleInclusionProof {
|
|
23
|
+
if (idx < 0 || idx >= leaves.length) throw new RangeError(`Index ${idx} out of [0,${leaves.length})`);
|
|
24
|
+
const { root, layers } = buildMerkleTree(leaves);
|
|
25
|
+
const siblings: MerkleInclusionProof['siblings'] = [];
|
|
26
|
+
let ci = idx;
|
|
27
|
+
for (let L = 0; L < layers.length - 1; L++) {
|
|
28
|
+
const layer = layers[L];
|
|
29
|
+
const isRight = ci % 2 === 1;
|
|
30
|
+
const si = isRight ? ci - 1 : (ci + 1 < layer.length ? ci + 1 : ci);
|
|
31
|
+
siblings.push({ hash: layer[si], position: isRight ? 'left' : 'right' });
|
|
32
|
+
ci = Math.floor(ci / 2);
|
|
33
|
+
}
|
|
34
|
+
return { leafHash: leaves[idx], leafIndex: idx, siblings, root };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function verifyProof(proof: MerkleInclusionProof): boolean {
|
|
38
|
+
let h = proof.leafHash;
|
|
39
|
+
for (const s of proof.siblings) {
|
|
40
|
+
h = s.position === 'left' ? pair(s.hash, h) : pair(h, s.hash);
|
|
41
|
+
}
|
|
42
|
+
return h === proof.root;
|
|
43
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { bytesToHex } from '@noble/hashes/utils';
|
|
3
|
+
import { sha256Cat } from './hash.js';
|
|
4
|
+
import type { SaltHex, SaltedCommitment, HashHex } from './types.js';
|
|
5
|
+
|
|
6
|
+
const enc = new TextEncoder();
|
|
7
|
+
|
|
8
|
+
export function generateSalt(): SaltHex { return bytesToHex(randomBytes(16)); }
|
|
9
|
+
|
|
10
|
+
export function saltedCommitment(content: Uint8Array | string, salt?: SaltHex): SaltedCommitment {
|
|
11
|
+
const s = salt ?? generateSalt();
|
|
12
|
+
const c = typeof content === 'string' ? enc.encode(content) : content;
|
|
13
|
+
return { commitment: sha256Cat(c, s), salt: s };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function verifySaltedCommitment(content: Uint8Array | string, salt: SaltHex, expected: HashHex): boolean {
|
|
17
|
+
return saltedCommitment(content, salt).commitment === expected;
|
|
18
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as ed from '@noble/ed25519';
|
|
2
|
+
import { sha512 } from '@noble/hashes/sha512';
|
|
3
|
+
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
|
|
4
|
+
import type { KeyPair, Signature, SignatureBase64 } from './types.js';
|
|
5
|
+
|
|
6
|
+
// Set sha512 sync ONCE at module load
|
|
7
|
+
ed.etc.sha512Sync = (...m: Uint8Array[]) => {
|
|
8
|
+
const total = m.reduce((n, a) => n + a.length, 0);
|
|
9
|
+
const buf = new Uint8Array(total);
|
|
10
|
+
let off = 0;
|
|
11
|
+
for (const a of m) { buf.set(a, off); off += a.length; }
|
|
12
|
+
return sha512(buf);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const enc = new TextEncoder();
|
|
16
|
+
|
|
17
|
+
export function generateKeyPair(): KeyPair {
|
|
18
|
+
const secretKey = ed.utils.randomPrivateKey();
|
|
19
|
+
return { publicKey: ed.getPublicKey(secretKey), secretKey };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function sign(msg: Uint8Array, sk: Uint8Array): Signature { return ed.sign(msg, sk); }
|
|
23
|
+
export function signStr(msg: string, sk: Uint8Array): Signature { return sign(enc.encode(msg), sk); }
|
|
24
|
+
|
|
25
|
+
export function verify(sig: Signature, msg: Uint8Array, pk: Uint8Array): boolean {
|
|
26
|
+
try { return ed.verify(sig, msg, pk); } catch { return false; }
|
|
27
|
+
}
|
|
28
|
+
export function verifyStr(sig: Signature, msg: string, pk: Uint8Array): boolean {
|
|
29
|
+
return verify(sig, enc.encode(msg), pk);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const sigToB64 = (s: Signature): SignatureBase64 => Buffer.from(s).toString('base64');
|
|
33
|
+
export const b64ToSig = (b: SignatureBase64): Signature => new Uint8Array(Buffer.from(b, 'base64'));
|
|
34
|
+
export const pkToHex = (pk: Uint8Array): string => bytesToHex(pk);
|
|
35
|
+
export const hexToPk = (h: string): Uint8Array => hexToBytes(h);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type PublicKey = Uint8Array;
|
|
2
|
+
export type SecretKey = Uint8Array;
|
|
3
|
+
export interface KeyPair { publicKey: PublicKey; secretKey: SecretKey; }
|
|
4
|
+
export type Signature = Uint8Array;
|
|
5
|
+
export type HashHex = string;
|
|
6
|
+
export type SignatureBase64 = string;
|
|
7
|
+
export type SaltHex = string;
|
|
8
|
+
|
|
9
|
+
export interface SaltedCommitment {
|
|
10
|
+
commitment: HashHex;
|
|
11
|
+
salt: SaltHex;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface MerkleInclusionProof {
|
|
15
|
+
leafHash: HashHex;
|
|
16
|
+
leafIndex: number;
|
|
17
|
+
siblings: Array<{ hash: HashHex; position: 'left' | 'right' }>;
|
|
18
|
+
root: HashHex;
|
|
19
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { createAGAServer } from './server.js';
|
|
4
|
+
|
|
5
|
+
async function main() {
|
|
6
|
+
const server = await createAGAServer();
|
|
7
|
+
const transport = new StdioServerTransport();
|
|
8
|
+
await server.connect(transport);
|
|
9
|
+
console.error('AGA MCP Server running on stdio');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
main().catch(e => { console.error('Fatal:', e); process.exit(1); });
|