Sign in to edit tickets from this page.

← all tickets · home

Remove Benac-specific “canonical” terminology from source and align package code with the package-document CID model

rejected 40355cfe-77e2-4473-a0f0-3e84b1175f45

created_at
2026-05-03
updated_at
2026-05-03
code_context
Known source/tooling hits include crates/benac-core/src/package.rs (`PackageCanonicalImplementation`, `PackageCanonicalPayload`, `canonical_payload()`), crates/benac-core/src/drisl.rs (`decode_canonical`, `canonical_string_for_binary`), crates/benac-core/src/cid.rs (`cid_parser_round_trips_canonical_string`), crates/benac-core/src/claim.rs (`claim_signing_uses_canonical_drisl_bytes`), crates/benac-conformance/src/hello_world_suite.rs, browser/PWA comments, CLI comments, and generated fixtures/capsules. Exclude docs/, records/, target/, node_modules/, and dist/ when source-gating.
labels
terminology, package_model, drisl, cid, conformance, security
resolved_at
2026-05-03
resolution
rejected

Body

Background

The requirements and terminology have been updated to avoid using canonical as a Benac-specific concept. The terms now instruct implementers to describe the actual mechanism instead: DRISL bytes produced by the required encoding rules, payload used to compute a CID, payload used to verify a signature, required CID string form, JSON projection, logical Benac object, normalized output, or validated output.

The source still uses canonical throughout core package identity, DRISL/CID handling, signed claims, conformance checks, browser runtime comments, CLI sync comments, and tooling. This creates a misleading conceptual layer and hides the actual mechanisms.

The most important instance is the current package model:

The revised package model should not work this way. The package CID should be computed directly from the package document. Fields that are not part of package identity should not be fields of the package document.

Goals

Refactor source terminology and package modeling so that:

  1. No Benac-specific source symbol, comment, test, conformance check, fixture, or tooling message uses canonical.
  2. The package CID is computed directly from the package document.
  3. Fields not intended to affect package identity are not fields of the package document.
  4. Implementation snapshots remain stable executable identities.
  5. Artifact binding and snapshot-substitution protections remain in force.
  6. Runtime and capability checks use package-bound artifact CIDs, not convenience projections.
  7. Source and conformance language matches the updated requirements and terms.

Non-goals

This ticket does not weaken package identity, implementation identity, artifact validation, capability mediation, or snapshot consistency checks.

This ticket does not reintroduce legacy JSON hashing or bencid:v0.

This ticket does not require backwards compatibility with old package documents that embedded claim_refs, implementations[*].cid, or blob_cid, unless a separate migration shim is explicitly added.

This ticket does not attempt to remove ordinary platform/Rust meanings such as Path::canonicalize() where that is the correct API name. Those should be explicitly allowlisted by any source gate.

Required code changes

1. Replace the PackageManifest / PackageCanonicalPayload split

Current shape:

pub struct PackageManifest {
    // includes claim_refs, implementations[*].cid, implementations[*].blob_cid, etc.
}

pub struct PackageCanonicalPayload { ... }

pub fn canonical_payload(&self) -> PackageCanonicalPayload

Replace with a package object whose fields are exactly the package document used for package CID computation. Suggested internal type name:

pub struct PackageDocument {
    pub object_type: String,
    pub schema_version: String,
    pub label: String,
    pub version_label: String,
    pub interfaces: Vec<String>,
    pub schemas: PackageSchemas,
    pub implementations: Vec<PackageImplementationEntry>,
    pub declared_capabilities: Vec<String>,
    pub forbidden_capabilities: Vec<String>,
    pub fixtures: Vec<serde_json::Value>,
}

The package document must not contain:

Then compute package CID directly from the package document:

impl PackageDocument {
    pub fn cid(&self) -> BenacResult<Cid> {
        let value = serde_json::to_value(self)
            .map_err(|e| BenacError::new("benac.error.invalid_package_document", e.to_string()))?;
        Ok(Cid::from_drisl_bytes(&encode_logical_payload(&value)?))
    }
}

Remove:

For minimal schema churn, the JSON schema version may temporarily remain benac.package_manifest.v1, but the internal Rust type should be named around the actual requirement: PackageDocument.

2. Replace PackageImplementation with PackageImplementationEntry

Current shape:

pub struct PackageImplementation {
    pub id: String,
    pub kind: String,
    pub cid: Cid,
    pub artifact_cids: Vec<Cid>,
    pub blob_cid: Option<Cid>,
}

Replace with something like:

pub struct PackageImplementationEntry {
    pub id: String,
    pub kind: String,
    pub artifact_cids: Vec<Cid>,
    pub runtime_requirements: Vec<String>,
    pub parameters: serde_json::Value,
}

If runtime_requirements and parameters affect execution, they must be package-document fields, not behavior introduced later by an implementation snapshot.

Remove from package implementation entries:

Add helpers:

impl PackageImplementationEntry {
    pub fn validate_artifacts(&self) -> BenacResult<()> { ... }
    pub fn wasm_artifact_cid(&self) -> BenacResult<&Cid> { ... }
}

For wasm_abi.v0:

For declarative_transform.v0:

Unknown implementation kinds fail closed unless an enabled extension profile supports them.

3. Generate implementation snapshots from package entries

Current code relies on PackageImplementation.cid as a forward reference to an implementation snapshot document.

New model:

impl ImplementationSnapshot {
    pub fn from_package_entry(
        package_cid: Cid,
        entry: &PackageImplementationEntry,
    ) -> Self {
        Self {
            object_type: "implementation_snapshot".to_string(),
            schema_version: "benac.implementation_snapshot.v1".to_string(),
            package_cid,
            implementation_entry_id: entry.id.clone(),
            implementation_type: entry.kind.clone(),
            artifact_cids: entry.artifact_cids.clone(),
            runtime_requirements: entry.runtime_requirements.clone(),
            parameters: entry.parameters.clone(),
        }
    }

    pub fn validate_against_package_entry(
        &self,
        package_cid: &Cid,
        entry: &PackageImplementationEntry,
    ) -> BenacResult<()> {
        ...
    }
}

Snapshot validation must compare:

This is important: if the package document no longer carries implementations[*].cid, the snapshot must not be allowed to introduce runtime requirements or parameters that were absent from the package document.

4. Update capsule import

Capsule validation should:

  1. Validate each package document.
  2. Compute the package CID directly from the package document.
  3. For each implementation entry, derive the expected implementation snapshot.
  4. Compute the expected implementation CID from that snapshot.
  5. If capsule includes implementation snapshot documents, verify that the matching snapshot document equals the derived snapshot.
  6. Ensure required implementation snapshot CIDs are present in the integrity manifest or generated/recorded according to the capsule profile.
  7. Count signed claims as separate capsule/provenance/catalog documents, not as package-document fields.

Package documents containing these fields should be rejected under the baseline package schema:

A migration shim may exist outside the baseline validator, but baseline import should reject them.

5. Update runtime and capability checks to use artifact CIDs

Replace runtime use of:

implementation.blob_cid

with:

implementation.wasm_artifact_cid()

or equivalent extraction from artifact_cids.

Effect/evidence records may still say blob_cid when the actual thing read is a blob, but the value must be sourced from the package document’s artifact_cids, not from a convenience field.

Areas to review:

6. Rename DRISL/CID helpers and tests

Suggested rename table:

CurrentReplace with
decode_canonicaldecode_drisl or decode_required_drisl
canonical_string_for_binaryrequired_cid_string_from_binary
cid_parser_round_trips_canonical_stringcid_parser_round_trips_required_string_form
drisl_cid_link_round_trip_decodes_canonical_stringdrisl_cid_link_round_trips_required_cid_string
drisl_map_order_is_canonicaldrisl_map_keys_use_required_order
drisl_rejects_non_canonical_uint_encodingdrisl_rejects_non_minimal_uint_encoding
cid_from_drisl_bytes_matches_for_canonical_payloadcid_from_drisl_bytes_verifies_logical_payload
claim_signing_uses_canonical_drisl_bytesclaim_signing_uses_drisl_bytes_from_required_encoding_rules
check_signed_claim_signs_drisl_bytes_not_canonical_jsoncheck_signed_claim_signs_drisl_bytes_not_json_bytes
check_couch_metadata_excluded_from_canonical_payloadcheck_couch_metadata_excluded_from_logical_payload_for_cid
check_package_canonical_payload_excludes_claim_refs_structurallyremove or replace with check_package_document_rejects_claim_refs
check_package_canonical_payload_excludes_implementation_snapshot_cidsremove or replace with check_package_document_rejects_implementation_snapshot_refs

7. Rewrite comments, diagnostics, and user/tooling output

Use mechanism-specific wording.

Replace canonical DRISL bytes with:

Replace canonical payload with:

Replace canonical package identity with:

Replace canonical CID string with:

Replace non-canonical CBOR with the actual reason:

Replace canonical output mapping with:

8. Update generated fixtures/capsules

Regenerate or update:

The package CID should remain stable if the new PackageDocument is byte-for-byte equivalent to the old projected package payload after DRISL encoding.

Implementation CIDs may remain stable only if the derived snapshot fields are unchanged. If runtime_requirements and parameters are newly added to package entries and were already present in snapshots, the resulting package CID may change. That is acceptable if recorded in the fixture update.

9. Add a source grep gate

Add or update a CI/source gate so this does not creep back.

Suggested rule:

For tooling that needs to search for banned legacy symbols, avoid embedding the literal word in comments/messages where practical.

Acceptance criteria

  1. rg -i canonical over source/tooling, excluding docs/, records/, target/, node_modules/, and dist/, produces no Benac-specific hits.
  2. PackageCanonicalPayload, PackageCanonicalImplementation, and canonical_payload() no longer exist.
  3. Package CID is computed directly from the DRISL-encoded package document.
  4. The baseline package document schema rejects:
    • claim_refs
    • implementations[*].cid
    • implementations[*].blob_cid
  5. Claims remain importable/exportable as separate capsule/provenance/catalog documents and do not change package CID.
  6. Implementation snapshots are derived from package entries and validate all behavior-relevant fields:
    • package_cid
    • implementation_entry_id
    • implementation_type
    • artifact_cids
    • runtime_requirements
    • parameters
  7. WASM execution and local_blob_read_exact capability checks use the WASM artifact CID from artifact_cids[0].
  8. Declarative implementations reject raw blob_cid fields and require exactly one DRISL artifact CID.
  9. WASM implementations require exactly one raw artifact CID.
  10. Existing artifact-substitution, snapshot-substitution, and capability-scope conformance tests still pass after renaming/rewording.
  11. New tests prove:
    • changing any package document field changes package CID;
    • adding a separate claim document does not change package CID;
    • package docs with claim_refs are rejected;
    • package docs with implementation snapshot forward refs are rejected;
    • package docs with blob_cid convenience fields are rejected;
    • runtime requirements/parameters mismatch between package entry and implementation snapshot is rejected.
  12. cargo test --workspace passes.
  13. Generated requirements and glossary still build, and Canonical appears only as an avoid-term in generated terminology plus explicitly allowed external-standard quotations.

Notes

Do not treat this as a cosmetic rename only. The core package-model cleanup is part of the ticket:

History (4 events)

Sign in as a human to drive this ticket from the page, or use the MCP tools.

← all tickets · home