Skip to content

Package Provenance

Package provenance is the evidence trail for a smart-contract package. It answers:

  • who signed this package;
  • which package descriptor they signed;
  • whether the signed descriptor still matches the WASM, manifest, and runtime being instantiated;
  • whether the signer satisfies the network's active provenance policy.

Provenance is not the same as registry approval. Provenance proves origin and integrity for a package. The Contract Registry proves that the network approved that package for deployment.

How It Works

The provenance flow has two parts:

  1. Static package evidence: the package contains noos-contract.json and, after signing, provenance/signatures.json.
  2. Consensus validation: transaction INSTANTIATE_CONTRACT may carry that provenance, and validators check it against the active policy stored in chain state.

The full lifecycle is:

  1. Build the contract WASM.
  2. Generate or review the manifest.
  3. Create noos-contract.json with the WASM hash and manifest hash.
  4. Sign the descriptor.
  5. Store signatures in provenance/signatures.json.
  6. Include the descriptor and signatures in the instantiate payload.
  7. Validators load the active provenance policy.
  8. Validators verify hashes, runtime, signatures, and signer policy.
  9. Accepted provenance is summarized in the contract metadata.

This is why package provenance starts as files instead of a database row. A package needs to be portable and reviewable before a node accepts it. The database records the network policy and the accepted result after instantiation; it does not continuously update the package's signature file.

Package Descriptor

The package descriptor is noos-contract.json. It is the object that signers approve.

json
{
  "schemaVersion": 1,
  "packageName": "report-store",
  "packageVersion": "0.1.0",
  "description": "Stores report metadata and emits report events.",
  "runtime": "wasm-assemblyscript-v1",
  "manifestPath": "manifest.json",
  "wasmPath": "build/contract.wasm",
  "codeHash": "<sha256-of-wasm>",
  "manifestHash": "<canonical-manifest-sha256>",
  "authors": [{ "name": "NOOS", "organizationId": "org-noos" }],
  "license": "UNLICENSED",
  "repository": "https://example.invalid/noos/report-store",
  "keywords": ["reports"],
  "audits": [],
  "metadata": {}
}
FieldMeaningReview guidance
schemaVersionDescriptor format version.Current value is 1. Unknown versions should be rejected unless the network explicitly supports them.
packageNameStable package identity.Should match the intended release, registry entry, and release workflow.
packageVersionVersion of the package identity.Should change when code, manifest, ABI, authorization, or behavior changes.
descriptionHuman review context.Should explain what the contract does clearly enough for review.
runtimeRuntime required by the package.Must match the manifest runtime. Current package provenance supports wasm-assemblyscript-v1.
manifestPathLocation of the manifest inside the package.Should point inside the package directory and match the reviewed file.
wasmPathLocation of the WASM inside the package.Should point inside the package directory and match the reviewed binary.
codeHashSHA-256 hash of the packaged WASM bytes.Recompute it before signing or approving.
manifestHashCanonical hash of the parsed manifest.Recompute it before signing or approving.
authorsResponsible authors or organizations.Use for accountability and review routing.
licenseLicense or release policy context.Should satisfy the deployment environment's policy.
repositorySource repository URL.Should point to the reviewed source when available.
keywordsDiscovery and classification tags.Useful for tooling, not security-critical by itself.
auditsReview or audit references.Use for completed audits, internal reviews, or security sign-off notes.
metadataExtra release context.Use for ticket ids, build ids, environment labels, or review references.

The security-critical fields are package identity, runtime, paths, codeHash, and manifestHash. Changing the descriptor, WASM, manifest, runtime, package name, or package version after signing makes the signed evidence stale.

For manifest hashing details, see Manifests.

Signatures

Package signatures live in:

text
provenance/signatures.json

A package with provenance normally looks like this:

text
contract-packages/report-store-0.1.0/
  noos-contract.json
  manifest.json
  build/
    contract.wasm
  provenance/
    signatures.json

Each signature has this shape:

json
{
  "signerType": "user",
  "signerId": "user-admin",
  "publicKey": "<USER_PUBLIC_KEY_PEM>",
  "algorithm": "ed25519",
  "signedAt": "2026-05-30T00:00:00.000Z",
  "signature": "<BASE64_SIGNATURE>"
}

provenance/signatures.json is a static package artifact. It is written when the package is signed. It is not a live chain table, and it is not rewritten by validators. If the descriptor, manifest, WASM, or signer set changes, regenerate the package evidence and sign again.

Supported signer types are:

Signer typeTypical meaning
userA named developer, owner, or reviewer approved the package.
organizationAn organization-level authority approved the package.
auditorAn internal or external reviewer signed after review.
ciA controlled build or release pipeline signed the descriptor.
operatorAn operator or release engineer approved deployment packaging.

A cryptographically valid signature is necessary but not sufficient. Validators also check the active policy. A valid signature from a disallowed signer type, signer id, or public key is rejected when the policy restricts those fields.

Signing Envelope

NOOSChain verifies signatures over a stable descriptor envelope. Conceptually, the signed object is:

json
{
  "nonce": "0",
  "createdAt": "1970-01-01T00:00:00.000Z",
  "type": "CONTRACT_PACKAGE_DESCRIPTOR",
  "signerPublicKey": "<SIGNER_PUBLIC_KEY>",
  "payload": {
    "descriptor": "<noos-contract.json object>",
    "signature": {
      "signerType": "user",
      "signerId": "user-admin",
      "algorithm": "ed25519",
      "signedAt": "2026-05-30T00:00:00.000Z"
    }
  }
}

The descriptor hash is sha256(canonicalize(descriptor)). Because the descriptor includes codeHash and manifestHash, the signature binds the signer to the exact WASM bytes and canonical manifest that were reviewed.

Active Provenance Policy

The active package provenance policy is network state. Validators load the default row from contract_provenance_policies during transaction INSTANTIATE_CONTRACT.

If no row exists, the default policy is:

json
{
  "mode": "optional"
}

The policy payload is:

json
{
  "id": "default",
  "mode": "required",
  "allowedSignerTypes": ["user", "auditor"],
  "allowedSignerIds": ["user-admin", "auditor-main"],
  "allowedPublicKeys": ["<PUBLIC_KEY_PEM>"],
  "minSignatures": 2,
  "reason": "Require developer and audit approval for production contracts",
  "metadata": {
    "changeControlId": "CHG-1234"
  }
}
FieldMeaning
idThe only accepted policy id is default.
modeoptional allows unsigned instantiation; required rejects missing provenance.
allowedSignerTypesOptional allowlist of signer types. Empty or absent means no restriction by type.
allowedSignerIdsOptional allowlist of signer ids. Empty or absent means no restriction by id.
allowedPublicKeysOptional allowlist of signer public keys. Empty or absent means no key restriction.
minSignaturesMinimum accepted signatures. If absent, required mode needs one accepted signature and optional mode needs zero.
reasonHuman explanation for the policy change.
metadataChange-control, rollout, environment, or review metadata.

The policy is set by transaction SET_CONTRACT_PROVENANCE_POLICY. The transaction requires chain:admin; non-admin submissions fail with CHAIN_PERMISSION_DENIED.

powershell
npm run noos -- tx build-and-submit --type SET_CONTRACT_PROVENANCE_POLICY --payload-file "contract-provenance-policy.json" --signer-public-key "$env:NOOS_CONTRACT_PACKAGE_SIGNER_PUBLIC_KEY" --signer-private-key-path ".secrets/user-admin.private.pem" --yes

This submits transaction SET_CONTRACT_PROVENANCE_POLICY.

Policy updates are consensus state changes in the contract_provenance_policy state-root namespace. Replay and snapshot restore read the same policy rows, so validators do not need a separate local policy file for normal production execution.

Older local tests and replay helpers may still pass a fallback policy through the protocol context. The active chain row wins when present; the fallback is only used when the default policy row does not exist.

Policy Models

ModelPolicy shape
Optional developmentmode: "optional" with no signer restrictions. Unsigned packages can instantiate, but signed packages still record accepted provenance.
Single developer approvalmode: "required", allowedSignerTypes: ["user"], allowedSignerIds set to approved package owners, minSignatures: 1.
CI-built releasesmode: "required", allowedSignerTypes: ["ci"], allowedPublicKeys set to controlled pipeline keys.
Audit approvalmode: "required", allowedSignerTypes: ["auditor"], optionally restricted by auditor id or public key.
Dual approvalmode: "required" and minSignatures: 2, with allowlists covering the approved developer, auditor, CI, or operator signers.

Use public-key allowlists for production when possible. Signer ids are useful for review clarity, but public keys are the cryptographic identity that validators verify.

Instantiate Validation

Transaction INSTANTIATE_CONTRACT can include:

json
{
  "provenance": {
    "descriptor": {
      "schemaVersion": 1,
      "packageName": "report-store",
      "packageVersion": "0.1.0",
      "runtime": "wasm-assemblyscript-v1",
      "codeHash": "<sha256-of-wasm>",
      "manifestHash": "<canonical-manifest-sha256>"
    },
    "signatures": []
  }
}

During deterministic block execution, validators check:

  • the instantiate manifest parses successfully;
  • the actual manifest hash matches the instantiate manifestHash;
  • descriptor codeHash equals the instantiate codeHash;
  • descriptor manifestHash equals the instantiate manifestHash;
  • descriptor runtime equals the manifest runtime;
  • each accepted signature verifies over the descriptor signing envelope;
  • accepted signatures match the active policy's type, id, public-key, and count requirements.

Common deterministic failures are:

Failure codeMeaning
CONTRACT_PROVENANCE_REQUIREDPolicy is required and the instantiate payload did not include provenance.
CONTRACT_PROVENANCE_INVALIDDescriptor/hash/runtime mismatch, invalid signature set, or too few accepted signatures after partial acceptance.
CONTRACT_PROVENANCE_SIGNER_NOT_ALLOWEDNo provided signature both verified and matched the active signer policy.

Accepted provenance is summarized into contract metadata:

json
{
  "contractProvenance": {
    "packageName": "report-store",
    "packageVersion": "0.1.0",
    "descriptorHash": "<sha256>",
    "acceptedSigners": [
      {
        "signerType": "user",
        "signerId": "user-admin",
        "publicKey": "<USER_PUBLIC_KEY_PEM>",
        "signedAt": "2026-05-30T00:00:00.000Z"
      }
    ]
  }
}

Only accepted signer metadata is stored in the contract metadata. The full package files should still be archived for review, audit, and incident response.

Create And Sign A Package

Package a contract:

powershell
npm run contracts:as-sdk:package -- --manifest "apps/noos-contract-sdk-as/examples/typed-report-contract/package/manifest.json" --wasm "apps/noos-contract-sdk-as/build/typed-report-contract.wasm" --out "contract-packages/report-store-0.1.0" --package-name "report-store" --package-version "0.1.0" --description "Stores report metadata and emits report events."

Sign the descriptor:

powershell
$env:NOOS_CONTRACT_PACKAGE_SIGNER_PUBLIC_KEY = Get-Content ".secrets/user-admin.public.pem" -Raw
$env:NOOS_CONTRACT_PACKAGE_SIGNER_PRIVATE_KEY_PEM = Get-Content ".secrets/user-admin.private.pem" -Raw
$env:NOOS_CONTRACT_PACKAGE_SIGNER_TYPE = "user"
$env:NOOS_CONTRACT_PACKAGE_SIGNER_ID = "user-admin"
npm run contracts:as-sdk:sign-package -- --package "contract-packages/report-store-0.1.0"

The signing command writes provenance/signatures.json. The publishing helper can include that provenance in the instantiate payload when publishing the package. See Publishing for the full workflow.

Provenance Review Checklist

Use this checklist before signing a package, accepting another signer's signature, registering a package, or embedding provenance in an instantiate payload.

CheckWhat to verifyWhy it matters
Package identitypackageName, packageVersion, repository, authors, and intended contract id match the release.Registry and release workflows match by package identity.
Version disciplineThe version changed when behavior, ABI, authorization, buckets, migrations, or runtime assumptions changed.Reusing a version makes audits ambiguous.
Runtime matchDescriptor runtime and manifest runtime are identical.Runtime mismatch invalidates deterministic execution assumptions.
WASM hashRecompute SHA-256 over the packaged WASM and compare it with codeHash.Proves the signed descriptor points to the reviewed bytecode.
Manifest hashRecompute the canonical manifest hash and compare it with manifestHash.Proves the signed descriptor points to the reviewed manifest.
Descriptor hashRecord sha256(canonicalize(descriptor)).Registry entries and contract metadata refer to this exact descriptor.
Signature validityVerify each signature over the descriptor envelope.Prevents copied, stale, or malformed signatures.
Signer typeConfirm the signer type is appropriate for the release.A development signer may not be acceptable for production.
Signer idConfirm the signer id is an approved actor for this package.Prevents unrelated actors from approving a package.
Public keyConfirm the public key is expected and current.Prevents plausible signer ids with unknown keys.
Signature countConfirm enough signatures will be accepted by policy.minSignatures may require multi-party approval.
Audit metadataConfirm descriptor audits and metadata point to real review evidence when required.Future incident review needs to know what was reviewed and by whom.
Registry alignmentIf registry approval is required, confirm package name, version, code hash, manifest hash, and descriptor hash match the registry entry.Provenance alone does not approve a package for the network.
Archive evidenceKeep the package directory, hashes, public keys, policy, review notes, and registry entry.Replay and incident response need the original evidence.

Common Rejection Reasons

  • provenance is missing while policy mode is required;
  • descriptor codeHash does not match the instantiate codeHash;
  • descriptor manifestHash does not match the instantiate manifestHash;
  • descriptor runtime does not match the manifest runtime;
  • signature was made over an older descriptor;
  • signer type is not allowed by policy;
  • signer id is not allowed by policy;
  • signer public key is not allowed by policy;
  • minSignatures is higher than the number of accepted signatures;
  • a package version was reused after behavior or manifest changes;
  • provenance exists in the local package but was not embedded in the instantiate payload.

Evidence To Keep

Archive these artifacts with the launch or review record:

  • package directory;
  • noos-contract.json;
  • manifest.json;
  • packaged WASM;
  • provenance/signatures.json;
  • computed descriptor hash;
  • computed WASM hash;
  • computed canonical manifest hash;
  • signer public keys or key registry references;
  • active provenance policy used for validation;
  • policy transaction hash, if policy changed for the release;
  • review notes and audit evidence;
  • registry entry id, if the package is approved in the registry.

Losing the package files does not remove accepted provenance from chain state, but it weakens audit and incident evidence. Keep the signed package because it shows what was reviewed before submission.

Next Steps

Audience-first NOOSChain documentation.