Confidential Mode
Confidential mode replaces sensitive fields with one-way HMAC-SHA256 commitments instead of plaintext. It is not encryption: hiding depends on salt secrecy, binding does not.
Summary
Confidential mode seals sensitive snapshot fields into one-way commitments. The current recommended path is V2 (sealConfidentialV2 / verifyConfidentialV2), documented below alongside the V1 path (sealConfidential / verifyConfidential), which remains supported. See AI Execution V2 for the full V2 snapshot schema.
Commitments Are Not Encryption
A commitment is a one-way HMAC-SHA256 value. It cannot be reversed to recover the original content, and NexArt never describes this as encryption. The two properties that matter are distinct:
- Hiding. Whether a third party can guess or recover the original value from the commitment depends entirely on the secrecy of the salt used to compute it.
- Binding. Whether a later-revealed value can be checked against the commitment does not depend on the salt being secret; binding holds even if the salt is later disclosed as part of an opening.
Never describe this mechanism as "encrypted by NexArt". The correct description is: values are sealed as one-way cryptographic commitments.
Commitment Construction
commitment = "hmac-sha256:" + hex(HMAC-SHA256(key = salt, msg = domainSep || content))
domainSep = utf8(toCanonicalJson({ scheme, field, protocolVersion })) || 0x00COMMITMENT_SCHEME_V1 = 'hmac-sha256-v1'COMMITMENT_PREFIX_V1 = 'hmac-sha256:'SALT_BYTES = 32(salts shorter than this are rejected as weak)
V2: sealConfidentialV2 / verifyConfidentialV2
V2 is the current recommended confidential path, operating on AiExecutionSnapshotV2 records. Sealing requires an explicit, mandatory feature gate: { confidential: true }. There is no implicit or accidental sealing.
import { sealConfidentialV2 } from "@nexart/ai-execution";
const { cer, openings } = sealConfidentialV2(snapshot, {
confidential: true, // mandatory gate
fields: ["input", "output"], // ConfidentialFieldV2[]
decisionRefIndexes: [0], // seal decisionRefs[0]
});
// openings holds { field, salt, scheme } entries kept by the producer.
// They are NOT written into the CER.import { verifyCerV2, verifyConfidentialV2 } from "@nexart/ai-execution";
// Base verification needs no openings at all:
const base = verifyCerV2(cer);
// Selective-disclosure verification, when the producer chooses to share openings:
const disclosed = verifyConfidentialV2(cer, openings);
// verifyConfidentialV2Async is the async equivalent.ConfidentialFieldV2
ConfidentialFieldV2 = 'input' | 'output' | 'instruction'. Only these three snapshot fields can be sealed as confidential in V2.
Commitment Domains and decisionRef Index Binding
V2 defines four commitment domains:
"input""output""instruction""decisionRef:<index>"
The decisionRef:<index> domain binds the array index of the decisionRefs entry being committed. This means an opening does not just prove "some decisionRef was committed"; it proves which specific slot in the decisionRefs array it opens. A verifier cannot substitute an opening from one index to satisfy another.
Openings Are Never Stored in the CER
An opening is { field, salt, scheme }. Openings are produced by sealConfidentialV2 / sealConfidentialV2Async and returned to the caller, but they are never written into the CER bundle itself. The producer is responsible for retaining and selectively disclosing openings. Because the CER never contains openings, base verifyCerV2 can verify commitment consistency and overall bundle integrity without any opening at all; openings are only needed when a verifier is meant to see the underlying plaintext.
Fail-Closed Rules
- Unknown commitment scheme (anything other than
hmac-sha256-v1) fails closed. - A salt shorter than
SALT_BYTES(32 bytes) is rejected as weak. - An opening naming a field that is not a recognized
ConfidentialFieldV2is rejected as an unknown field. - An opening naming a
decisionRefindex that is absent from the snapshot'sdecisionRefsarray is rejected rather than silently ignored.
Other V2 exports: computeCommitment, computeCommitmentAsync, generateSalt, assertValidSalt, assertSupportedScheme.
V1: sealConfidential / verifyConfidential (supported)
The V1 confidential path remains supported for cer.ai.execution.v1 bundles. sealConfidential(snapshot, options) seals a V1 snapshot into commitment envelopes for sensitive fields; verifyConfidential(bundle, openings?) verifies it. V1 uses the same one-way HMAC-SHA256 commitment model, not encryption. New integrations should prefer the V2 path above; see AI Execution V2 and Migration: V1 to V2.
Security Model
Confidential mode guarantees:
- Sealed fields are replaced by one-way commitments; the CER does not contain the plaintext.
- Binding: a disclosed value can be checked against the commitment, and this check does not depend on salt secrecy.
- Openings, when they exist, are never embedded in the CER; they are producer-controlled and shared out of band.
It does NOT guarantee:
- Encryption or confidentiality in transit or at rest beyond the commitment itself; this is a one-way commitment scheme, not a cipher.
- Hiding, if the salt used for a commitment is exposed or guessable. Hiding is entirely dependent on salt secrecy.
- Protection for fields outside the declared confidential set; unsealed fields remain plaintext in the snapshot.
Best Practices
- Always pass
{ confidential: true }explicitly; there is no default-on confidential sealing. - Store openings separately from the CER, with access control matching your disclosure policy.
- Use
decisionRefIndexesto seal only the specific decisionRefs entries that are sensitive; the index binding prevents cross-slot substitution. - Treat salts as secrets. Rotating or leaking a salt affects hiding for that field, not the validity of the commitment itself.