CONTRACT HUB / CURRENT

게임 연결 계약을 한곳에.

게임은 자기 화면과 데이터를 소유하고, KOISCORE는 플레이어 셸·로그인·공용 젬을 담당합니다. 출시할 때 필요한 API와 브릿지 계약을 구현 순서대로 정리했습니다.

CONTRACT MAP

어떤 문서를 봐야 하나요?

신규 게임은 네 계약을 모두 적용합니다. 기존 라이브 게임은 Portal Bridge v1을 유지한 채 Identity와 Runtime 계약부터 점진적으로 전환합니다.

IMPLEMENTATION ORDER

연동 순서

01게임 등록

고정 gameId와 런타임 URL, 장르, 언어, 화면 비율을 draft로 등록합니다.

02프레임 연결

game:ready 이후 portal:init을 받고 origin·source·gameId를 검증합니다.

03사용자 연결

identity.playerId로 게임 데이터를 분리하고 assertion을 서버에서 검증합니다.

04검수·출시

프레임 크기, 무스크롤, 결제 멱등성, 플레이타임 조건을 확인합니다.

PORTAL BRIDGE / V1

포털과 게임 iframe

전송 방식은 window.postMessage입니다. 게임은 리스너를 먼저 등록하고 game:ready를 보냅니다. 포털은 현재 locale, 음소거, 셸 설정과 게임별 identity를 portal:init으로 응답합니다.

방향TYPE역할
게임 → 포털game:ready메시지 수신 준비 완료
포털 → 게임portal:initlocale, shell, audio, identity 전체 동기화
포털 → 게임portal:audio실행 중 마스터 음소거 변경
게임 → 포털game:identity-refresh만료 전 새 identity assertion 요청
게임 → 포털game:how-to현지화된 게임 방법과 규칙 전달
게임 → 포털game:wallet교환 후 공용 젬 잔액 갱신 요청
window.addEventListener('message', (event) => {
  if (event.source !== window.parent) return;
  if (event.origin !== 'https://koiscore.com') return;

  const message = event.data;
  if (message?.protocol !== 'koiscore.portal.v1') return;
  if (message?.gameId !== 'your-game-id') return;

  if (message.type === 'portal:init') {
    applyLocale(message.locale);
    setMuted(message.audio?.muted === true);
    setIdentity(message.identity);
  }
});

window.parent.postMessage({
  protocol: 'koiscore.portal.v1',
  type: 'game:ready',
  gameId: 'your-game-id'
}, 'https://koiscore.com');
postMessage('*')로 사용자 identity를 보내지 마세요. 승인된 포털 origin, 부모 window, protocol과 gameId를 모두 확인해야 합니다.

LOGIN IDENTITY / V2

로그인은 공용, 데이터는 게임별

KOISCORE가 로그인 세션과 계정 연결을 관리합니다. 게임은 전역 profileId가 아니라 게임마다 다른 identity.playerId를 세이브·점수·인벤토리의 사용자 키로 사용합니다.

{
  "schemaVersion": "koiscore.game-identity.v2",
  "gameId": "your-game-id",
  "authenticated": true,
  "playerId": "ply_...",
  "displayName": "Koi Player",
  "locale": "ko",
  "provider": "google",
  "assertion": {
    "format": "jwt",
    "token": "header.payload.signature",
    "expiresAt": "2026-08-04T00:01:00.000Z"
  }
}

게임 서버 필수 검증

  1. GET /api/platform/identity/jwks의 Ed25519 공개키로 JWT 서명을 검증합니다.
  2. iss=https://koiscore.com, aud=gameId, game_id=gameId를 확인합니다.
  3. 검증된 sub만 데이터 키로 사용하고 body의 playerId는 신뢰하지 않습니다.
  4. assertion은 최대 300초만 허용하고 만료되면 game:identity-refresh를 요청합니다.

RUNTIME / V2

게임 프레임과 캔버스 정책

항목필수 정책
Rendererpixi-canvas, wasm-canvas, unity-webgl 중 등록
Sizeiframe 실제 너비·높이를 사용하고 ResizeObserver로 갱신
Mobilehtml, body, root 100%, 내부 문서 스크롤 금지
Shell타이틀, 광고, 공용 젬 UI는 KOISCORE 호스트가 소유
LaunchURL에 세션 token을 넣지 않고 승인된 호스트 진입만 허용
Migrationsandbox → shadow → canary → v2-live, rollback 산출물 유지

INTEGRATION / V1

등록·광고·젬 경계

  • 게임 화면, 세이브, 점수, 게임 전용 재화와 인벤토리는 개별 게임이 소유합니다.
  • 로그인 세션, 게임별 identity 발급, 공용 젬 원장과 결제 영수증은 KOISCORE가 소유합니다.
  • 웹 광고는 포털, Apps in Toss 광고는 미니앱 채널이 담당하며 게임에서 중복 요청하지 않습니다.
  • 젬 구매는 서버 상품 카탈로그와 영수증을 사용하고 게임 지급 API는 멱등 처리합니다.

PUBLIC SOURCES

원본 계약 참조

자동화 도구와 CI는 공개 계약 카탈로그에서 현재 Schema URL을 조회할 수 있습니다. 사람이 읽는 브릿지 상세본은 아래 레퍼런스를 사용합니다.