This App Registry documentation is in preview and is not production-ready.
App Registry Technical Architecture
This documentation is in draft format and under active development. Technical details may change as the protocol evolves. For the latest updates, see the GitHub repository.
This document explains how the App Registry works under the hood: the smart contracts, data structures, and verification mechanisms that power service registration and identity.
System Components
1. Identity Registry Contract
Contract: OMA3AppRegistry.sol
Purpose: Tokenize services as ERC-721 NFTs with metadata extensions
Standard: Compatible with ERC-8009 (Tokenized Services)
Core Functions:
function mint(
string did,
uint16 interfaces,
string dataUrl,
bytes32 dataHash,
uint8 dataHashAlgorithm,
string fungibleTokenId,
string contractId,
uint8 major, uint8 minor, uint8 patch,
bytes32[] traitHashes,
string metadataJson
) external returns (uint256 tokenId)
Key Features:
- DID-based indexing - Apps indexed by DID + major version
- Interface bitmap - Supports Human (1), API (2), Contract (4) combinations
- Version tracking - Major.minor.patch with on-chain history
- Status management - Active, Deprecated, Replaced
- Trait-based tagging - Hash-based tags for indexer discoverability
Query Functions:
getAppsByStatus()- Filter by status (Active, Deprecated, Replaced)getAppsByOwner()- Get user's appsgetAppsByInterface()- Filter by interface type(s) - On-chain searchhasAnyTraits(),hasAllTraits()- Check individual app traits
Note: Trait search requires client-side filtering or indexer (Shinzo). Interface search is on-chain due to finite values (1-7) vs unlimited trait possibilities.
2. Metadata Contract
Contract: OMA3AppMetadata.sol
Purpose: Store optional on-chain metadata JSON
Storage Model:
- Metadata stored by DID only (not versioned)
- Version history tracked via blockchain events
- Gas-efficient: Only store when needed
Why Separate?
- Registry = Core identity (always on-chain)
- Metadata = Optional enrichment (can be off-chain via dataUrl)
3. Resolver Contract
Contract: OMA3ResolverWithStore.sol
Purpose: Verify DID ownership and dataHash attestations
Verification Types:
// DID ownership verification
function checkDID(bytes32 didHash, address controller) view returns (bool)
// DataHash attestation (from oracle/auditor)
function checkDataHashAttestation(bytes32 didHash, bytes32 dataHash) view returns (bool)
Attestation Flow:
- Service registers with DID + dataHash
- Oracle verifies ownership (domain verification, wallet signature)
- Oracle writes attestation to resolver
- Clients query resolver for proof
The App Registry integrates with the broader OMATrust Reputation System for structured attestations (certifications, endorsements, reviews, etc.) via the Ethereum Attestation Service (EAS). See the Attestation Framework documentation for details on attestation types, schemas, and the EAS integration.
Data Model
App NFT Structure
interface App {
did: string; // Decentralized Identifier
versionMajor: uint8; // Major version (indexed)
versionHistory: Version[]; // Full version history
// Interface support
interfaces: uint16; // Bitmap: bit 0=Human (value 1), bit 1=API (value 2), bit 2=Contract (value 4)
// Metadata location
dataUrl: string; // URL to off-chain metadata JSON
dataHash: bytes32; // Hash of data at dataUrl
dataHashAlgorithm: string; // "keccak256" or "sha256"
// On-chain identifiers
contractId: string; // CAIP-10 contract address
fungibleTokenId: string; // CAIP-19 token ID
// Ownership & status
minter: address; // Original creator
status: uint8; // 0=Active, 1=Deprecated, 2=Replaced
// Discoverability
traitHashes: bytes32[]; // Searchable tags (≤20 entries)
}
Metadata JSON Structure
Off-chain metadata (at dataUrl):
{
"name": "My App",
"description": "App description",
"image": "https://example.com/icon.png",
"publisher": "Publisher Name",
// Interface-specific fields
"platforms": {
"web": {"launchUrl": "https://app.example.com"},
"ios": {"downloadUrl": "https://...", "artifactDid": "did:artifact:..."}
},
"endpoints": [
{
"name": "REST API",
"endpoint": "https://api.example.com",
"schemaUrl": "https://api.example.com/openapi.json"
},
{
"name": "MCP",
"endpoint": "https://mcp.example.com",
"tools": [...],
"resources": [...],
"prompts": [...]
}
],
// Traits for discovery
"traits": ["api:rest", "pay:x402", "gaming"]
}
Storage Strategy: DID-Only with Version Events
Challenge: Storing full metadata for every version is expensive.
Solution: Store by DID only, track versions via events.
On-Chain Storage
Registry: stores App struct at tokenID
├─ DID: did:web:example.com
├─ Major Version: 2
└─ versionHistory: [1.0.0, 1.5.2, 2.0.0]
Metadata Contract: stores JSON by DID
└─ did:web:example.com → {latest metadata JSON}
Version Tracking
Blockchain events provide history:
Event: MetadataSet(did, 1.0.0, hash, timestamp)
Event: MetadataSet(did, 1.5.2, hash, timestamp)
Event: MetadataSet(did, 2.0.0, hash, timestamp)
Benefits:
- ✅ Gas efficient (one storage slot per DID)
- ✅ Version history preserved in events
- ✅ Can reconstruct any version from events + dataUrl
Verification Architecture
DID Verification Flow
For did:web:
1. User registers: did:web:example.com
2. Frontend calls: POST /api/verify-and-attest
3. Server checks for existing attestations (fast path)
4. If missing: fetches https://example.com/.well-known/did.json
5. Validates: wallet address in DID document
6. Issues attestation to resolver
7. User can now mint app NFT
For did:pkh:
1. User registers: did:pkh:eip155:1:0xContractAddress
2. Frontend calls: POST /api/verify-and-attest
3. Server checks for existing attestations (fast path)
4. If missing: checks contract owner matches connected wallet
5. Issuer issues attestation
6. User can mint
DataHash Verification
Integrity check:
// 1. Fetch metadata from dataUrl
const response = await fetch(app.dataUrl);
const jsonText = await response.text();
// 2. Canonicalize JSON using JCS (RFC 8785)
// Note: Use a JCS library for proper canonicalization
// See Identity Specification for details
const canonicalJson = canonicalizeJson(jsonText);
// 3. Compute hash
const computedHash = app.dataHashAlgorithm === 'keccak256'
? ethers.id(canonicalJson)
: sha256(canonicalJson);
// 4. Compare with stored hash
if (computedHash === app.dataHash) {
// ✅ Data hasn't been tampered with
}
// 5. Check for attestation
const attested = await resolver.checkDataHashAttestation(didHash, dataHash);
if (attested) {
// ✅ Issuer verified this hash
}
Important: JSON must be canonicalized using JCS (JSON Canonicalization Scheme, RFC 8785) before hashing to ensure consistent hash values. See the Identity Specification for complete details.
Interface Types Explained
OMATrust supports three interface types (can be combined):
Human Interface (Bit 1)
For: Websites, apps with GUI, downloadable binaries
Required Fields:
- Screenshots
- App icon
- Platform availability (web, iOS, Android, etc.)
Optional:
- Video URLs
- 3D assets (for AR/VR)
- IWPS portal URL (metaverse integration)
API Interface (Bit 2)
For: REST APIs, GraphQL, JSON-RPC, MCP servers, A2A agents
Required Fields:
- Endpoint URL
- API type (via traits:
api:rest,api:mcp, etc.)
Optional:
- Schema URL (OpenAPI spec, GraphQL SDL, etc.)
- Interface versions
- MCP configuration (for AI agents)
Smart Contract Interface (Bit 4)
For: On-chain applications
Required Fields:
- Contract address (via DID:
did:pkh:eip155:1:0xAddress)
Optional:
- Recommended RPC endpoint
- ABI schema URL
Examples:
Human only: interfaces = 1
API only: interfaces = 2
Contract only: interfaces = 4
Human + API: interfaces = 3
All interfaces: interfaces = 7
Deployment
Testnet (Current)
Network: OMAchain Testnet
Chain ID: 66238
RPC: https://rpc.testnet.chain.oma3.org/
Explorer: https://explorer.testnet.chain.oma3.org/
Faucet: https://faucet.testnet.chain.oma3.org/
Contracts:
Registry: 0x63A7C12f54B4f42Cae7234f7e20c7A08f725B9F9
Metadata: 0xFdd87eA429D963eCB671D409128dC94BFf5f0694
Resolver: 0x77E058106762AeA4A567f2919Ef896bb6A82f914
Mainnet (Planned)
Network: OMAchain Mainnet
Chain ID: 999999 (TBD)
Status: Coming soon
Security Model
Ownership & Permissions
- Minter - Original creator, can update metadata
- Owner - Current NFT holder (transferable)
- Resolver Issuers - Authorized oracles for attestations
Trust Assumptions
What's verified on-chain:
- ✅ DID ownership (via resolver attestation)
- ✅ DataHash integrity (hash comparison with JCS canonicalization)
What's NOT verified:
- ❌ Off-chain metadata content (you must trust dataUrl host)
Mitigation Strategies
- DataHash verification - Detect tampering using JCS-canonicalized hashes
- Challenge mechanism - Resolver supports ownership challenges with maturation delay (see Identity Specification §5.2 for details)
- Maturation delay - Time-based delay before attestations count toward ownership scores
DID → Index Address Mapping: The system maps DIDs to Ethereum addresses for on-chain indexing. This mapping is handled by the SDK and contracts. See the Identity Specification for technical details.
Integration Points
Frontend
Registry UI: https://registry.omatrust.org
API Endpoints
Verify and attest (unified endpoint):
POST /api/verify-and-attest
Body: { did, connectedAddress, requiredSchemas }
Returns: { ok, status, attestations, txHashes }
Fetch metadata:
GET /api/data-url/{versionedDid}
Returns: JSON metadata
Smart Contract Integration
Read from registry:
import "./OMA3AppRegistry.sol";
OMA3AppRegistry registry = OMA3AppRegistry(registryAddress);
App memory app = registry.getApp("did:web:example.com", 1);
// Verify dataHash
bytes32 computedHash = keccak256(bytes(fetchedJson));
require(computedHash == app.dataHash, "Data tampered");
Query attestations:
import "./OMA3ResolverWithStore.sol";
OMA3ResolverWithStore resolver = OMA3ResolverWithStore(resolverAddress);
bytes32 didHash = keccak256(bytes(did));
bool verified = resolver.checkDataHashAttestation(didHash, dataHash);
Gas Optimization
Efficient Storage
- DID-only metadata - Not versioned, saves gas
- Event-based history - Versions tracked in logs
- Hash verification - Cheap comparison vs full storage
Batch Operations
Registry contract supports single-transaction minting:
// One tx mints registry NFT AND stores metadata
registry.mint(...12 parameters including metadataJson)
Roadmap
Current (Testnet):
- ✅ Registry, Metadata, Resolver contracts
- ✅ DID verification (did:web, did:pkh)
- ✅ Multi-interface support (Human, API, Contract)
- ✅ DataHash attestations
Coming Soon:
- Cross-chain deployment (Ethereum, Base, Optimism)
- Deduplication contracts on OMAchain
Learn More
- Tokenized Services - Data model details
- Registration Cookbooks - Specific examples
- Client Guide - Integration for apps
Questions? Open an issue on GitHub.