Skip to content

Credentials & Environment Variables

Status: Reference — Current
Date: 2026-05-06
Single source of truth for every credential TwoMore uses. See docs/best-practices/debugging-workflow.md for the Sentry pipeline mechanics. See docs/guides/api-supabase.md for Supabase-specific details.


Quick reference table

VariableVisibilityLocal .envEAS envPurposeHow to obtain
EXPO_PUBLIC_SUPABASE_URLplaintextrequiredset on all envsSupabase project endpointSupabase dashboard → Settings → API → Project URL
EXPO_PUBLIC_SUPABASE_ANON_KEYplaintextrequiredset on all envsSupabase anon JWT (safe in client)Supabase dashboard → Settings → API → anon key
EXPO_PUBLIC_SUPABASE_PUBLISHABLE_KEYplaintextoptionalset on all envsNewer sb_publishable_* format keySupabase dashboard → Settings → API → publishable key
EXPO_PUBLIC_USE_MOCKplaintextoptionalnot set (defaults false)Toggle mock adapters for devHardcode false; set true only during local unit dev
EXPO_PUBLIC_KAKAO_APP_KEYplaintextrequiredset on all envsKakao Login native SDK init; fallback Kakao Share native plugin keydevelopers.kakao.com → 내 애플리케이션 → 앱 키 → 네이티브 앱 키
TWOMORE_KAKAO_NATIVE_APP_KEYplaintextoptionalpreview / production build envPreferred R1 Kakao Share native plugin key. Uses the same Kakao Native App Key as EXPO_PUBLIC_KAKAO_APP_KEY; set this for clarity, or rely on the existing EXPO_PUBLIC_ fallbackSame Kakao Native App Key as above
TWOMORE_KAKAO_TEMPLATE_*plaintextoptionalpreview / production build envR1 KakaoTalk Share custom-template IDs for session, club, match result, tier promotion, stat card, and leaderboard sharesKakao Developers → 메시지 템플릿 → approved custom-template ID
TWOMORE_APPLE_TEAM_IDplaintextoptionalweb deploy enviOS Universal Links AASA appID value for twomore.appApple Developer → Membership details → Team ID
TWOMORE_ANDROID_SHA256_CERT_FINGERPRINTSplaintextoptionalweb deploy envAndroid App Links assetlinks.json certificate fingerprint list for com.ivorybridge.twomoreEAS/Play signing certificate SHA-256 fingerprint; comma-separate preview/production fingerprints if both should verify
EXPO_PUBLIC_NAVER_CLIENT_IDplaintextrequiredset on all envsNaver Local Search API headerdevelopers.naver.com → 애플리케이션 등록 → 검색(지역) API
EXPO_PUBLIC_NAVER_CLIENT_SECRETplaintextrequiredset on all envsNaver Local Search API header (paired with ID; bundle-restricted, not a true secret)Same dashboard as Client ID
DATA_GO_KR_SERVICE_KEYsecretoptional (server-only)Supabase Edge Function Secrets onlydata.go.kr service key — ACCOUNT-WIDE: authorizes every API approved on the account (한국천문연구원 특일정보, 소상공인 상가정보, 등록체육시설업, …), not just holidays. Read server-side only by supabase/functions/fetch-holidays; must never be EXPO_PUBLIC_* or ship in the client bundle.data.go.kr → 한국천문연구원 특일정보 API → 활용신청, then npx supabase secrets set DATA_GO_KR_SERVICE_KEY=...
EXPO_PUBLIC_DEV_EMAILplaintextoptionalset in eas.json (test-account fallback)Dev one-tap login button (local dev only)Create a test account in Supabase Auth dashboard
EXPO_PUBLIC_DEV_PASSWORDplaintextoptionalset in eas.json (test-account fallback)Dev one-tap login button (local dev only)Same as above
EXPO_PUBLIC_SENTRY_DSNsensitiveoptionalset on preview + productionRuntime Sentry SDK init — inlined into JS bundle at build timesentry.io → Settings → Projects → twomore-v2 → Client Keys (DSN)
SENTRY_AUTH_TOKENsecretnot needed locallyset on preview + productionBuild-time sentry-cli sourcemap upload to Sentry — never inlined into binarysentry.io → Settings → Auth Tokens → Create new org-level token. Required scopes: project:releases, project:write, org:read
EXPO_PUBLIC_SHOW_DEV_TOOLSplaintextoptionalset in eas.json preview onlyToggle dev-panel visibility on preview builds (production builds always false)Hardcode true for preview, omit for production
SUPABASE_ACCESS_TOKENsecretnot needed locallyGitHub Actions repo secret onlyCI: opt-in Supabase type-drift gate (regenerates generated.types.ts from live schema, fails if drift)Supabase Dashboard → Settings → Access Tokens → "Generate new token" → paste into GitHub repo settings → Secrets and variables → Actions
TWOMORE_SECRET_KEYsecretn/a (server-only)Supabase Edge Function Secrets onlyPost-Path-B rotation: edge functions read this sb_secret_* value instead of the legacy SUPABASE_SERVICE_ROLE_KEY reserved name. Rotates per-key without affecting user sessions.Supabase Dashboard → Settings → API Keys → "New API key" → Secret → set via npx supabase secrets set TWOMORE_SECRET_KEY=sb_secret_...

EAS env vs local .env

How EAS environment variables work

EAS has three variable visibility levels:

LevelDashboard visible?Build machine readable?Can be EXPO_PUBLIC_*?
plaintextYes (shown in UI)YesYes
sensitiveMasked in UIYes (readable on build machine; may appear in compiled bundle)Yes — use for EXPO_PUBLIC_* values that get inlined
secretNeverYes (only on build machine)No — secrets cannot be EXPO_PUBLIC_* because those must be readable at JS-bundle compile time

Build-time vs runtime distinction

EXPO_PUBLIC_* variables are inlined into the JS bundle at Metro compile time. This means:

  • Setting an EXPO_PUBLIC_* variable after a build has no effect — you must rebuild.
  • They end up in the binary (and can be extracted with tooling). Use sensitive visibility for values that are technically embeddable but should not be trivially visible in the EAS dashboard.
  • EXPO_PUBLIC_SENTRY_DSN is the key example: it needs to be in the bundle for the SDK to initialize, but you still want it masked in dashboards — hence sensitive.

Non-prefixed variables like SENTRY_AUTH_TOKEN are available only to the build environment (gradle, metro config, EAS build scripts). They are never inlined into the runtime binary.

Precedence rule

If a variable is set in both eas.json's env block AND via eas env:create, the eas.json value wins. We intentionally use this for EXPO_PUBLIC_DEV_EMAIL and EXPO_PUBLIC_DEV_PASSWORD: eas.json provides the test-account fallback for builds that do not have EAS environment variable access.


Adding a new credential

  1. Add the variable to .env.example with a comment header following the existing style (service name + obtain instructions).

  2. Set the variable on EAS via:

    bash
    eas env:create --name MY_NEW_VAR --value "the-value" --visibility plaintext
    # or --visibility sensitive / --visibility secret
  3. Document the variable in the quick reference table above.

  4. Update the consumer code to read process.env.MY_NEW_VAR (or Constants.expoConfig.extra.myNewVar if threaded through app.config.ts).

  5. If it is a EXPO_PUBLIC_* variable, rebuild — OTA alone will not pick up the new value.


Rotation procedure

Supabase anon key

  1. Supabase dashboard → Settings → API → Rotate anon key.

  2. Update EXPO_PUBLIC_SUPABASE_ANON_KEY on EAS:

    bash
    eas env:create --name EXPO_PUBLIC_SUPABASE_ANON_KEY --value "new-key" --visibility plaintext --force
  3. Rebuild the app — the old key is inlined in existing binaries.

Supabase publishable key

Same procedure as anon key above but for EXPO_PUBLIC_SUPABASE_PUBLISHABLE_KEY.

Sentry DSN

The DSN is not a secret; it is safe to rotate if a project is compromised.

  1. sentry.io → Settings → Projects → twomore-v2 → Client Keys → Revoke the old DSN → Create new.

  2. Update on EAS:

    bash
    eas env:create --name EXPO_PUBLIC_SENTRY_DSN --value "https://new-key@ingest.sentry.io/xxx" --visibility sensitive --force
  3. Rebuild — DSN is inlined at build time.

Sentry auth token

This is the most sensitive credential because it can upload sourcemaps. Rotate immediately if compromised.

  1. sentry.io → Settings → Auth Tokens → Revoke the old token.

  2. Create a new org-level token with scopes project:releases, project:write, org:read.

  3. Update on EAS:

    bash
    eas env:create --name SENTRY_AUTH_TOKEN --value "sntrys_new_token" --visibility secret --force
  4. No rebuild required — this variable is not inlined into the binary.

Kakao app key

  1. developers.kakao.com → 내 애플리케이션 → 앱 설정 → 앱 키 (네이티브 앱 키).

  2. Update on EAS:

    bash
    eas env:create --name EXPO_PUBLIC_KAKAO_APP_KEY --value "new-key" --visibility plaintext --force
    eas env:create --name TWOMORE_KAKAO_NATIVE_APP_KEY --value "new-key" --visibility plaintext --force
  3. TWOMORE_KAKAO_NATIVE_APP_KEY is the preferred R1 Kakao Share build-time variable; EXPO_PUBLIC_KAKAO_APP_KEY remains a backward-compatible fallback. Do not set the two names to different Kakao apps.

  4. Rebuild.

KakaoTalk Share template IDs

  1. developers.kakao.com → 내 애플리케이션 → 메시지 템플릿.

  2. Create or update the six R1 custom templates represented in packages/app/src/config/kakao-share-templates.json.

  3. Update the approved template IDs on EAS:

    bash
    eas env:create --name TWOMORE_KAKAO_TEMPLATE_SESSION_INVITE --value "template-id" --visibility plaintext --force
    eas env:create --name TWOMORE_KAKAO_TEMPLATE_CLUB_INVITE --value "template-id" --visibility plaintext --force
    eas env:create --name TWOMORE_KAKAO_TEMPLATE_MATCH_RESULT --value "template-id" --visibility plaintext --force
    eas env:create --name TWOMORE_KAKAO_TEMPLATE_TIER_PROMOTION --value "template-id" --visibility plaintext --force
    eas env:create --name TWOMORE_KAKAO_TEMPLATE_STAT_CARD --value "template-id" --visibility plaintext --force
    eas env:create --name TWOMORE_KAKAO_TEMPLATE_LEADERBOARD --value "template-id" --visibility plaintext --force
  4. Rebuild; these IDs are exposed to the app through Expo extra at build time and are verified by yarn check:r1-kakao-share:credentials.

  1. developers.naver.com → 애플리케이션 → 해당 앱 → API 설정 → 재발급.
  2. Update EXPO_PUBLIC_NAVER_CLIENT_ID and/or EXPO_PUBLIC_NAVER_CLIENT_SECRET on EAS (same eas env:create --force pattern).
  3. Rebuild.

Cross-references

Markdown remains the source of truth. Run yarn docs:check before handoff.