Skip to content

Add identity bridge: entity URIs to wallet addresses #13

@glandua

Description

@glandua

Summary

The claims engine has no mapping between KOI entity URIs (urn:koi:entity:...) and on-chain wallet addresses (regen1...). The attestor_address column exists in the claim_attestations schema (migration 066) but is never populated by any code path.

This blocks interop with the CLAMS CosmWasm contract where identity is wallet-based, and with EAS where identity is Ethereum address-based.

Current State

  • All identity uses KOI entity URIs via FK to entity_registry.fuseki_uri
  • claim_attestations.attestor_address column exists but is always NULL
  • No wallet_address column on entity_registry
  • V2 Phase 3 (per-reviewer signing via cosmos.authz) requires this mapping but has no implementation path

Proposed Changes

  1. Add wallet_address TEXT column to entity_registry (nullable, new migration)
  2. Populate claim_attestations.attestor_address when creating attestations (if the reviewer entity has a mapped wallet address)
  3. Add API endpoint or field for registering wallet address mappings: PATCH /entities/{uri}/wallet or similar
  4. Document the identity model: KOI entity URI = semantic identity, wallet address = cryptographic identity, both reference the same real-world entity

Why This Matters

Related


Implementation Spec (for Darren)

1. Add wallet_address to entity_registry

New Alembic migration:

"""Add wallet_address to entity_registry"""

def upgrade():
    op.add_column('entity_registry', sa.Column('wallet_address', sa.Text(), nullable=True))
    # Partial unique index: only enforce uniqueness where wallet_address is not null
    op.execute("""
        CREATE UNIQUE INDEX ix_entity_registry_wallet_address 
        ON entity_registry (wallet_address) 
        WHERE wallet_address IS NOT NULL
    """)

The partial unique index ensures no two entities share a wallet address while allowing multiple NULL values (entities without wallets).

2. Populate attestor_address during attestation creation

In the attestation creation code path (likely claims_router.py or attestation service):

# When creating an attestation, look up the reviewer's wallet
reviewer = db.query(EntityRegistry).filter_by(fuseki_uri=reviewer_uri).first()
attestor_address = reviewer.wallet_address if reviewer else None

# Pass to attestation INSERT/UPSERT
new_attestation = ClaimAttestation(
    ...existing fields...,
    attestor_address=attestor_address,  # Populate from entity registry
)

This populates the existing (currently always-NULL) attestor_address column without requiring any schema change to claim_attestations.

3. New endpoint: PATCH /entities/{uri}/wallet

@router.patch("/entities/{uri:path}/wallet")
async def register_wallet(
    uri: str,
    request: WalletRegistrationRequest,
    db: Session = Depends(get_db),
):
    """Register or update a wallet address for an entity.
    
    Links a semantic KOI entity URI to an on-chain wallet address,
    enabling interop with CLAMS contract and EAS attestations.
    """
    entity = db.query(EntityRegistry).filter_by(fuseki_uri=uri).first()
    if not entity:
        raise HTTPException(404, f"Entity not found: {uri}")
    
    validate_wallet_address(request.wallet_address)
    entity.wallet_address = request.wallet_address
    db.commit()
    return {"entity_uri": uri, "wallet_address": request.wallet_address}

Request model:

class WalletRegistrationRequest(BaseModel):
    wallet_address: str
    
    @validator('wallet_address')
    def validate_address(cls, v):
        if not validate_wallet_address(v):
            raise ValueError(f"Invalid wallet address format: {v}")
        return v

4. Address format validation

import re

REGEN_ADDRESS_PATTERN = re.compile(r'^regen1[a-z0-9]{38}$')
ETH_ADDRESS_PATTERN = re.compile(r'^0x[0-9a-fA-F]{40}$')

def validate_wallet_address(address: str) -> bool:
    """Validate wallet address format.
    
    Accepts:
    - Regen Network bech32 addresses: regen1[a-z0-9]{38}
    - Ethereum hex addresses: 0x[0-9a-fA-F]{40}
    
    Does NOT validate checksum — that's the wallet's responsibility.
    """
    return bool(
        REGEN_ADDRESS_PATTERN.match(address) or 
        ETH_ADDRESS_PATTERN.match(address)
    )

5. Include in entity responses

Update the entity serialization (response model) to include wallet_address when present:

class EntityResponse(BaseModel):
    fuseki_uri: str
    name: str
    type: str
    wallet_address: Optional[str] = None  # Add this field
    # ...existing fields...

This ensures API consumers (including CLAMS and EAS integrations) can discover wallet mappings through normal entity queries.

Acceptance Criteria

  • New migration adds wallet_address TEXT to entity_registry with partial unique index
  • attestor_address is populated from reviewer's wallet_address during attestation creation
  • PATCH /entities/{uri}/wallet endpoint registers/updates wallet addresses
  • Validation accepts regen1[a-z0-9]{38} and 0x[0-9a-fA-F]{40} formats
  • Entity API responses include wallet_address when present
  • Existing entities and attestations unaffected (wallet_address is nullable)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions