--- eip: 8338 title: Token-Bound Executable Skills description: An ERC-721 extension binding tokens to hash-verifiable executable skill packages with update control and optional confidentiality. author: Gary Yang (@garyyang-finchip) discussions-to: https://ethereum-magicians.org/t/erc-8338-token-bound-executable-skills/29005 status: Draft type: Standards Track category: ERC created: 2026-07-10 requires: 165, 721 --- ## Abstract This standard binds an [ERC-721](./eip-721.md) token to an executable skill: a package of files, led by a primary Markdown document, that an off-chain runtime (including LLM-based agent runtimes) can fetch, verify byte-for-byte, and execute. ``` ERC-721 Asset Layer ── who owns this skill token tokenId ├── owner ├── approved / operator └── tokenURI (Metadata ext., optional) display identity only Mandatory Skill Binding ── which version it binds; who may update; whether it may change tokenId ├── mdHash plaintext primary-document commitment ├── packageHash published-package commitment (plaintext or ciphertext) ├── version content version ├── packageURI mutable transport hint ├── updateAuthority independent publication right └── frozen permanently immutable content Optional On-chain Document ── read the plaintext primary document from the chain itself tokenId ├── hasOnchainSkillDocument() once true, permanently true └── skillDocument() → exact plaintext primary-document bytes Off-chain Normative Layer ── what the commitments protect, how to fetch and verify packageHash └── SkillRoot ├── md { path, cid } primary document: raw or ciphertext CID ├── manifest public, machine-readable, minimal schema below ├── entry / license / files files may be raw or ciphertext CIDs ├── confidentiality? OPTIONAL confidentiality descriptor └── prev mandatory version chain (absent only at version 1) External Scalable Rights Layer ── companion standards; not part of this interface SkillRef = (chainId, skillContract, skillTokenId) └── ERC-1155 rights contracts: licenses / editions / usage credits ``` Each token defines a canonical skill reference. Identity is the hashes; transport is a replaceable hint; publication is a right separate from ownership; freezing is an irreversible promise; confidentiality is a package property, not a token field. Rental, subscription, metering, delegation, gated upgrades, and license issuance are deliberately left to companion extensions. This proposal standardizes executable-artifact identity, integrity, versioning, and publication governance. It intentionally does not define skill registries, discovery, installation state, usage attestations, reputation, access rights, licensing, or settlement. ## Motivation AI agents have on-chain identities ([ERC-8004](./eip-8004.md)) and smart accounts ([ERC-4337](./eip-4337.md)), but the skills they run live in centralized registries: unverifiable, unownable, silently mutable — the root condition of registry supply-chain attacks. `tokenURI` cannot fix this; it is a display pointer with no integrity guarantee. Registry and package-manager protocols can identify, locate, and distribute a skill, but an execution runtime must answer a lower-level question: which exact document, file graph, package version, and publication history does a token commit to? Without a shared artifact-binding model, two clients may resolve the same registered skill while receiving different companion files, following different update histories, or interpreting confidentiality metadata differently. This proposal standardizes that artifact boundary independently of any particular registry, marketplace, agent identity system, or runtime. Two hashes and a URI alone would be custom metadata. What makes this a standard is the pair of normative definitions beneath them: `packageHash` commits to a deterministic content-addressed object graph (the SkillRoot) covering every file the skill's execution depends on, and `mdHash` commits to the plaintext primary document within it. Together they form a verifiable binding between a token and an executable artifact — publishable in the clear or as ciphertext, readable on-chain where the publisher chooses, and referenced by scalable rights systems built on top. A skill's token reference is singular; its usage value must be replicable, distributable, tiered, and metered. This standard defines the former. Multi-token rights contracts (a companion standard) reference it via `(chainId, skillContract, skillTokenId)` and handle the latter: **[ERC-721](./eip-721.md) makes a skill identifiable; [ERC-1155](./eip-1155.md) makes the skill economy scalable.** ## Specification The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174). ### Core interface (mandatory) ```solidity interface ISkillToken /* standalone extension interface; see Compliance */ { struct SkillBinding { bytes32 mdHash; // SHA-256 digest of the plaintext primary Markdown document bytes32 packageHash; // SHA-256 digest of the encoded SkillRoot as published uint64 version; // content version, starts at 1 } event SkillUpdated(uint256 indexed tokenId, bytes32 mdHash, bytes32 packageHash, uint64 version); event SkillURIUpdated(uint256 indexed tokenId, string packageURI); event SkillUpdateAuthorityChanged(uint256 indexed tokenId, address indexed previousAuthority, address indexed newAuthority); event SkillFrozen(uint256 indexed tokenId); function skillOf(uint256 tokenId) external view returns (SkillBinding memory); function skillURI(uint256 tokenId) external view returns (string memory); function updateAuthorityOf(uint256 tokenId) external view returns (address); function isSkillFrozen(uint256 tokenId) external view returns (bool); function updateSkill(uint256 tokenId, bytes32 mdHash, bytes32 packageHash) external; function setSkillURI(uint256 tokenId, string calldata packageURI) external; function setUpdateAuthority(uint256 tokenId, address newAuthority) external; function freezeSkill(uint256 tokenId) external; } ``` Confidentiality adds no field here: encryption is described inside the hash-protected package, not as a token flag. License supply, pricing, and mint mechanics likewise never enter this interface. ### On-chain document interface (optional) ```solidity interface IOnchainSkillDocument /* own ERC-165 id */ { /// @notice Whether a plaintext on-chain copy of the primary document exists for tokenId. /// @dev MUST revert for nonexistent tokenId. Once true, MUST remain true permanently. function hasOnchainSkillDocument(uint256 tokenId) external view returns (bool); /// @notice The exact plaintext UTF-8 bytes of the primary Skill document. /// @dev MUST revert when hasOnchainSkillDocument(tokenId) == false. function skillDocument(uint256 tokenId) external view returns (bytes memory document); /// @notice Atomically update the on-chain document together with the binding. /// @dev mdHash is computed in-contract as sha256(document). function updateSkillWithDocument(uint256 tokenId, bytes calldata document, bytes32 packageHash) external; /// @notice Publish the current primary document on-chain without a version change. /// @dev MUST revert unless sha256(document) == skillOf(tokenId).mdHash. /// MUST NOT change packageHash or version. Sets hasOnchainSkillDocument permanently true. function publishSkillDocument(uint256 tokenId, bytes calldata document) external; event SkillDocumentPublished(uint256 indexed tokenId, bytes32 mdHash); } ``` `skillDocument` returns `bytes`: the commitment is over exact raw bytes. When a copy exists, `sha256(skillDocument(tokenId))` MUST equal `skillOf(tokenId).mdHash` at all times. **Existence is permanent.** `hasOnchainSkillDocument` MAY transition from `false` to `true` — via `publishSkillDocument` (disclosing the current document without touching `packageHash` or `version`, since the package itself is unchanged) or via `updateSkillWithDocument` (when the document content actually changes) — and MUST NOT ever transition back: publishing the primary document on-chain is an irreversible disclosure, and the interface state reflects that. A token that has once published its document on-chain may later encrypt companion files, but its primary document can never return to genuine confidentiality (see Security Considerations). **Consistency with updates.** When `hasOnchainSkillDocument(tokenId) == true` and a proposed update would change `mdHash`, plain `updateSkill` MUST revert; the update MUST go through `updateSkillWithDocument`, which computes `mdHash = sha256(document)` in-contract and updates document, both commitments, and `version` in one transaction. **The on-chain document is exclusively the primary Markdown document.** No other package file may be published through this extension; the `sha256(document) == mdHash` invariant makes this structural rather than procedural. **The on-chain document is always plaintext**: for skills whose primary document is confidential, `hasOnchainSkillDocument` MUST return `false` and `skillDocument` MUST revert. Storage technique is implementation-defined and invisible to callers. An on-chain document never replaces `mdHash`. ### Compliance - The contract MUST implement ERC-721 and [ERC-165](./eip-165.md). - `supportsInterface` MUST return `true` for `ISkillToken`, and additionally for `IOnchainSkillDocument` when implemented. The interfaces deliberately do not inherit `IERC721`. - A compliant contract SHOULD implement the ERC-721 Metadata extension. If `tokenURI` is implemented, it MUST NOT be used as a verification source; it MAY mirror facts such as `encrypted: true` or `document_onchain: true` for wallets, without authority. ### Identity versus transport The skill's identity is `(mdHash, packageHash, version)`. `packageURI` is a transport hint and is **not** part of the identity: - `setSkillURI` MUST NOT change `version` and MUST emit `SkillURIUpdated`. Replacing a dead endpoint never constitutes a new skill version. `packageURI` is a single URI; multi-mirror retrieval is expressed by URI resolvers or off-chain retrieval manifests, not by this field. - `packageURI` MUST NOT be treated as a trust input: consumers MUST verify all retrieved content against the commitments and MUST discard non-matching content, whatever its source. ### Update authority Publication is a right separate from ownership: - `updateAuthorityOf(tokenId)` is initialized at mint. Only the current update authority may call `updateSkill`, `updateSkillWithDocument`, `setSkillURI`, `setUpdateAuthority`, and `freezeSkill`. - `setUpdateAuthority(tokenId, address(0))` MUST revert: abandoning maintenance is expressed by `freezeSkill`, never by burning the authority — a zero authority would leave `packageURI` permanently unmaintainable without setting `frozen`. - `ownerOf(tokenId)` does not automatically equal the update authority; ERC-721 transfers MUST NOT change the update authority; ERC-721 `approved`/operator approvals MUST NOT confer any of these powers. ### Freezing - `freezeSkill` MUST be irreversible and MUST emit `SkillFrozen`. Once frozen, `updateSkill` and `updateSkillWithDocument` MUST revert forever. - Freezing binds content — `mdHash`, `packageHash`, `version` — not transport: `setSkillURI` and `setUpdateAuthority` remain callable, so a frozen skill's retrieval hints can outlive any storage provider. ### Existence and update behavior - `skillOf`, `skillURI`, `updateAuthorityOf`, `isSkillFrozen` on a nonexistent `tokenId` MUST revert. - At mint the binding MUST be fully populated, `version` MUST equal 1, and the contract MUST emit `SkillUpdated` and `SkillUpdateAuthorityChanged(tokenId, address(0), initialAuthority)`. - `mdHash` and `packageHash` MUST NOT be `bytes32(0)`. - Updates MUST set `mdHash` and `packageHash` atomically in one call. The new `packageHash` MUST differ from the current one. `mdHash` MAY remain unchanged when the primary document is unchanged — editing companion files or rotating encryption keys legitimately changes only `packageHash`. - Callers never pass `version`: the contract increments it by exactly 1 per successful update and MUST revert on `uint64` overflow. Versions are append-only. ### Hash definitions (normative) - `mdHash = SHA-256(plaintext primary Markdown document raw bytes)`. No line-ending, whitespace, BOM, or Unicode normalization; no salting or keying of any kind. The commitment is always to the exact plaintext, even when the published copy is encrypted. - `packageHash = SHA-256(deterministically encoded SkillRoot bytes)` — the package **as published**, which under confidentiality means the ciphertext objects. It is never the hash of a zip, tar, or `.skill` archive. ### Package paths (normative) All paths in this standard (`md.path`, `entry.path`, `files` keys, confidentiality `objects` keys) MUST be relative, case-sensitive UTF-8 POSIX paths. Paths MUST NOT begin with `/`; MUST NOT contain `\`, empty segments, or the segments `.` or `..`. No path normalization is performed: paths match byte-for-byte or not at all. ### SkillRoot (normative off-chain object) SkillRoot MUST be encoded as deterministic DAG-CBOR per the DAG-CBOR specification and RFC 8949 core deterministic encoding: definite lengths, shortest-form integers, and map keys sorted by the bytewise lexical order of their **encoded** bytes (string keys therefore sort effectively length-first due to the CBOR length prefix). ```cddl skill-root = { "md": md-entry, ; primary document "manifest": link, ; machine-readable self-description; MUST be public "entry": entry, ? "license": license-entry, ; legal/commercial terms ? "files": { * tstr => link }, ; remaining files; raw or ciphertext CIDs ? "confidentiality": link, ; OPTIONAL confidentiality descriptor; absent = fully public package ? "prev": link ; version chain, rules below } md-entry = { "path": tstr, "cid": link } license-entry = { "path": tstr, "cid": link } entry = { "path": tstr, "cid": link, "profile": tstr } link = #6.42(bytes .size (37)) ; DAG-CBOR link: tag 42 over a byte string of exactly 37 bytes: ; 0x00 identity multibase prefix ; 0x01 CIDv1 ; 0x71 (dag-cbor) / 0x55 (raw) codec varint ; 0x12 0x20 sha2-256 multihash prefix ; 32-byte digest ``` - Optional fields MUST be omitted when absent; `null` MUST NOT appear. SkillRoot, `md-entry`, `license-entry`, and `entry` are closed maps (extensibility lives in the manifest). - **Primary document**: a SkillRoot has exactly one primary document. `md.path` names it within the package namespace and MUST NOT appear as a key of `files`. Likewise `license.path`, when present, names the license file, MUST differ from `md.path`, and MUST NOT appear as a key of `files` — every addressable object in the SkillRoot carries an explicit path, so confidentiality objects resolve unambiguously; `md.cid` MUST identify the exact published bytes of that document. The publisher MAY choose the filename; `SKILL.md` SHOULD be used as the conventional default. The document MUST be UTF-8 encoded text and SHOULD be Markdown; its path SHOULD end in `.md` or `.markdown`, and consumers MUST NOT determine document type from the extension alone. - **Version chain (`prev`)**: at `version == 1`, `prev` MUST be omitted; at `version > 1`, `prev` MUST be present and the digest inside its CID MUST equal the `packageHash` of the immediately preceding version. - If `entry.path == md.path`, `entry.cid` MUST equal `md.cid`; otherwise `files[entry.path]` MUST exist and equal `entry.cid`. - `entry.profile` is an opaque identifier of the execution environment; unknown profiles MUST NOT be executed by guesswork. - **Package boundary**: everything execution depends on MUST be reachable from the SkillRoot; unreachable content enjoys no protection under `packageHash`. ### Manifest (normative minimal schema) The manifest is a public, machine-readable JSON document. REQUIRED fields: `schemaVersion`, `name`, `skillVersion`, `summary`, `entrypoint`. `manifest.entrypoint` MUST equal `SkillRoot.entry.path`. Consumers MUST ignore unknown manifest fields; extension fields SHOULD use namespaced keys (e.g. `x-vendor/*`). Human-readable version strings live here (`skillVersion`), never on-chain. ### Confidentiality descriptor (optional, in-package; normative schema) When the `confidentiality` link is present, it MUST resolve to a **public** JSON descriptor. REQUIRED fields: `schemaVersion`, `mode`, `profile`, `objects`. `policyHash` and `keyManagement` are profile-specific: their presence and semantics are defined by the declared profile, not by this standard. ```json { "schemaVersion": "1.0", "mode": "encrypted", "profile": "x-lit-threshold-access-v1", "objects": { "SKILL.md": { "ciphertext": "bafy…", "plaintextHash": "0x…" }, "src/main.py": { "ciphertext": "bafy…", "plaintextHash": "0x…" } }, "keyManagement": { "type": "threshold-network" } } ``` - Every encrypted object MUST appear in `objects` under its package path (per the path rules above). `objects[path].ciphertext` MUST equal the corresponding CID in the SkillRoot: `md.cid` when `path == md.path`, `license.cid` when a license entry exists and `path == license.path`, and `files[path]` otherwise. `plaintextHash` MUST be the 32-byte SHA-256 digest of the plaintext, hex-encoded. - If the primary document is encrypted, `objects[md.path].plaintextHash` MUST equal the token's `mdHash`. - `profile` is an opaque identifier of the confidentiality scheme. A runtime encountering an unknown confidentiality profile MUST NOT attempt to obtain keys or decrypt by guesswork. - The manifest and the confidentiality descriptor themselves MUST remain unencrypted. - Partial encryption is expected and legitimate (e.g. public `SKILL.md`, manifest, and examples; encrypted source and configuration). - Key rotation re-encrypts content, changing ciphertext CIDs and therefore `packageHash`: this MUST be published as a version update. Plaintext-unchanged rotation leaves `mdHash` unchanged — the version history makes this legible. - Execution-privacy targets (TEE, FHE, ZK-proof profiles) are distinct from storage encryption and MAY be declared in manifest extension namespaces; standardizing them is future work. ### Verification **Public mode** (no `confidentiality` link) — a verifier MUST: read `skillOf(tokenId)`; retrieve the SkillRoot bytes from any source; check `SHA-256(bytes) == packageHash`; decode, re-encode under the deterministic rules, and check byte equality; check the schema, path rules, and version-chain rules; check `digest(md.cid) == mdHash`; retrieve leaves as needed, checking each against its CID; deliver only fully verified content. **Confidential mode** — the same through schema checking, then: verify each retrieved ciphertext against its CID (integrity of the published package needs no keys); confirm execution rights through whatever rights layer applies; obtain keys per the declared profile; decrypt; check `sha256(plaintext primary document) == mdHash` and each decrypted object against its `plaintextHash`; deliver only fully verified plaintext to the runtime. In both modes, partial retrieval (root → manifest → primary document → full package) is a first-class workflow. Runtimes MUST pin the versions they execute by hash, MUST NOT automatically follow updates, and SHOULD require explicit re-authorization after observing `SkillUpdated`. ### Scalable rights (companion standards) This interface defines one canonical skill reference per token. Scaling distribution is the job of rights contracts — typically [ERC-1155](./eip-1155.md) — that reference the skill externally: ```solidity struct SkillRef { uint256 chainId; address skillContract; uint256 skillTokenId; } ``` Duplicating the ERC-721 skill token itself for sales is NOT RECOMMENDED: copies fragment the reference, version stream, and update authority. Sell rights that point at the skill, not copies of the skill. The rights interface (`skillRefOf`, issuance, supply, pricing) is deliberately outside this standard. ## Rationale **Two hashes and a URI alone would be metadata; the normative SkillRoot is what makes this a binding standard.** The chain holds commitments; the object graph defines exactly what they commit to, byte for byte. **Why `packageHash` must change on every update but `mdHash` may not.** Every update publishes a new package (new SkillRoot, new `prev` link); but the primary document legitimately survives edits to companion files and key rotations. **Why the primary document carries an explicit path.** Confidentiality objects, entry resolution, and tooling all address files by path; leaving the primary document's path implicit would smuggle a naming convention into a byte-precise standard. `md { path, cid }` mirrors `entry { path, cid, profile }`. **Why the on-chain document is permanent once published.** Chain history preserves the bytes regardless of later state; an interface that could report `false` after a disclosure would misrepresent reality. The monotone rule makes the state truthful. **Why zero-address authority is forbidden.** Abandonment has a dedicated, honest expression — `freezeSkill` — which sets the flag consumers actually check. A zero authority would strand transport maintenance while leaving `frozen == false`, the worst of both. **Why confidentiality is a package property, not a token flag.** A boolean cannot express which files are encrypted, under which scheme, with which custody, or how keys rotate — and a token field would sit outside the hash protection. A buyer can verify the published ciphertext package before paying; verifying plaintext against `mdHash` necessarily follows decryption. **Why `mdHash` stays mandatory even with an on-chain document.** The hash is the document's identity — linking the on-chain copy, the in-package copy (or its `plaintextHash` under encryption), and every version comparison to the same document. **Transport outside identity; publication outside ownership; freezing in the kernel, gates in extensions; contract-incremented versions** — gateways die without version bumps; marketplaces trade without publishing; an irreversible freeze costs one boolean; counters owned by contracts make monotonicity a property, not a convention. **Why rights scale in a companion layer.** ERC-721 answers *which skill this is*; ERC-1155 rights contracts answer *who holds which class of rights, and how many*; execution-rights extensions answer *who may run it now*; confidentiality profiles answer *how content is obtained after rights are proven*. **Relation to prior art.** [ERC-4907](./eip-4907.md) is the formal model as a minimal 721 extension but governs usage roles, not what the token is. [ERC-5169](./eip-5169.md) attaches contract-level client scripts; here each token binds a content-addressed, versioned, per-file-verifiable executable artifact. ERC-8004 anchors who an agent is; this standard anchors what a capability is. **Relationship to skill and tool registries.** An ERC-721-based agent skill registry has separately been proposed (in draft review at the time of writing) defining skill-manifest commitments and agent-skill installation and usage attestations. [ERC-8257](./eip-8257.md) defines a mapping-based Agent Tool Registry with canonical tool metadata, creator and origin binding, and optional predicate-based access control. Both proposals operate primarily at the registration, discovery, access, or attestation layers. This proposal does not define any of those layers. It defines the deterministic executable artifact and version history bound to an ERC-721 token: registry-level commitments establish where an artifact is described; this proposal additionally standardizes what exact executable package, file graph, plaintext primary document, and version history a token commits to. Because that registry proposal represents each registered skill as an ERC-721 token, such a registry can also implement this interface for the same token identifier. In that composition, the registry supplies registration, discovery, installation, and attestation semantics, while this proposal supplies package identity, per-file integrity, version chaining, publication authority, freezing, and optional confidentiality. ERC-8257 does not require ERC-721 representation; an ERC-8257 tool manifest can instead reference a token-bound executable artifact as an external artifact dependency. A registry manifest hash, an ERC-8257 metadata hash, and this proposal's `packageHash` commit to different objects and are not interchangeable. ### Lifecycle example (informative) A creator with `my-skill/` (SKILL.md, manifest.json, runtime.yaml, src/main.py, config/schema.json) runs a packaging tool that: reads all file bytes → (optionally encrypts selected files and writes the confidentiality descriptor) → computes leaf CIDs → builds the SkillRoot (linking `prev` to the previous version's `packageHash` when updating) → computes `packageHash` over its canonical bytes and `mdHash` over the plaintext primary document → uploads to content-addressed storage → mints or updates with `(mdHash, packageHash, packageURI)`. A consumer's agent, given `chainId + contract + tokenId`: checks ERC-165 → reads `skillOf`/`skillURI` → fetches and verifies the SkillRoot → in public mode verifies and loads content directly; in confidential mode verifies ciphertext, proves rights, obtains keys per the declared profile, decrypts, and verifies plaintext hashes → builds a sandboxed environment per `entry.profile` → runs the skill. Tooling can wrap this as `skill install|verify|run eip155:1/0xSkill/42` — tool-layer conveniences, not contract functions. ## Backwards Compatibility Fully compatible with ERC-721 infrastructure: ownership, approvals, and transfers are untouched. Wallets unaware of this extension display skill tokens as ordinary NFTs; they will not display update authority, frozen status, on-chain documents, or confidentiality facts. ## Reference Implementation Provided in the assets of this proposal: Solidity reference contracts (`ISkillToken`, `IOnchainSkillDocument`, and a self-contained `SkillToken`); the two ERC-165 interface identifiers (compiler-verified: `ISkillToken = 0x734553a6`, `IOnchainSkillDocument = 0x7050dd2c`); JSON Schemas for the manifest and the confidentiality descriptor; path-validation negative tests; a Foundry suite asserting every MUST clause, including the on-chain-document atomic update; and six frozen test vectors for the deterministic encoding — public v1, a v2 update changing only a companion file (constant `mdHash`), a confidential package, an encrypted key-rotation update, a license-encrypted package, and a custom primary-document path — so independent implementations can be checked byte-for-byte. ## Security Considerations **Updates are the attack surface.** Substitution beneath an unchanged hash is computationally infeasible; risk concentrates in the update functions. Mitigations: the independent update authority (never reachable via ERC-721 approvals), irreversible freezing, append-only versions with a closed `prev` chain, runtime pinning norms. Companion extensions add timelocked and attested gates. **Buyers must inspect publication control.** ERC-721 ownership transfer does not transfer update authority. Purchasers must inspect `updateAuthorityOf` and `isSkillFrozen` before acquisition: buying a token whose update authority remains with a third party means buying an asset whose content that party can still change. **Skill content is untrusted input.** Prompt injection targets LLM runtimes through the primary document. This standard anchors integrity, not benignity: runtimes must sandbox execution with least-privilege tooling; vetting belongs to attestation and reputation layers. **On-chain plaintext is irreversible disclosure.** A document published via the on-chain document extension can never be re-secreted; the permanent `hasOnchainSkillDocument` state reflects this. Converting a skill to encrypted mode in a later version does not erase historical plaintext from chain history. **Plaintext commitments reveal equality.** A public `mdHash` (and `plaintextHash` entries) confirms content equality: an adversary who can guess a document's exact bytes can confirm the guess against the hash. Publishers of short or highly predictable confidential documents should weigh this exposure; salted or private commitment schemes are outside this standard and left to future extensions. **Ownership is not confidentiality — and confidentiality is not this standard's key custody.** Public packages are downloadable by anyone knowing the CID. Confidential packages anchor ciphertext integrity on-chain, but key release depends on the declared profile's custodian: its availability, honesty, and revocation behavior are outside this standard and must be documented by implementations. A compromised custodian can leak plaintext but can never alter the skill undetected. **Key rotation and stale ciphertext.** Rotation must surface as a version update; availability of old plaintext to consumers pinned on old versions is a custodial policy question, not an integrity one. **Unprotected content.** Only content reachable from the SkillRoot is protected; out-of-graph files are not part of the skill. **Availability.** Anchors outlive any storage provider; publishers should store redundantly; frozen skills retain a live `setSkillURI` precisely so transport can outlive providers. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md).