// Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
// ══════════════════════════════════════════════════════════════════
// Decision Receipt (v2.3, Component A — dumb-sink hardening)
// ══════════════════════════════════════════════════════════════════
// Emits an in-toto Decision Receipt v0.1 predicate alongside the
// existing PolicyReceipt. The PolicyReceipt is the v2.x backward-
// compatible record; the DecisionReceiptEnvelope is the bilateral
// attestation target committed to in:
//   docs/ENFORCEMENT-TRUST-ANCHOR.md Component A
//   https://github.com/in-toto/attestation/pull/549
//
// The envelope is produced as a DSSE-style signed Statement:
//
//   { payloadType, payload: <JCS-canonical Statement string>,
//     signatures: [{ keyid, sig }] }
//
// so it verifies against @veritasacta/verify and composes with the
// parallel Python emission in aeoess/hermes-aps-delegation. This
// module is a pure primitive: no network, no mutable state, no
// gateway dependency. Gateway integration is the caller's choice.
// ══════════════════════════════════════════════════════════════════
import { createHash } from 'node:crypto';
import { sign } from './crypto/keys.js';
import { canonicalizeJCS } from './core/canonical-jcs.js';
/** Canonical predicate identifier for the in-toto Decision Receipt v0.1.
 *  Matches the emission on the Hermes-APS delegation Python side. */
export const DECISION_RECEIPT_PREDICATE_TYPE = 'https://veritasacta.com/attestation/decision-receipt/v0.1';
/** in-toto Statement envelope type. */
export const INTOTO_STATEMENT_V1 = 'https://in-toto.io/Statement/v1';
/** DSSE payloadType for in-toto Statements. */
export const INTOTO_PAYLOAD_TYPE = 'application/vnd.in-toto+json';
// ── Helpers ──
function sha256Hex(input) {
    return createHash('sha256').update(input).digest('hex');
}
function digestOf(obj) {
    return { sha256: sha256Hex(canonicalizeJCS(obj)) };
}
/** Compute the delegation chain root digest used by v2.3 bilateral receipts.
 *  Deterministic across re-emissions: SHA-256 of the JCS serialization of the
 *  chain array. Exported so verifiers and cross-repo implementations (e.g. the
 *  Python hermes-aps-delegation emitter) can reproduce it byte-for-byte. */
export function computeDelegationChainRoot(chain) {
    return sha256Hex(canonicalizeJCS(chain));
}
// ── Main primitive ──
/** Emit an in-toto Decision Receipt v0.1 envelope.
 *
 *  Pure function. Given an intent/decision/receipt triple and the delegation
 *  chain that authorized it, produces a DSSE-style signed Statement whose
 *  predicate carries delegation_chain_root, delegation_depth, and the typed
 *  epistemic_claims. The caller retains the ordinary PolicyReceipt for v2.2.x
 *  consumers and emits this envelope in parallel for v2.3-aware verifiers.
 *
 *  The returned envelope's payload string is the JCS canonicalization of the
 *  in-toto Statement — signed bytes match the payload field exactly, so a
 *  verifier canonicalizes json.loads(envelope.payload) and checks equality
 *  before verifying the signature. Cross-repo interop requires nothing more
 *  than JCS and Ed25519. */
export function emitDecisionReceipt(input) {
    const apsVersion = input.apsVersion ?? '2.3.0-alpha';
    const subject = [
        {
            name: `aps-action:${input.intent.action.type}:${input.intent.intentId}`,
            digest: digestOf(input.intent),
        },
    ];
    const predicate = {
        decision: input.decision.verdict,
        reason: input.decision.reason,
        policyId: input.policyId,
        policyDigest: digestOf({ version: input.decision.floorVersion, policyId: input.policyId }),
        delegationChainRoot: {
            sha256: computeDelegationChainRoot(input.delegationChain),
        },
        delegationDepth: input.delegationChain.length,
        epistemicClaims: input.epistemicClaims,
        issuerId: input.issuerId,
        issuedAt: new Date().toISOString(),
        intentDigest: digestOf(input.intent),
        receiptDigest: digestOf(input.receipt),
        metadata: {
            framework: 'aps',
            receiptKind: 'decision_receipt',
            apsVersion,
            actionRef: input.intent.actionRef,
        },
    };
    const statement = {
        _type: INTOTO_STATEMENT_V1,
        predicateType: DECISION_RECEIPT_PREDICATE_TYPE,
        subject,
        predicate,
    };
    const payload = canonicalizeJCS(statement);
    const signature = sign(payload, input.signerPrivateKey);
    return {
        payloadType: INTOTO_PAYLOAD_TYPE,
        payload,
        signatures: [{ keyid: input.signerKeyId, sig: signature }],
        _digest: { sha256: sha256Hex(payload) },
    };
}
/** Parse a DecisionReceiptEnvelope payload back into its in-toto Statement.
 *  Round-trip: canonicalizeJCS(parseDecisionReceiptStatement(env)) === env.payload. */
export function parseDecisionReceiptStatement(envelope) {
    return JSON.parse(envelope.payload);
}
//# sourceMappingURL=decisionReceipt.js.map