GCV HANDBOOK · 02 / ENGINEERING
확정할 수 있는 것만,
원장에 한 번.
서버 검증·저장 계약·사용자 피드백을 같은 사실에 연결하는 개발 안내서입니다.
One authoritative event, one ledger effect. Build UI, verification and storage around the same evidence.
범위 / Scope
현재 릴리스 계약의 개요이며 OpenAPI·코드의 실제 라우트 및 최신 서버 설정이 우선합니다. 문서만으로 DB 마이그레이션·배포·실제 회원 시험·결제·토큰 발행을 승인하지 않습니다. 이미 완료된 설치·검증·배포는 반복하지 않습니다. 다른 언어 번역은 검증되지 않았습니다.
This is a dated engineering guide, not authorization to migrate, deploy, test real users, pay or issue tokens. Actual routes and current server configuration remain authoritative. Reuse completed evidence instead of repeating installations or full runs. Other translations are not verified. Browser Print / Save as PDF omits screen-only styling.
01. 책임과 신뢰 경계 / Architecture
- 정적 포털: 백서·가이드·정책 안내. 회원 잔액을 만들거나 인증정보를 수집하지 않습니다.
- 앱: Pi SDK·서버 세션·API·로컬 대기 요청과 화면. 기기 시각·점수·클릭·성공 애니메이션은 지급 권위가 아닙니다.
- Express 서버: origin·rate limit·strict JSON·세션 identity·기능 준비 상태를 확인한 후 전용 서비스로 전달합니다.
- 인증 저장소: 회원·동의·signup intent·추천관계·코드 소유·writer fence·세션. 새 적립 클라이언트와 분리합니다.
- 적립 저장소: 정책/원천 증명 → participant 트랜잭션 → 미정산 → 잔액 효과·영수증. sourceRef와 고유 인덱스가 중복을 방지합니다.
- 운영 콘솔: 본사·연합회·크리에이터 등 역할을 서버에서 인가합니다. UI에서 역할을 고르는 것만으로 권한을 얻지 않습니다.
The static portal explains; the app displays and retains pending requests; Express validates origin, JSON, session identity and readiness. Authentication and accrual use separate database clients. Source evidence enters an atomic participant, then pending settlement and balance receipts. Role consoles never replace server authorization. Client clocks, scores and animations cannot authorize ledger value.
Node 지원 범위는 package.json의20 이상25 미만입니다. 기존 의존성·잠금파일과 검증된 실행 환경을 재사용하고 문서 작업 때문에 패키지를 갱신하지 않습니다.
Use the existing lockfile and supported Node range (20 through 24); do not upgrade dependencies as a documentation side effect.
02. 보존해야 할 불변식 / Invariants
- 정확 금액은 소수8자리 atomic 정수/문자열로 처리합니다. 부동소수점 합계로 원장 금액을 확정하지 않습니다.
- 가입314, 추천인6.28, 운동3.14, 게임6.28, 회원QR31.4/3.14는 반감 전 기준입니다. 검증된 서버 사건 시각으로 반감·회계일을 적용합니다.
- 채굴24시간·게임 전체5/일·운동5/일·회원QR2/일·부스트/선물 각각5/일·GCV 송금OFF를 유지합니다. 게임 목록 확장은 보상 횟수 확장이 아닙니다.
- 현재 기본률 b에 효과와 오늘 추천 증분을 더합니다: b × (1 +0.6B +0.4G +0.2 × min(N,314)). 추천에 반감을 두 번 적용하지 않으며 영구 R2 보너스는 제외합니다.
- 고정UTC−5 회계일, UTC05:00 경계, UTC05:05 전일 정산 기준입니다. 기기 시간·재로그인·채굴 재시작으로 일일 한도를 바꾸지 않습니다.
- 미확인 원천·오래된 누락 활동·응답 유실을 근거로 가상 지급 시각이나 보상 기록을 만들지 않습니다.
Keep 8-decimal atomic accounting, verified event-time halving, 24-hour mining, 5 daily game rewards across the catalog, 5 exercise rewards, 2 member-QR rewards, 5 boost/gift uses each, and transfers OFF. Use fixed UTC−5 days (UTC 05:00 boundary; prior-day settlement at 05:05). No double halving of referral increments, permanent R2 bonus, invented event times or retroactive rewards. A larger game catalog does not increase reward limits.
가입 v2 / Signup v2
서버 opt-in referralPolicy:'grace24-v1'만 새 흐름을 만듭니다. source는 intentId, userId, inviterId, acceptedAtUtcMs, referralPolicy, referralFinalizedAtUtcMs를 검증합니다. 초기 intent의 null 추천인은 불변이며 최종 관계가 이후 확정됩니다. 기존4필드 evidence와 v1는 유지합니다.
rewardState는 REGISTERED → USER_ACCRUED → ALL_ACCRUED입니다. 저장소 state는 user-only부터 ACCRUED이므로 기존 고유 인덱스 보호를 받습니다. HTTP 완료 응답은 내부 rewardState와 다릅니다: USER_ACCRUED는 가입자만, ACCRUED는 완료된 보상 처리 결과이며 외부 지갑은 NOT_CONNECTED입니다.
가입자 사건은 acceptedAtUtcMs, 추천인 사건은 referralFinalizedAtUtcMs 기준입니다. pending 추천 중에도 가입자 정산을 허용합니다. 기존 sourceRef·6인덱스·POLICY cutoff·이전 records를 보존하고 과거 intent를 새 정책에 편입하지 않습니다. QR 스키마1과 코드별 발급자314회 제한도 유지합니다.
Only server opt-in grace24-v1 admits the new flow. Initial intent attribution stays immutable; final relations may be bound later. Preserve legacy 4-field evidence and v1 records. rewardState progresses REGISTERED → USER_ACCRUED → ALL_ACCRUED; storage state becomes ACCRUED from the subscriber-only stage to retain unique-index protection. Public completion uses USER_ACCRUED or ACCRUED, not the internal state vocabulary. Subscriber and inviter use their respective verified times; subscriber settlement must not wait. Keep sourceRef, 6 indexes, the original POLICY cutoff and QR schema 1. Cap issuer rewards at 314 per signup QR without suppressing subscriber rewards.
03. 실제 연결 API / Mounted API map
아래는 현재 라우트 개요입니다. 실행용 요청·토큰 예시는 제공하지 않습니다. 보호 API는 서버 세션을 요구하며 클라이언트가 userId·보상액·적용시각을 추가할 수 없습니다. config가 가용성을 결정하며 HTTP200만으로 모든 지급 상태를 성공으로 간주하지 않습니다.
These are route contracts, not live calls. Protected routes use server-session identity. Do not add client userId, reward amount or event time. Read feature config and the response status; HTTP 200 alone is not a reward receipt.
- 가입 / Signup —
/api/auth/pi/signup/ GET config: 가용성·동의 버전·유예 정책.POST begin: accessToken, inviterCode, 선택 signupQrCode.POST complete: accessToken, intentId, termsVersion, privacyVersion, accepted, profile{language,country}. 비밀은 로그에 남기지 않습니다. / Config → verified identity → explicit consent → completion; redact all credentials.- 추천 등록 / Referral registration
/api/referral/registration— GET은 읽기 전용. POST body는 정확히{"inviterCode":"…"}. PENDING / FINALIZED / LEGACY와 서버 deadline을 사용합니다. 별도 Idempotency-Key 계약이 아니라 관계 확정과 재조회로 복구합니다. / Read status; submit only inviterCode; re-read after an ambiguous response.- 적립 / Accrual —
/api/accrual/ GET config, 보호GET me,GET live,GET stats;POST start,checkpoint,boost,gift는 빈 JSON object와 Idempotency-Key를 사용합니다. / Empty-object writes; start is idempotent in the active24-hour cycle mode.- 운동 / Exercise —
/api/accrual/ GET exercise/config,POST exercise의 body는 mission_key 하나,GET exercise/stats. 허용 키는 walk, stretch, stair, drink, walk_end. / One mission key; server-enforced limits.- 게임 / Games —
/api/accrual/ GET game/config;POST game/runsbody game_key;POST game/runs/:runId/endbody outcome(completed 또는 early_exit);GET game/runs/:runId;POST game/runs/:runId/ad-claim빈 JSON;GET game/stats. 게임 키·mapping_version은 현재 계약을 따릅니다. / Start, end, read and ad-opportunity claim have distinct meanings.- 회원 QR / Member QR —
/api/accrual/ GET qr/config,POST qr/scanbody qr_code,GET qr/stats; 권한 있는POST qr/issue빈 JSON,GET qr/issued,GET qr/issuer-stats. / Issuer authorization is independent of scanner eligibility.- 가입 QR / Signup QR —
/api/accrual/ POST signup-qr/issue빈 JSON,GET signup-qr/issued. 회원 QR scan 경로와 구분합니다. / Dedicated signup attribution, not a member scan reward.- 세션 / Sessions —
/api/auth/sessions - GET 목록, POST
/logout,/revoke,/revoke-others. 해제 body/proof는 현재 세션 계약을 따르며 임의 토큰을 복사하지 않습니다. / Use the managed session and reauthentication flow.
04. 멱등성 · 불명확한 커밋 / Retry discipline
지원되는 쓰기는 Idempotency-Key를 요구합니다. 길이16~128, 첫 문자는 영문/숫자, 이후 영문/숫자·점·밑줄·콜론·하이픈입니다. identity + method + route + canonical body에 묶인 fingerprint를 사용합니다. 같은 논리 요청의 key와 body를 함께 보존하세요.
- 요청 결과가 불명확하면 같은 계정·같은 key·같은 body로 제공된 재시도 경로를 사용합니다.
- 다른 payload를 같은 key로 보내면409 충돌입니다. 해결을 위해 무작정 새 key를 만들어 재지급하지 않습니다.
- 세션/계정 변경 시 이전 응답을 새 계정 UI에 적용하거나 대기 요청을 재전송하지 않습니다.
- 가입 완료는 intent, 추천 등록은 이미 확정된 relation이 별도 중복방지 단위입니다. 모든 POST에 동일한 key 프로토콜이 있다고 가정하지 않습니다.
Supported mutations require a 16–128 character Idempotency-Key (alphanumeric first, then alphanumeric, dot, underscore, colon or hyphen). Fingerprints bind identity, method, route and canonical body. Preserve key and payload for the same logical action. Unknown commit outcome means reconcile/retry the original action, not issue compensation with a fresh key. Clear account ownership of pending UI work on session changes. Signup intent and referral finalization have their own deduplication contracts.
05. 오류는 계약의 일부 / Error handling
AUTH_REQUIRED·401- 재인증 후 원래 기록 확인. / Reauthenticate, then inspect the original record.
ACCRUAL_BODY_FIELD_FORBIDDEN·400 /ACCRUAL_JSON_REQUIRED·415- 정확한 필드/JSON만 전송. 예기치 않은 query도 거부됩니다. / Fix the contract, not the ledger.
IDEMPOTENCY_KEY_CONFLICT·409- key/payload 소유와 원래 요청을 확인. / Inspect original request ownership and payload.
IDEMPOTENCY_COMMIT_OUTCOME_UNKNOWN·503- 실패 확정이 아님. 같은 요청 복구. / Outcome unknown; preserve the original request.
REFERRAL_REGISTRATION_EXPIRED,REFERRAL_ALREADY_BOUND,REFERRAL_SELF_FORBIDDEN,REFERRAL_CYCLE_FORBIDDEN·409- 등록 상태 재조회. 강제 귀속·클라이언트 시간 보정 금지. / Re-read; never force attribution or client time.
MINING_EFFECT_REQUIRES_ACTIVE_MINING·409 /MINING_EFFECT_DAILY_LIMIT_REACHED·429- 유효 주기·서버 일일 횟수 확인. / Respect cycle and server-day limits.
ACCRUAL_RUNTIME_UNAVAILABLE,ACCRUAL_GAME_UNAVAILABLE,PI_SIGNUP_UNAVAILABLE·503- 가용성 확인·기존 기록 보존. 로컬0잔액·가상성공으로 대체 금지. / Fail closed; unknown is neither zero nor success.
QR 등 일부 업무 거절은 JSON의 REJECTED 상태로 반환될 수 있습니다. status, scope, source/run 참조, 계정 소유를 함께 확인합니다. 위 오류 목록은 대표 사례이며 모든 라우트의 전체 명세는 아닙니다.
Some domain rejections, including QR outcomes, use a REJECTED response body. Validate status, scope, source/run binding and account ownership. This is a representative list, not an exhaustive error schema.
06. 세션 · 보안 · 개인정보 / Security
Pi access token은 서버 identity 확인용이며 GCV 관리 세션과 다릅니다. Pi 인증을 KYC로 표시하지 않습니다. 세션의 만료·해제·다른 기기/계정 전환을 처리하고, origin 허용목록·엄격한 JSON·rate limit·no-store를 유지합니다. 민감한 세션 해제 proof를 UI 편의로 생략하지 않습니다.
토큰·개인키·비밀번호·지갑 비밀문구·연결 URI는 코드/문서/로그/스크린샷에 넣지 않습니다. telemetry에는 허용된 오류 코드와 비식별 상관관계만 사용합니다. 콘솔 역할, QR 발급, 지급 승인은 서버에서 매번 확인하며 권한 실패를 성공으로 대체하지 않습니다.
Pi identity tokens and managed GCV sessions are different credentials. Preserve expiry, revocation, origin checks, strict JSON, rate limits and no-store. Do not bypass sensitive session proofs or label Pi authentication as KYC. Never log credentials, wallet secrets or connection URIs. Authorize console roles and QR issuance server-side; keep telemetry sanitized.
07. SDK 가동 ≠ 광고 재고 ≠ 수익 / Advertising
이번 게임 업그레이드는 Rhythm을 원본 WebAudio 100 BPM으로 구현하고 화면2048을 대체합니다. rhythmRewardKey:'merge' 명시 연결만 기존 보상 슬롯을 사용하며 과거 기록·서버 game key·금액·반감·전체5/일 한도를 바꾸지 않습니다. Spark Tap은 시작 광고를 먼저 요청하는 추가 연습이며 보상·추가 보너스가 없습니다. 08b03c8은 기존 배포 기준이며 신규 기능의 실제 반영 여부는 최신 배포 영수증을 따릅니다. 이 설명만으로 신규 기능의 배포 완료를 뜻하지 않습니다.
This game upgrade uses original WebAudio 100 BPM Rhythm in place of the 2048 presentation, with an explicit rhythmRewardKey:'merge' compatibility alias. Preserve old records, server keys, amounts, halving and the shared 5/day cap. Spark Tap requests a start ad and is a practice-only extra with no rewards or extra bonus. Backend 08b03c8 remains the previously deployed baseline. Check the latest deployment receipt for the actual rollout of new features; this description alone does not establish deployment completion.
앱은 Pi SDK 광고 흐름을 사용합니다. SDK가 활성이어도 지원 환경·준비 상태·재고에 따라 no-fill/timeout/닫힘이 발생할 수 있습니다. 광고 클릭을 지급 조건으로 만들지 않습니다. 게임 ad-claim은 서버가 종료 후 광고 기회를 중복 사용하지 않도록 기록하는 계약이지 광고 노출·시청 완료·매출 영수증이 아닙니다.
게임 no-fill은 이미 확인된 보상을 취소하지 않습니다. 광고 성공을 위조해 실패를 숨기거나 새로운 적립을 만들지 않습니다. 홈페이지 광고와 앱 활동 후 SDK 광고도 구분합니다. 실제 광고 수익화 승인·Pi 입금·90/10 배분 완료는 별도 증거가 필요합니다.
The app uses Pi SDK ad flows; an active SDK can still have no inventory, unsupported environments, timeout or closure. Ad clicks are not a payout condition. A game ad-claim records an opportunity/deduplication decision, not a verified impression, completed view or revenue receipt. No-fill does not revoke a confirmed game reward. Never fabricate ad success. Portal advertising and app post-activity ads are separate; monetization approval, Pi receipt and 90/10 distribution need their own evidence.
08. 테스트와 증거 재사용 / Verification
- 변경 파일·소유자·정책 버전과 기존 성공/실패 영수증을 먼저 고정합니다. 서로 다른 작업자가 같은 파일을 편집하지 않습니다.
- 변경 계약의 pure/unit·HTTP fixture·문서 링크 검사를 명시적 파일 목록으로 실행합니다. 현재 문서용 명령은 저장소 가이드에 있습니다.
- 합성 시간·계정으로 경계/동시성/재시도/세션 교체를 검증합니다. native는 별도 승인된 격리 환경만 사용합니다.
- 최초 실패와 보완 결과를 모두 보고합니다. focused pass는 전체 suite 통과가 아니고, local/native pass는 실제 사용자·광고·운영 승인이 아닙니다.
- 완료한 native315·운영 스키마 설치·전체 검사를 같은 문서 변경 때문에 반복하지 않습니다. 다음 릴리스 전체 검사는 통합 담당의 명시적 계획으로만 합니다.
Freeze scope and reuse existing receipts. Run explicitly named focused tests; use synthetic clocks/accounts for boundaries and retries. Native database tests need separate isolated authorization. Preserve first failures and distinguish focused, whole-suite, native, deployment and real-user evidence. Do not repeat the completed 315-signup proof or schema installation for documentation changes.
09. 배포 · 중지 · 보존 / Deployment runbook
기존 릴리스는 완료 상태입니다. 다음 절차는 향후 승인된 변경을 위한 점검표이며 지금 실행하라는 지시가 아닙니다.
- 최신 영수증과 현재 immutable release를 확인합니다. 문서 기준 backend08b03c8 / app20260928-referral-grace를 재설치하지 않습니다.
- 파일 소유권·기존 변경·정확한 정책·검증 결과를 확인한 뒤 불변 후보와 자산 manifest를 준비합니다. 비밀은 산출물에 포함하지 않습니다.
- 스키마 변경이 필요하다면 별도 대상·권한 승인 후 정확한 validator/UUID/index/POLICY를 읽어 비교합니다. 이번 grace auth2+reward1은 이미 설치됐습니다.
- 승인된 창에서만 환경 설정·배포를 적용하고 /ready와 기능 config, PI_SIGNUP_READY·SESSION_CONTROL_READY·ACCRUAL_RUNTIME_READY, 정적 자산 hash와 보안 헤더를 확인합니다. /ready만으로 보상 기능 준비를 단정하지 않습니다.
- 실제 회원 Pi 동의·인증·광고·금융 행위는 본인이 수행합니다. 실패 시 다음 호환 롤백을 사용하고 영수증을 보존합니다.
환경 계약 / Environment contract
GCV_PI_SIGNUP_MODE, GCV_ACCRUAL_RUNTIME_MODE, GCV_SIGNUP_REWARD_MODE, GCV_REFERRAL_GRACE_MODE: 활성화는 모두 enabled, 보존 중지는 모두 off입니다. GCV_PI_SIGNUP_STORAGE_MODE, GCV_ACCRUAL_STORAGE_MODE, GCV_SIGNUP_REWARD_STORAGE_MODE는 verify를 유지합니다. 기존 세션·bindings·소유자·writer fence 설정을 보존합니다.
signup OFF는 기존 로그인 alias에도 영향을 주며 accrual ON은 signup READY를 요구합니다. signup OFF + accrual ON을 부분 롤백으로 가정하지 않습니다. 현재 호환 코드에서 네 flag만 OFF로 중지하고, auth/reward 확장 validator·UUID·인덱스·POLICY·회원·미정산·잔액을 유지합니다. 구형 스키마 전용 binary나 validator 축소로 되돌리지 않습니다.
기존 POLICY의 activationId/cutoff는 유지되지만 확장 validator의 metadata proof는 새 planId여야 합니다. mining/effect 공존 verifier까지 정확한 old/new 계약을 이해해야 합니다. provision/prepare 재실행이나 검증 OFF로 실패를 우회하지 않습니다.
The release is already complete. For a future authorized deployment, pin an immutable candidate, reuse prior evidence, verify exact metadata and confirm readiness plus feature config and asset hashes. The four signup/accrual/reward/grace runtime flags are enabled together, or off together for preservation; the three storage modes remain verify. Keep sessions, bindings, ownership and writer fence. Signup OFF affects the existing-login alias and cannot coexist with accrual ON. Roll back by disabling those flags on a schema-compatible binary, never by shrinking validators or deleting records. Preserve the original POLICY cutoff while pinning the expanded validator’s new proof planId. All metadata consumers—including mining/effects coexistence—must support the exact contract.
10. 저비용 확장과 미래 체인 / Scale roadmap
- 먼저 관측: 활성 사용자·동시접속·p95 지연·DB 쿼리/트랜잭션 충돌·정산 지연·추천 만료 backlog를 분리합니다. 등록 회원 수를 처리량으로 환산하지 않습니다.
- 기존 자원 최적화: 정적 파일 재사용, 화면별 읽기 빈도 제한, 페이지/커서 기반 조회, bounded batch, 원장 조회에 맞는 검증된 인덱스를 우선합니다. 개인정보 응답을 공용 캐시에 넣지 않습니다.
- 부하 검증 후 확장: 승인된 합성 부하로 정상·급증·복구를 측정합니다. 서버가 소유한60초 정산/만료 타이머와 최대100행 커서 처리는 전 회원 규모의 처리량 보장이 아닙니다.
- 규모 목표:300만명·1,000만명은 목표입니다. queue/worker 분리·read replica·샤딩은 측정된 병목과 데이터 보존/장애복구 계획, 비용 승인이 있을 때만 선택합니다.
Measure active/concurrent users, p95 latency, database conflicts, settlement lag and referral-expiry backlog. Prefer static delivery, bounded reads, cursor batches and measured index improvements before paid infrastructure. Never public-cache private responses. The owned 60-second timer and 100-row expiry batch are not proof of global throughput. 3 million / 10 million members are targets; queues, replicas and sharding require measured bottlenecks, recovery plans and separate cost approval.
Pi DEX·AMM 및 Launchpad 공식 안내는 Testnet 실험·변경 가능성을 설명합니다. GCV의 승인·상장·Mainnet 발행 완료를 뜻하지 않습니다. 내부 원장→외부 자산 연결은 별도 감사·공식 지원·사용자 승인·복구 증명을 거쳐야 합니다. Pi DEX·AMM 공식 안내 · Pi Launchpad 공식 안내
Pi’s DEX/AMM and Launchpad publications describe Testnet experimentation and evolving designs, not GCV approval, listing or Mainnet issuance. Bridging an internal ledger to external assets requires separate support, authorization, audit and recovery evidence.
GCV