aga_measure_subject
Measure digital content against sealed cryptographic references to verify integrity and generate signed receipts for tamper-evident logging.
Instructions
Measure subject state, compare to sealed reference. Generates signed receipt. (Claims 1e-1g)
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| subject_content | No | Raw content to measure | |
| subject_bytes_hash | No | Pre-computed SHA-256 bytes hash (64 hex) | |
| subject_metadata_hash | No | Pre-computed SHA-256 metadata hash (64 hex) | |
| subject_metadata | No |
Implementation Reference
- src/tools/measure-subject.ts:16-122 (handler)The handleMeasureSubject function processes subject measurement requests, performs drift detection, updates portal state, generates cryptographic receipts, and interacts with the continuity chain.
export async function handleMeasureSubject(args: MeasureSubjectArgs, ctx: ServerContext) { if (!ctx.portal.artifact) return ctx.error('No artifact loaded. Call aga_create_artifact first.'); if (ctx.portal.state === 'TERMINATED') return ctx.error('Portal is terminated. Re-attest required.'); if (ctx.portal.state === 'SAFE_STATE') return ctx.error('Portal is in safe state. Re-attest required.'); let currentBytesHash: string; let currentMetaHash: string; let match: boolean; if (args.subject_bytes_hash) { // Pre-computed hash mode currentBytesHash = args.subject_bytes_hash; currentMetaHash = args.subject_metadata_hash ?? ctx.portal.artifact.subject_identifier.metadata_hash; match = currentBytesHash === ctx.portal.artifact.subject_identifier.bytes_hash && currentMetaHash === ctx.portal.artifact.subject_identifier.metadata_hash; if (!match && ctx.portal.state === 'ACTIVE_MONITORING') { (ctx.portal as any).state = 'DRIFT_DETECTED'; } } else if (args.subject_content) { // Content mode - use portal.measure() const result = ctx.portal.measure( new TextEncoder().encode(args.subject_content), args.subject_metadata ?? {}, ); currentBytesHash = result.currentBytesHash; currentMetaHash = result.currentMetaHash; match = result.match; if (!result.ttl_ok) { ctx.measurementCount++; const receipt = generateReceipt({ subjectId: ctx.portal.artifact.subject_identifier, artifactRef: hashArtifact(ctx.portal.artifact), currentHash: 'UNAVAILABLE', sealedHash: `${result.expectedBytesHash}||${result.expectedMetaHash}`, driftDetected: true, driftDescription: 'TTL expired - fail-closed termination', action: 'TERMINATE', measurementType: ctx.portal.artifact.enforcement_parameters.measurement_types.join(','), seq: ctx.portal.sequenceCounter + 1, prevLeaf: ctx.portal.lastLeafHash, portalKP: ctx.portalKP, }); await ctx.storage.storeReceipt(receipt); await ctx.appendToChain('INTERACTION_RECEIPT', { receipt_id: receipt.receipt_id, drift_detected: true, enforcement_action: 'TERMINATE' }); return ctx.json({ success: true, match: false, drift_detected: true, ttl_ok: false, revoked: false, enforcement_action: 'TERMINATE', portal_state: ctx.portal.state, receipt_id: receipt.receipt_id, measurement_count: ctx.measurementCount }); } if (result.revoked) { ctx.measurementCount++; const receipt = generateReceipt({ subjectId: ctx.portal.artifact.subject_identifier, artifactRef: hashArtifact(ctx.portal.artifact), currentHash: 'UNAVAILABLE', sealedHash: `${result.expectedBytesHash}||${result.expectedMetaHash}`, driftDetected: true, driftDescription: 'Artifact revoked - fail-closed termination', action: 'TERMINATE', measurementType: ctx.portal.artifact.enforcement_parameters.measurement_types.join(','), seq: ctx.portal.sequenceCounter + 1, prevLeaf: ctx.portal.lastLeafHash, portalKP: ctx.portalKP, }); await ctx.storage.storeReceipt(receipt); await ctx.appendToChain('INTERACTION_RECEIPT', { receipt_id: receipt.receipt_id, drift_detected: true, enforcement_action: 'TERMINATE' }); return ctx.json({ success: true, match: false, drift_detected: true, ttl_ok: true, revoked: true, enforcement_action: 'TERMINATE', portal_state: ctx.portal.state, receipt_id: receipt.receipt_id, measurement_count: ctx.measurementCount }); } } else { return ctx.error('Provide either subject_content or subject_bytes_hash'); } const artRef = hashArtifact(ctx.portal.artifact); const currentStr = `${currentBytesHash}||${currentMetaHash}`; const sealedStr = `${ctx.portal.artifact.subject_identifier.bytes_hash}||${ctx.portal.artifact.subject_identifier.metadata_hash}`; let action: EnforcementAction | null = null; let driftDesc: string | null = null; if (!match) { driftDesc = 'Subject modified - hash mismatch'; action = ctx.portal.artifact.enforcement_parameters.enforcement_triggers[0] ?? 'ALERT_ONLY'; if (ctx.portal.state === 'DRIFT_DETECTED') { ctx.portal.enforce(action); } if (action === 'QUARANTINE') ctx.quarantine = initQuarantine(); } ctx.measurementCount++; const receipt = generateReceipt({ subjectId: ctx.portal.artifact.subject_identifier, artifactRef: artRef, currentHash: currentStr, sealedHash: sealedStr, driftDetected: !match, driftDescription: driftDesc, action, measurementType: ctx.portal.artifact.enforcement_parameters.measurement_types.join(','), seq: ctx.portal.sequenceCounter + 1, prevLeaf: ctx.portal.lastLeafHash, portalKP: ctx.portalKP, }); await ctx.storage.storeReceipt(receipt); await ctx.appendToChain('INTERACTION_RECEIPT', { receipt_id: receipt.receipt_id, drift_detected: !match, enforcement_action: action, }); return ctx.json({ success: true, match, drift_detected: !match, ttl_ok: true, revoked: false, enforcement_action: action, portal_state: ctx.portal.state, receipt_id: receipt.receipt_id, measurement_count: ctx.measurementCount, }); } - src/tools/measure-subject.ts:9-14 (schema)The MeasureSubjectArgs interface defines the expected input schema for the aga_measure_subject tool.
export interface MeasureSubjectArgs { subject_content?: string; subject_bytes_hash?: string; subject_metadata_hash?: string; subject_metadata?: SubjectMetadata; } - src/server.ts:122-137 (registration)Registration of the aga_measure_subject tool in the MCP server using governedTool middleware.
// 4. aga_measure_subject (governed) governedTool('aga_measure_subject', 'Measure subject state, compare to sealed reference. Generates signed receipt. (Claims 1e-1g)', { subject_content: z.string().optional().describe('Raw content to measure'), subject_bytes_hash: z.string().optional().describe('Pre-computed SHA-256 bytes hash (64 hex)'), subject_metadata_hash: z.string().optional().describe('Pre-computed SHA-256 metadata hash (64 hex)'), subject_metadata: z.object({ filename: z.string().optional(), version: z.string().optional(), author: z.string().optional(), content_type: z.string().optional(), }).optional(), }, async (args) => handleMeasureSubject({ ...args, subject_metadata: args.subject_metadata ?? {} }, ctx), );