From 7d4f5598976af7e9b27729cbbba433a6019bbe3f Mon Sep 17 00:00:00 2001 From: Skullcoin <116502774+proofofgame@users.noreply.github.com> Date: Sat, 10 Jan 2026 18:40:04 +0300 Subject: [PATCH 1/3] Create sip-enft-private-layer.md --- sip-enft-private-layer.md | 197 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 sip-enft-private-layer.md diff --git a/sip-enft-private-layer.md b/sip-enft-private-layer.md new file mode 100644 index 00000000..b3048ce2 --- /dev/null +++ b/sip-enft-private-layer.md @@ -0,0 +1,197 @@ +# SIP-XXX: Encrypted NFTs - Standard Trait for Commitment-Based Private Layers (eNFT) + +## Preamble + +- **SIP**: SIP-XXX +- **Title**: Encrypted NFTs - Standard Trait for Commitment-Based Private Layers (eNFT) +- **Authors**: Xenitron nortinex@gmail.com +- **Status**: Draft +- **Consideration**: Technical +- **Type**: Standard +- **Layer**: Traits +- **Created**: 2026-01-10 +- **License**: CC0-1.0 +- **Sign-off**: +- **Discussions-To**: https://forum.stacks.org/t/encrypted-nfts-on-stacks-a-standard-trait-for-commitment-based-private-layers-enft/18560 + +# Abstract + +This SIP proposes a simple, composable trait for “Encrypted NFTs” (eNFTs): NFTs that commit on-chain to a private off-chain payload (a “private layer”) while keeping the payload itself off-chain. The standard defines (1) a public commitment interface, (2) an optional owner-gated envelope descriptor for wallet UX, and (3) indexer-friendly event conventions. This enables interoperable private-layer NFT patterns across contracts, wallets, marketplaces, and indexers without requiring consensus changes. + +# Copyright + +This SIP is made available under the terms of the CC0-1.0 license. +This SIP’s copyright is waived to the extent possible under law. + +# Introduction + +Today, “private layer / encrypted metadata” NFTs on Stacks are implemented as bespoke, project-specific patterns. That fragmentation makes it hard for wallets, marketplaces, and indexers to support them generically. + +This SIP standardizes a minimal interface for eNFT private layers: +- A **public commitment** (hash) to the private payload, enabling anyone to verify integrity after reveal. +- An optional **owner-gated envelope descriptor** to guide wallet UX (e.g., show “Reveal” only to the owner). +- A recommended **event format** so indexers can discover updates efficiently. + +## Goals + +- Define a reusable trait for commitment-based private layers for NFTs. +- Make private-layer patterns discoverable and composable for wallets/indexers. +- Keep the standard minimal and non-consensus. + +## Non-Goals + +- Full on-chain confidentiality (not achievable in a public read-only model). +- Mandating a specific storage network, encryption scheme, or reveal flow. +- Defining a full game/puzzle “answer verification” protocol (out of scope). + +# Specification + +## 1. Terminology + +- **Payload**: The private off-chain data (JSON, image, encrypted blob, etc.). +- **Commitment**: A hash over the payload (or ciphertext) recorded on-chain. +- **Envelope**: A small off-chain descriptor used for UX/access, e.g., a retrieval URI, encrypted key, or signed URL. +- **Reveal**: Off-chain delivery of payload/envelope to the user; verification is done by comparing hash(payload) to on-chain commitment. + +## 2. Commitment Semantics + +Implementations MUST expose a 32-byte commitment for a given token-id. +The commitment MUST be computed using the declared algorithm identifier. + +This SIP does not mandate a single algorithm, but RECOMMENDS: +- `algo = u0` => SHA-256 over raw payload bytes + +## 3. Trait Interface (Clarity) + +The trait defines two read-only methods: one public (commitment) and one optional/UX-oriented (owner envelope). + +### 3.1 Clarity trait definition + +```clarity +(define-trait enft-private-layer-trait + ( + ;; Public: returns the commitment to the private payload (or ciphertext) + (get-private-commitment (uint) + (response + (tuple + (commitment (buff 32)) ;; hash of payload or ciphertext + (algo uint) ;; algorithm id (e.g., u0 = sha256) + (mime (optional (string-ascii 64))) ;; e.g. "application/json", "image/png" + (size (optional uint)) ;; bytes (optional) + (commitment-version uint) ;; scheme versioning + ) + uint + ) + ) + + ;; UX-oriented: returns an owner-gated envelope descriptor. + ;; Implementations SHOULD check ownership for consistent wallet UX. + (get-owner-envelope (uint) + (response + (tuple + (envelope-uri (string-ascii 256)) ;; retrieval location (NOT a secrecy guarantee) + (envelope-hash (buff 32)) ;; integrity check for envelope blob/response + (algo uint) ;; algorithm id for envelope-hash + (envelope-version uint) ;; scheme versioning + ) + uint + ) + ) + ) +) +``` + +## 3.2 Error Codes (Recommended) +Implementations MAY choose their own error codes. This SIP RECOMMENDS: + +`err u404` => token-id not found / no private layer set + +`err u100` => not authorized (e.g., tx-sender not owner) for get-owner-envelope + +## 4. Events (Indexer Conventions) +When a private-layer commitment is set or updated, contracts SHOULD emit a print event with a predictable structure: + +```clarity + +(print { + notification: "enft-update", + token-id: token-id, + commitment: commitment, + algo: algo, + commitment-version: commitment-version +}) +``` + +If an envelope descriptor is set/updated, contracts MAY emit: + +```clarity + +(print { + notification: "enft-envelope-update", + token-id: token-id, + envelope-hash: envelope-hash, + algo: algo, + envelope-version: envelope-version +}) +``` + +Notes: + +- Events are intended for indexers/wallets discovery and UI updates. + +- `envelope-uri` SHOULD NOT be treated as confidential by default. + +## Backwards Compatibility +This SIP is additive and does not break existing SIP-009 NFT contracts. Projects can implement this trait alongside existing token URI metadata patterns. Wallets/indexers can support eNFTs incrementally. + +## Security Considerations +**Read-only visibility and “owner gating”** + +`get-owner-envelope` ownership checks are primarily for **wallet UX consistency**, not cryptographic secrecy. Read-only calls can be simulated by node operators; therefore, **confidentiality MUST NOT rely solely on on-chain gating**. + +If the envelope or payload must remain private, it SHOULD be protected off-chain via: + +- encryption (e.g., encrypt payload/key per owner), and/or + +- access control (signed messages, bearer tokens, short TTL URLs). + +**Integrity and server non-equivocation** + +The on-chain commitment ensures the server cannot swap the underlying private payload after minting without being detected. Clients MUST verify `hash(payload)==commitment` after reveal. + +**Phishing / malicious URIs** + +Wallets SHOULD treat envelope URIs as untrusted input and apply standard URL safety practices. + +## Reference Implementation + +A reference contract (informative) can implement: + +- storage mapping token-id => commitment tuple + +- storage mapping token-id => envelope tuple + +- setters restricted to contract-defined roles (e.g., token owner or game operator) + +- `print` events as specified above + +## Related Work + +Cryptographic commitments (hash commitments) and commit–reveal mechanics are widely used across blockchains and in on-chain games to prevent equivocation. Separately, some NFT ecosystems support “encrypted metadata” or off-chain private content patterns, but these are typically application-specific and lack a shared interface for wallets/indexers. +To the best of our knowledge, Stacks currently has no minimal, interoperable trait + event convention that standardizes a commitment-anchored private layer for SIP-009 NFTs. This SIP fills that gap by defining a composable trait and predictable indexer events, while remaining non-consensus and implementation-agnostic. + +## Activation + +No consensus changes are required. Adoption is voluntary: + +- NFT contracts implement the trait + +- wallets/indexers add UI for commitments and optional reveal flows + +- marketplaces may display “private layer present” and verified reveal status + +## References + +[1] SIP-009 (NFT trait standard on Stacks) + +[2] Forum discussion thread linked in Preamble From 84fafd0ec0c8a614fc152d6a3b59d76b2f259c8c Mon Sep 17 00:00:00 2001 From: Skullcoin Labs <116502774+proofofgame@users.noreply.github.com> Date: Sun, 18 Jan 2026 02:07:49 +0300 Subject: [PATCH 2/3] Update sip-enft-private-layer.md --- sip-enft-private-layer.md | 189 ++++++++++++++++++++++++-------------- 1 file changed, 121 insertions(+), 68 deletions(-) diff --git a/sip-enft-private-layer.md b/sip-enft-private-layer.md index b3048ce2..31968cde 100644 --- a/sip-enft-private-layer.md +++ b/sip-enft-private-layer.md @@ -1,14 +1,13 @@ -# SIP-XXX: Encrypted NFTs - Standard Trait for Commitment-Based Private Layers (eNFT) +# SIP-XXX: Standard Trait Definition for Commitment-Based Private Metadata (Encrypted NFTs) ## Preamble - **SIP**: SIP-XXX -- **Title**: Encrypted NFTs - Standard Trait for Commitment-Based Private Layers (eNFT) +- **Title**: Standard Trait Definition for Commitment-Based Private Metadata (Encrypted NFTs) - **Authors**: Xenitron nortinex@gmail.com - **Status**: Draft - **Consideration**: Technical - **Type**: Standard -- **Layer**: Traits - **Created**: 2026-01-10 - **License**: CC0-1.0 - **Sign-off**: @@ -16,27 +15,40 @@ # Abstract -This SIP proposes a simple, composable trait for “Encrypted NFTs” (eNFTs): NFTs that commit on-chain to a private off-chain payload (a “private layer”) while keeping the payload itself off-chain. The standard defines (1) a public commitment interface, (2) an optional owner-gated envelope descriptor for wallet UX, and (3) indexer-friendly event conventions. This enables interoperable private-layer NFT patterns across contracts, wallets, marketplaces, and indexers without requiring consensus changes. +This SIP proposes a minimal, composable trait interface for “Encrypted NFTs” (eNFTs): NFTs that commit on-chain to a private off-chain payload (“private metadata”) while keeping the payload itself off-chain. The standard defines: + +1) a public commitment interface, +2) an optional owner-gated envelope descriptor for wallet User Experience, and +3) indexer-friendly event conventions. + +This enables interoperable private-metadata NFT patterns across contracts, wallets, marketplaces, and indexers without requiring consensus changes. + # Copyright This SIP is made available under the terms of the CC0-1.0 license. This SIP’s copyright is waived to the extent possible under law. -# Introduction -Today, “private layer / encrypted metadata” NFTs on Stacks are implemented as bespoke, project-specific patterns. That fragmentation makes it hard for wallets, marketplaces, and indexers to support them generically. +## Introduction + +“Encrypted metadata / private metadata” NFTs on Stacks are currently implemented as project-specific patterns. This fragmentation makes it difficult for wallets, marketplaces, and indexers to support such NFTs uniformly. + +This SIP standardizes a minimal interface for commitment-based private metadata on SIP-009 NFTs: -This SIP standardizes a minimal interface for eNFT private layers: -- A **public commitment** (hash) to the private payload, enabling anyone to verify integrity after reveal. -- An optional **owner-gated envelope descriptor** to guide wallet UX (e.g., show “Reveal” only to the owner). -- A recommended **event format** so indexers can discover updates efficiently. +- A **public commitment** (hash) to the payload (or ciphertext), enabling anyone to verify integrity after reveal. +- An optional **owner-gated envelope descriptor** to guide wallet User Experience (e.g., show “Reveal” only to the owner). +- A recommended **event format** so indexers can detect updates efficiently. + +### Relationship to SIP-009 + +This trait is designed to be implemented **alongside** SIP-009-compliant NFT contracts, not as a replacement. An eNFT contract implements SIP-009 for ownership and transfer, and additionally implements this trait to expose commitment-based private metadata. ## Goals -- Define a reusable trait for commitment-based private layers for NFTs. +- Define a reusable trait for commitment-based private metadata for eNFTs. - Make private-layer patterns discoverable and composable for wallets/indexers. -- Keep the standard minimal and non-consensus. +- Keep the standard minimal, additive, and backward-compatible. ## Non-Goals @@ -49,42 +61,72 @@ This SIP standardizes a minimal interface for eNFT private layers: ## 1. Terminology - **Payload**: The private off-chain data (JSON, image, encrypted blob, etc.). -- **Commitment**: A hash over the payload (or ciphertext) recorded on-chain. -- **Envelope**: A small off-chain descriptor used for UX/access, e.g., a retrieval URI, encrypted key, or signed URL. -- **Reveal**: Off-chain delivery of payload/envelope to the user; verification is done by comparing hash(payload) to on-chain commitment. +- **Ciphertext**: An encrypted payload (still considered “payload bytes” for commitment purposes). +- **Commitment**: A hash over the payload bytes (often ciphertext bytes) recorded on-chain. +- **Envelope**: A small off-chain descriptor used for UX/access (e.g., retrieval URI, encrypted key blob, signed URL descriptor). +- **Reveal**: Off-chain delivery of payload/envelope to the user; the client verifies integrity by comparing hash(payload_bytes) to the on-chain commitment. +- **Algo ID**: An integer identifier that indicates how the commitment hash is computed. +- **Scheme Version**: A monotonically increasing version number for commitment/envelope encoding. -## 2. Commitment Semantics +### 2. Commitment Semantics -Implementations MUST expose a 32-byte commitment for a given token-id. -The commitment MUST be computed using the declared algorithm identifier. +Implementations **MUST** expose a commitment for a given token-id. -This SIP does not mandate a single algorithm, but RECOMMENDS: +- The commitment **MUST** be computed exactly according to the declared algorithm id. +- The commitment **MUST** be stable: the same payload bytes must produce the same commitment. +- This SIP does not mandate a single algorithm, but **RECOMMENDS**: - `algo = u0` => SHA-256 over raw payload bytes -## 3. Trait Interface (Clarity) +### Algorithm Registry (Informative) + +| Algo ID | Algorithm | Notes | +|---------|-----------|-------| +| u0 | SHA-256 | Recommended default | +| u1-u99 | Reserved | For future standardization | +| u100+ | Application-defined | Projects may define custom algorithms | + +**Notes** +- If the payload is JSON, implementers should treat the committed object as raw bytes of a canonical encoding chosen by the application (this SIP does not standardize canonicalization). Consumers must verify against the exact bytes they receive on reveal. -The trait defines two read-only methods: one public (commitment) and one optional/UX-oriented (owner envelope). +--- -### 3.1 Clarity trait definition +### 3. Trait Interface (Clarity) + +To make “optional” functionality actually optional in Clarity, this SIP defines **two traits**: + +- A **required** commitment trait (baseline interoperability). +- An **optional** owner-envelope trait (wallet UX extension). + +Contracts may implement the baseline trait alone, or both traits. + +#### 3.1 Required Trait: Commitment ```clarity -(define-trait enft-private-layer-trait +(define-trait enft-commitment-trait ( - ;; Public: returns the commitment to the private payload (or ciphertext) + ;; Public: returns the commitment to the private payload (often ciphertext bytes) (get-private-commitment (uint) (response (tuple - (commitment (buff 32)) ;; hash of payload or ciphertext + (commitment (buff 32)) ;; hash of payload bytes (algo uint) ;; algorithm id (e.g., u0 = sha256) (mime (optional (string-ascii 64))) ;; e.g. "application/json", "image/png" - (size (optional uint)) ;; bytes (optional) - (commitment-version uint) ;; scheme versioning + (size (optional uint)) ;; payload size in bytes (optional) + (commitment-version uint) ;; commitment scheme version ) uint ) ) + ) +) +``` + +#### 3.2 Optional Trait: Owner-Gated Envelope (Wallet UX Extension) - ;; UX-oriented: returns an owner-gated envelope descriptor. +```clarity +(define-trait enft-owner-envelope-trait + ( + ;; User Experience oriented: returns an owner-gated envelope descriptor. ;; Implementations SHOULD check ownership for consistent wallet UX. (get-owner-envelope (uint) (response @@ -92,7 +134,7 @@ The trait defines two read-only methods: one public (commitment) and one optiona (envelope-uri (string-ascii 256)) ;; retrieval location (NOT a secrecy guarantee) (envelope-hash (buff 32)) ;; integrity check for envelope blob/response (algo uint) ;; algorithm id for envelope-hash - (envelope-version uint) ;; scheme versioning + (envelope-version uint) ;; envelope scheme version ) uint ) @@ -101,18 +143,26 @@ The trait defines two read-only methods: one public (commitment) and one optiona ) ``` -## 3.2 Error Codes (Recommended) -Implementations MAY choose their own error codes. This SIP RECOMMENDS: +**Normative guidance** +- Contracts that claim eNFT support **MUST** implement `enft-commitment-trait`. +- Contracts **MAY** implement `enft-owner-envelope-trait` to provide a standardized “Reveal” UX path. +- Wallets/indexers should treat the presence of `enft-owner-envelope-trait` as an optional capability. -`err u404` => token-id not found / no private layer set +--- -`err u100` => not authorized (e.g., tx-sender not owner) for get-owner-envelope +### 3.3 Error Codes (Recommended) -## 4. Events (Indexer Conventions) -When a private-layer commitment is set or updated, contracts SHOULD emit a print event with a predictable structure: +Implementations **MAY** choose their own error codes. This SIP **RECOMMENDS**: -```clarity +- `err u404` => token-id not found / no commitment set +- `err u100` => not authorized for `get-owner-envelope` (only relevant if `enft-owner-envelope-trait` is implemented) + + +### 4. Events (Indexer Conventions) +When a private-metadata commitment is set or updated, contracts **SHOULD** emit a `print` event with a predictable structure: + +```clarity (print { notification: "enft-update", token-id: token-id, @@ -122,10 +172,9 @@ When a private-layer commitment is set or updated, contracts SHOULD emit a print }) ``` -If an envelope descriptor is set/updated, contracts MAY emit: +If an envelope descriptor is set/updated, contracts **MAY** emit: ```clarity - (print { notification: "enft-envelope-update", token-id: token-id, @@ -135,63 +184,67 @@ If an envelope descriptor is set/updated, contracts MAY emit: }) ``` -Notes: - +**Notes** - Events are intended for indexers/wallets discovery and UI updates. +- `envelope-uri` is intentionally excluded from the event for safety; wallets should fetch it via the read-only function if needed. +- Indexers should treat these events as hints and verify by reading the latest on-chain state as required. -- `envelope-uri` SHOULD NOT be treated as confidential by default. +--- ## Backwards Compatibility -This SIP is additive and does not break existing SIP-009 NFT contracts. Projects can implement this trait alongside existing token URI metadata patterns. Wallets/indexers can support eNFTs incrementally. + +This SIP is additive and does not break existing SIP-009 NFT contracts. Projects can implement these traits alongside existing token URI / metadata patterns. Wallets and indexers can support eNFTs incrementally. + +--- ## Security Considerations -**Read-only visibility and “owner gating”** +### Read-only visibility and “owner gating” `get-owner-envelope` ownership checks are primarily for **wallet UX consistency**, not cryptographic secrecy. Read-only calls can be simulated by node operators; therefore, **confidentiality MUST NOT rely solely on on-chain gating**. -If the envelope or payload must remain private, it SHOULD be protected off-chain via: - +If the envelope or payload must remain private, it **SHOULD** be protected off-chain via: - encryption (e.g., encrypt payload/key per owner), and/or +- access control (signed challenges, bearer tokens, short-TTL URLs). -- access control (signed messages, bearer tokens, short TTL URLs). - -**Integrity and server non-equivocation** +### Integrity and server non-equivocation +The on-chain commitment ensures the content provider cannot swap the underlying payload after minting (or after a commitment update) without being detected. Clients **MUST** verify `hash(payload_bytes) == commitment` after reveal. -The on-chain commitment ensures the server cannot swap the underlying private payload after minting without being detected. Clients MUST verify `hash(payload)==commitment` after reveal. +### Phishing / malicious URIs +Wallets should treat envelope URIs as untrusted input and apply standard URL safety practices (origin warnings, link previews sandboxing, and user confirmation flows where appropriate). -**Phishing / malicious URIs** +--- -Wallets SHOULD treat envelope URIs as untrusted input and apply standard URL safety practices. +## Reference Implementation (Informative) -## Reference Implementation - -A reference contract (informative) can implement: - -- storage mapping token-id => commitment tuple +A reference contract can implement: +- a mapping `token-id -> commitment tuple` +- optionally a mapping `token-id -> envelope tuple` +- setters restricted to contract-defined roles (e.g., token owner or authorized operator) +- `print` events as specified above -- storage mapping token-id => envelope tuple +--- -- setters restricted to contract-defined roles (e.g., token owner or game operator) +## Related Work (Informative) -- `print` events as specified above +Commitments and commit–reveal schemes are widely used across blockchains to prevent equivocation. Some NFT ecosystems support “encrypted metadata” patterns, but these are often application-specific and lack a shared interface for wallets/indexers. -## Related Work +To the best of our knowledge, Stacks lacks a minimal, interoperable trait and event convention for commitment-anchored private metadata on SIP-009 NFTs; this SIP defines such a standard in a composable, non-consensus, and implementation-agnostic manner. -Cryptographic commitments (hash commitments) and commit–reveal mechanics are widely used across blockchains and in on-chain games to prevent equivocation. Separately, some NFT ecosystems support “encrypted metadata” or off-chain private content patterns, but these are typically application-specific and lack a shared interface for wallets/indexers. -To the best of our knowledge, Stacks currently has no minimal, interoperable trait + event convention that standardizes a commitment-anchored private layer for SIP-009 NFTs. This SIP fills that gap by defining a composable trait and predictable indexer events, while remaining non-consensus and implementation-agnostic. +--- ## Activation -No consensus changes are required. Adoption is voluntary: +This SIP defines a voluntary standard trait and does not require a consensus upgrade. -- NFT contracts implement the trait +The SIP is considered **“Active”** when: -- wallets/indexers add UI for commitments and optional reveal flows +1) A reference implementation is deployed on Stacks mainnet (maintained by the SIP authors or any party), and +2) At least one independent integration demonstrates interoperability (e.g., an indexer parses the events, or a wallet/marketplace displays the commitment and/or envelope capability). -- marketplaces may display “private layer present” and verified reveal status +Until then, the SIP remains **“Draft”**, and implementers may ship experimental contracts under the proposed interface. ## References -[1] SIP-009 (NFT trait standard on Stacks) +[1] [SIP-009 ](https://github.com/stacksgov/sips/blob/main/sips/sip-009/sip-009-nft-standard.md) -[2] Forum discussion thread linked in Preamble +[2] [Forum discussion thread](https://forum.stacks.org/t/encrypted-nfts-on-stacks-a-standard-trait-for-commitment-based-private-layers-enft/18560) From 03c2a106288ad97f66fe304eb4f9debc4ea60e30 Mon Sep 17 00:00:00 2001 From: Skullcoin Labs <116502774+proofofgame@users.noreply.github.com> Date: Fri, 30 Jan 2026 14:38:01 +0300 Subject: [PATCH 3/3] Improve clarity in notes and security considerations Clarified notes on events and updated security considerations regarding read-only visibility and owner gating. --- sip-enft-private-layer.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sip-enft-private-layer.md b/sip-enft-private-layer.md index 31968cde..669eb886 100644 --- a/sip-enft-private-layer.md +++ b/sip-enft-private-layer.md @@ -185,7 +185,7 @@ If an envelope descriptor is set/updated, contracts **MAY** emit: ``` **Notes** -- Events are intended for indexers/wallets discovery and UI updates. +- Events are intended for indexers/wallets discovery and user interface updates. - `envelope-uri` is intentionally excluded from the event for safety; wallets should fetch it via the read-only function if needed. - Indexers should treat these events as hints and verify by reading the latest on-chain state as required. @@ -200,7 +200,7 @@ This SIP is additive and does not break existing SIP-009 NFT contracts. Projects ## Security Considerations ### Read-only visibility and “owner gating” -`get-owner-envelope` ownership checks are primarily for **wallet UX consistency**, not cryptographic secrecy. Read-only calls can be simulated by node operators; therefore, **confidentiality MUST NOT rely solely on on-chain gating**. +`get-owner-envelope` ownership checks are primarily for **wallet UX consistency**, not cryptographic secrecy. Read-only calls can be simulated by any party running a node; therefore, **confidentiality MUST NOT rely solely on on-chain gating**. If the envelope or payload must remain private, it **SHOULD** be protected off-chain via: - encryption (e.g., encrypt payload/key per owner), and/or