Skip to main content
[AVAILABLE]. packages/shared/src/domain/execution-trust-record.ts, ExecutionTrustRecordBuilder. CLAIMS.md 2.5.

What it is

Every Business Transaction produces exactly one Execution Trust Record: the aggregate of everything Parmana knows about it, built from a chain of immutable artifacts, each referencing the one before it by ID:
Authority           who is empowered to authorize execution
     │              packages/shared/src/domain/authority.ts

Authorization        the grant, with purpose and optional expiry
     │              packages/shared/src/domain/authorization.ts

Intent               the specific action + target + parameters requested
     │              packages/shared/src/domain/intent.ts

Business Transaction the immutable input to policy evaluation
     │              packages/shared/src/domain/business-transaction.ts

Decision              the policy's outcome (APPROVED / REJECTED) + reason + evaluated signals
     │              packages/shared/src/domain/decision.ts

Execution             what actually happened: status, mode, evidence
     │              packages/shared/src/domain/execution.ts

Execution Trust Record  the aggregate: transaction + executions + overrides +
                         verifications + receipts + canonical hash + signature
                       packages/shared/src/domain/execution-trust-record.ts
BusinessTransactionValidator enforces structural consistency across the chain before anything is evaluated, for example, authorization.authorityId must match authority.authorityId, and intent.authorizationId must match authorization.authorizationId (packages/runtime/src/validators/BusinessTransactionValidator.ts:21-37). A Business Transaction that doesn’t hang together structurally never reaches policy evaluation.

Why it exists

Every earlier concept on this site, policy, authorization, gateway verification, credential isolation, produces something. The trust record is where all of it lands: one artifact you, or anyone, can verify independently, without running Parmana at all. It’s the answer to “what actually happened,” not “what was supposed to happen.”

Shape

export interface ExecutionTrustRecord {
  readonly trustRecordId: string;
  readonly businessTransactionId: string;
  readonly transaction: BusinessTransaction;
  readonly overrides: readonly Override[];        // append-only
  readonly executions: readonly Execution[];       // append-only
  readonly verifications: readonly Verification[]; // append-only
  readonly receipts: readonly Receipt[];           // append-only
  readonly trustRecordHash: string;
  readonly signature: Signature;
  readonly createdAt: Date;
  readonly updatedAt: Date;
}

What’s immutable, and what’s append-only

businessTransactionId, metadata, policy, signals, and decision never change after creation. overrides, executions, verifications, and receipts may only grow, nothing is ever edited or removed from them. For overrides and executions, this append-only invariant is what lets a trustRecordHash mean anything: they are inside the signed content, so growing them changes the hash in a verifiable way. verifications and receipts are append-only too, but for a different reason: they are outside the signed content entirely (see below), so their append-only guarantee comes from repository API convention rather than from the signature.

What the hash and signature actually cover

VerificationCrypto.canonicalRecord() (packages/crypto/src/VerificationCrypto.ts:46-68) builds the exact object that gets hashed and signed. It includes six fields: trustRecordId, businessTransactionId, transaction, overrides, executions, and createdAt. hash() and sign() both call it (lines 73-78 and 85-97), so the trustRecordHash and signature cover precisely this content, no more and no less.
verifications and receipts are not part of the hashed content, even though they are append-only fields on the same record (CLAIMS.md 2.5). This is intentional, not an oversight: a Verification (packages/shared/src/domain/verification.ts:44) carries the trustRecordHash it checked as one of its own fields. If verifications were inside the hash they verify, appending a new verification would change the hash, invalidating every verification that came before it. Keeping them outside is what lets the signed content stay stable while an unbounded number of verifications and receipts accumulate against it. The append-only guarantee for verifications and receipts comes from repository API convention, not from being under the signature.
execution.metadata.authorizationId is inside the canonically-hashed content, because it lives under executions. Tampering with it changes the recomputed trustRecordHash and fails verification (CLAIMS.md 2.11, execution-authorization-wiring.test.ts, “trust record references the authorization”).

Minimal example: getting one back

trust_record = client.execution.execute(transaction)     # POST /execute
trust_record = client.trust_records.get(transaction_id)  # GET /trust-records/:id
Real example, captured from a live local run (2026-07-12), trimmed:
{
  "trust_record_id": "cabb41d7-1ab5-4cc9-a950-5c4300c6a826",
  "business_transaction_id": "be045836-0016-4c96-838e-a8934cbe0ee9",
  "executions": [
    {
      "execution_id": "8f5605e9-19ee-48e7-9863-132aa565b037",
      "decision": { "outcome": "APPROVED", "reason": "Vendor payment authorized. ..." },
      "status": "COMPLETED",
      "evidence": {
        "action": "payments:execute",
        "target": "vendor://payments",
        "parameters": { "amount": 1000, "currency": "USD" },
        "success": true,
        "attributes": { "connector": { "connectorId": "vendor-payment", "capability": "payments:execute" } }
      },
      "metadata": { "authorizationId": "7824fffb-dbb2-4046-9436-fbbd0ea777fa" }
    }
  ],
  "verifications": [{ "status": "VERIFIED", "trust_record_hash": "fdb313e8..." }],
  "receipts": [{ "algorithm": "ed25519", "receipt_hash": "5e0ab23f..." }],
  "trust_record_hash": "fdb313e82c7cd86cf5f1dfb0ab90ac72f550a67a91891ebfce9462a73e9da103",
  "signature": { "algorithm": "ed25519", "key_id": "default" }
}
Field names shown in snake_case (Python SDK decode); the wire format from the API is camelCase. Source: python/examples/quickstart/.

Connector evidence extends evidence.attributes, it does not add a new field

ExecutionEvidence.attributes (packages/shared/src/domain/execution-evidence.ts) has always been an open Record<string, unknown> bag, and ExecutionEvidenceBuilder has always forwarded ExecutionResult.metadata into it verbatim, neither changed for this milestone. @parmana/connector-sdk’s SdkConnectorExecutor populates ExecutionResult.metadata.connector with a ConnectorEvidence object (connector ID, version, capability, sanitized endpoint, redacted request/response summaries, a connectorEvidenceHash computed by the existing TrustRecordHasher, no alternative hash), so it lands at execution.evidence.attributes.connector through this same, unmodified path. Records created before connector-sdk existed were serialized, and hashed, without this key; that stored content and hash are never recomputed or migrated, so old and new records verify identically against their own stored hash.

Next

Verify a trust record independently

The third-party verification story, no Parmana runtime required.

Detect tampering

Mutate a record, verify, watch it fail: the negative path.