콘텐츠로 이동

시스템 구조

Umul은 지갑 주소를 신원으로 쓰는 모바일형 웹 메신저입니다. 메시징은 XMTP dev 네트워크, 인증 상태 조회와 대화 증빙은 GIWA Sepolia를 사용합니다.

시스템 아키텍처

앱은 기기별 XMTP installation 키와 로컬 MLS DB로 메시지를 암복호화하고, 서버는 로그인 세션과 증빙 상태(Auth / Evidence)만 다룹니다. 편집 가능한 원본은 architecture.drawio다.

구성 요소

구성 요소 책임 메시지 원문 접근
frontend 지갑 연결, XMTP E2EE 메시징, Dojang 조회, 증빙 manifest 생성과 서명 있음
backend 로그인 nonce와 세션, 증빙 요청 상태와 서명 수집 없음
contracts EIP-712 다중 서명 검증, Dojang 재확인, 증빙 해시 기록 없음

메시지 원문을 다루는 유일한 구성 요소는 프론트엔드입니다. 백엔드와 컨트랙트에는 해시와 서명만 전달합니다.

계층 경계

프론트엔드와 백엔드 모두 같은 네 계층을 씁니다.

  • domain: 주소 정규화, 수신함 분류, 증빙 manifest 규칙, 증빙 상태 전이
  • application: 유스케이스와 포트 인터페이스
  • infrastructure: XMTP, 지갑, RPC, PostgreSQL, HTTP 어댑터
  • presentation / interfaces: 화면과 HTTP 라우트

presentation은 SDK를 직접 호출하지 않고 application의 포트만 사용합니다. 구현체는 앱 시작 시 src/app/runtime/UmulRuntime.ts에서 주입합니다.

런타임 조립

readConfig()  ->  createShell()  --지갑 로그인-->  createSession()
                     │                                  │
                     ├─ WagmiWalletAdapter               ├─ XmtpMessagingAdapter
                     ├─ HttpSessionAdapter               ├─ ViemEvidenceContractAdapter
                     ├─ ViemDojangAdapter                └─ 메시징·증빙 유스케이스
                     └─ HttpEvidenceAdapter

createShell은 로그인 전에 만들 수 있는 의존성만 담습니다. XMTP 클라이언트와 증빙 컨트랙트 어댑터는 지갑 주소가 확정된 뒤 createSession에서 만듭니다.

VITE_EVIDENCE_CONTRACT_ADDRESS가 없으면 증빙 기능만 비활성이고 메시징은 그대로 동작합니다.

신뢰와 분류

DojangScroll.isVerified(address, attesterId) 결과를 주소별로 한 시간 캐시합니다. RPC 오류는 unverified가 아니라 unknown으로 구분해, 확인 불가 상태에서도 메시징은 허용하고 증빙만 차단합니다.

수신함 분류는 domain/inbox/classifyConversation.ts 한 곳에서 결정합니다.

조건 분류
사용자가 스팸으로 지정 (XMTP consent denied) 스팸함
자신을 제외한 참여자 전원 verified 메시지함
그 외 (unverified 또는 unknown 포함) 요청함

증빙 흐름

[제안자] 메시지 선택 -> manifest 생성 -> Dojang 재조회(캐시 우회)
    -> 전원 verified 일 때만 POST /v1/evidence-requests
    -> 원문을 암호화해 POST .../archive (콘텐츠 키는 서버에 없다)
    -> XMTP 제어 메시지 gasok:evidence:v1:<base64url-json> 전송 (키 포함)

[참여자] 제어 메시지 수신 -> 로컬 메시지로 content hash 재계산
    -> 일치할 때만 EIP-712 서명 -> POST .../signatures
    -> 지갑 서명 파생 키로 래핑한 복원 키 POST .../archive/keys

[전원 서명] 상태 READY -> recordEvidence 트랜잭션 -> POST .../submission

[복원] 서명한 지갑이 GET .../archive -> 지갑 서명으로 복호화
    -> content hash 재검증 후 표시 ([대화 기록](message-history.md) 참조)

컨트랙트는 제출 시점에 Dojang을 다시 조회합니다. 서명 이후 인증이 철회되면 프론트엔드 사전 검사를 통과했더라도 트랜잭션이 거부됩니다.

관련 문서