Stripe is not used. Keeping its checkout, portal and webhook paths meant a
second payment provider, a second return-URL format and dead UI.
- Delete the stripe-checkout, stripe-portal and stripe-webhook functions and
their config; billing-catalog serves Payple prices only, and the web parser
rejects a catalog that still mixes in Stripe prices.
- Web: drop the Stripe checkout/portal buttons, provider toggle and return
notices; billing shows Payple only. Past rows with provider='stripe' are
still displayed ("Stripe (종료)") with a support contact instead of a portal.
- Desktop: delete the Stripe checkout modal, payment IPC channels, preload
namespace and their types; "Remove ads with Pro" opens the web billing page
via license.openBilling. Support/refund copy names Payple.
- billingUrl() loses the Stripe-only success/canceled result option; the
Deno contract is regenerated.
- Migrations and the DB's accepted provider values are untouched (history).
- Docs and the backlog record the removal (MON-04, EXT-STRIPE-01, GAP-BILL-03).
Verified: typecheck (desktop/web/admin/api-client/mobile), contract:check,
deno check all functions, deno test 80/80, desktop 1478/1480 on the Electron
runtime (2 known environment failures), web and admin builds, release
metadata and mobile boundary self-tests, eslint on changed files.
21 KiB
02 — Repository & Infrastructure Map
Surface: whole repo Source of truth for: layout, workspaces, build/test commands, CI/CD, Docker, deploy, scripts, resources
1. Repository layout
D:/workspace/D3ROVoice
├── apps/
│ ├── desktop/ Electron app (npm workspace @d3ro/desktop)
│ ├── web/ Next.js console (npm workspace @d3ro/web); basePath `/app`, served at `d3ro.chanpaca.net/app`
│ ├── admin/ Next.js back office (npm workspace @d3ro/admin)
│ ├── mobile-rn/ React Native product mobile app (NOT an npm workspace)
│ ├── api-server/ ASP.NET Core 10 API (D3ROVoice.Api)
│ ├── api-server.Tests/ xUnit tests for the API
│ └── admin-swagger/ Static Swagger UI + openapi.json
├── packages/ Shared TS packages (npm workspaces)
│ ├── core/ @d3ro/core — types, errors, IPC channels, crypto, utils
│ ├── ui/ @d3ro/ui — web/desktop design system (MUI + tokens)
│ ├── ui-native/ @d3ro/ui-native — React Native design system
│ ├── i18n/ @d3ro/i18n — 12 locales, provider, formatters
│ └── api-client/ @d3ro/api-client — Supabase wrapper + shared types
├── server/
│ ├── supabase/ Supabase project: config.toml, migrations/, functions/, tests/
│ ├── cloudflare-worker/ Edge gateway (wrangler.toml + src/index.ts)
│ └── cloudflare-site-bridge/ Site bridge worker: forwards `/app/*` to WEB_APP_ORIGIN, rewrites upstream redirects, else proxies to Pages
├── site/ Vite landing site (deployed to Cloudflare Pages; GitHub Pages removed 2026-09-26, Wave 3)
├── resources/ icons/ (empty), sox/ (bundled Windows SoX binaries)
├── release/ Version identity SSOT + license/evidence public keys
├── scripts/ ~145 automation scripts + scripts/ci/ (31) + scripts/lib/
├── docs/ Design, phases, v2/v3 plans, deployment, map/ (this map)
├── memory/ Project status, handoffs, archive
├── tests/e2e/, test-results/ Root-level e2e + last-run artifacts
├── scratch/ Large local evidence (APKs, screenshots, DBs) — not build input
├── supabase/ Empty scaffolding (.branches/, snippets/) — real project is server/supabase
├── .gitlab-ci.yml GitLab CI (primary desktop/mobile release pipeline)
├── .forgejo/workflows/ Forgejo Actions (CI, site deploy to Cloudflare Pages, payple-renew cron, portable/release)
├── docker-compose.yml, docker-compose.nas.yml
├── apps/api-server/Dockerfile, apps/admin/Dockerfile, apps/web/Dockerfile
├── turbo.json, tsconfig.json, tsconfig.base.json, pnpm-workspace.yaml
├── package.json monorepo root, npm workspaces
├── CLAUDE.md Claude-specific project rules
└── AGENTS.md Cross-agent entry: rules + map obligation (this map's anchor)
Note: root package.json declares npm workspaces apps/desktop, apps/web, apps/admin, packages/*. pnpm-workspace.yaml also exists (apps/*, packages/*) but npm is the active toolchain. Mobile is intentionally outside the workspace.
2. Toolchain & versions
| Tool | Version | Source |
|---|---|---|
| Node | 24.19.0 | .nvmrc |
| TypeScript | 5.7 | root package.json |
| .NET SDK | 10.0.300 (rollForward latestPatch) | global.json |
| Deno | 2.8.1 | CI (edge-functions-quality) |
| JDK | 17 | mobile CI |
| Electron | 33.4.11 | apps/desktop/electron-builder.yml |
| koffi | 3.3.1 | apps/desktop/package.json — FFI into user32/kernel32 for foreground window title/pid/exe/bounds (utils/win32-foreground.ts). Chosen over get-windows because that package needs an install script this repo does not run; koffi ships N-API 8 prebuilds as optional deps. asarUnpack covers koffi/@koromix. |
| uiautomation / comtypes | 2.0.29 / 1.4.17 | apps/desktop/sidecar/requirements.txt (Windows only) — UI Automation snapshot behind GET /uia/focus (sidecar/uia_bridge.py). Dev install: apps/desktop/sidecar/.venv/Scripts/python.exe -m pip install uiautomation comtypes. |
| React Native | 0.85 | apps/mobile-rn/package.json |
| Turborepo | turbo.json tasks: build/typecheck/test/lint/dev | turbo.json |
3. Root scripts (package.json)
npm run dev # desktop dev (electron-vite)
npm run build # desktop production build
npm run build:admin # admin production build
npm run build:all | npm run ci # scripts/ci/build-all.mjs
npm run checksum # scripts/ci/generate-checksums.mjs
npm run version:check | version:sync
npm run release:metadata[:test]
npm run release:forgejo[:check] # canonical Forgejo publisher/feed
npm run release:tag # annotated/signed immutable release tag
npm run security:secrets[:test] # hardcoded-secret scanner
npm run check:desktop-renderer[:test] # built renderer pages reference only assets on disk
npm run test:e2e:red # content-report red e2e
npm run release:mobile:boundary[:test]
npm run release:mobile:config[:test]
npm run release:mobile:build-config:test
npm run release:play:assets[:test]
npm run typecheck # all workspaces
npm run test # all workspaces
npm run lint # eslint apps/desktop apps/web apps/admin packages
npm run format # prettier
npm run typecheck:mobile # apps/mobile-rn tsc (outside npm workspaces)
npm run lint:mobile # apps/mobile-rn eslint --max-warnings=0
npm run test:mobile # apps/mobile-rn jest
npm run verify:all # aggregate: workspaces + mobile (typecheck/lint/test)
Per-app commands that matter:
| App | Commands |
|---|---|
| desktop | npm run dev --workspace=@d3ro/desktop, build, typecheck, test (vitest), playwright e2e; local STT engine: npm --prefix apps/desktop run sidecar:setup then sidecar:build (PyInstaller → sidecar-dist/sidecar), full local Windows package: npm --prefix apps/desktop run dist:win:full; setup:sox re-downloads the bundled SoX |
| mobile-rn | npm run typecheck:mobile / lint:mobile / test:mobile (root), or npm --prefix apps/mobile-rn run lint/typecheck/test; android gradle builds, Maestro E2E |
| api-server | dotnet build, dotnet test (also apps/api-server.Tests) |
| web | next build, playwright e2e in apps/web/e2e |
| admin | next build (build:admin) |
Desktop GUI execution rule (from CLAUDE.md / .agents/rules/): run via run-desktop.bat or an external terminal; do not background-launch GUI from an agent subshell.
4. Shared packages
See 03-shared-packages.md. Summary:
| Package | Provides |
|---|---|
@d3ro/core |
Domain types, D3ROError/ErrorCode, IPC channel SSOT, key-binding SSOT (./keybinding), constants, crypto-license, pii-redactor, secure-memory, supabase-config, meeting-markdown, markdown-to-docx; has vitest tests (packages/core/vitest.config.ts, npm run test --workspace=@d3ro/core) |
@d3ro/ui |
Theme tokens, CSS vars, MUI DS components (web/desktop) |
@d3ro/ui-native |
RN design system (MetalCard, PhosphorText, Led, PhysicalButton, WaveBars, …) |
@d3ro/i18n |
12 locales, I18nProvider, t(), date/number/relative formatters |
@d3ro/api-client |
Supabase browser/server clients, meetings/history/usage/transcribe wrappers, shared types; has vitest tests |
5. CI/CD
.github/workflows/was deleted 2026-09-26 (Wave 3,dc43884): the repository has Forgejo and GitLab remotes but no GitHub remote, so nothing under it ever ran — including the daily Payple renewal cron. CI now runs only on Forgejo Actions (.forgejo/workflows/); see below.
GitLab CI (.gitlab-ci.yml)
Stages validate → test → build → e2e → package → publish → deploy. Primary pipeline for desktop Windows/macOS releases (Forgejo Generic Registry is the canonical updater feed; GitLab project 1172 is a legacy mirror) and production mobile releases (mobile-production-release, manual/protected). Admin NAS deploy job is intentionally disabled. package-windows/package-macos build the faster-whisper sidecar (sidecar:setup → sidecar:build) and run scripts/ci/verify-sidecar-bundle.mjs before electron-builder, so a release can never ship without the local STT engine. Every pipeline that runs npm run build --workspace=@d3ro/desktop (.forgejo CI/release/portable) then runs scripts/ci/verify-desktop-renderer-bundles.mjs, which fails packaging when a renderer page references an asset the build did not emit (GAP-INFRA-05).
Forgejo Actions (.forgejo/workflows/)
ci.yml — CI is now Forgejo-only (.github/workflows deleted 2026-09-26, Wave 3, dc43884). Jobs: quality (version:check, contract:check, release:metadata:test, secret scan, mobile release/config/build-config self-tests, Play asset contract, lint, typecheck, Vitest), build-validation, mobile-quality, edge-functions (Deno), api-server-tests.
payple-renew.yml — daily cron (+ manual dispatch) → payple-renew edge function; ported from the never-run .github/workflows/payple-renew.yml because the repository has no GitHub remote. Requires Forgejo secrets SUPABASE_URL, CRON_SECRET.
portable.yml — 태그/수동 실행으로 서명 없이 portable 채널(95MiB 7z 분할 볼륨 + Scoop 매니페스트 + 설치 스크립트)을 게시한다. WIN_CSC_* 불필요, updater feed는 건드리지 않는다.
deploy-site.yml — 유일한 사이트 배포 경로(Linux, Cloudflare Pages). Windows 수동 중복 워크플로(deploy-site-windows.yml)는 2026-09-26 삭제됐다(dc43884). build site, write release identity, deploy to Cloudflare Pages d3ro (d3ro.pages.dev), verify live commit/version, app-links, legal URLs. 커스텀 도메인 d3ro.chanpaca.net 은 Pages 커스텀 도메인이 DNS CNAME을 요구하므로, DNS를 건드릴 수 없는 동안은 Workers 라우트 브리지 server/cloudflare-site-bridge/(d3ro.chanpaca.net/* → Pages 프록시, 수동 npx wrangler deploy)가 서빙한다. CNAME을 추가한 뒤 브리지를 삭제하면 Pages 커스텀 도메인으로 직접 서빙된다(GAP-REL-09b).
release.yml — tag-triggered Windows build (signed) + publish-forgejo-release.mjs to the canonical Forgejo feed/release hub.
6. Docker & deployment
| File | Purpose |
|---|---|
apps/admin/Dockerfile |
Next.js standalone runner for .next/standalone (the old root Dockerfile.admin 3-stage build was a duplicate, deleted 2026-09-26, Wave 3, cd9d199) |
apps/api-server/Dockerfile |
Multi-stage .NET 10 (sdk → aspnet runtime), port 5000, VOLUME /app/data; no longer serves static files (wwwroot removed — see 07-api-server.md) |
apps/web/Dockerfile |
Next.js standalone runner for @d3ro/web, basePath /app, port 3002 (added 2026-09-26, Wave 3, b6fe588) |
docker-compose.yml |
Dev/self-host: d3ro-api-server (5050→5000, ./data volume), d3ro-admin (3001), d3ro-web (3002, @d3ro/web basePath /app, Supabase/Payple keys as build args), optional ollama (profile ai, 11434) |
docker-compose.nas.yml |
NAS: prebuilt d3ro-voice-api:latest / d3ro-voice-admin:latest / d3ro-voice-web:latest (3002) / d3ro-whisper (faster-whisper, OpenAI-compatible). No legal-doc wwwroot mount and no NEXT_PUBLIC_API_URL — both were dropped 2026-09-26 (Wave 3, b6fe588); legal pages are served only from site/public. |
Deploy scripts: scripts/deploy-nas.ps1, scripts/deploy-nas.sh, scripts/nas-control.sh (start/stop/restart/status/logs/backup/update). scripts/deploy-site-to-nas.js was deleted 2026-09-26 (Wave 3, cd9d199) — site deploy is Cloudflare Pages only (.forgejo/workflows/deploy-site.yml).
Public endpoints (production): https://d3ro.chanpaca.net — 랜딩/다운로드 센터(site/, Pages d3ro 배포본을 Workers 라우트 브리지가 서빙), https://d3ro.chanpaca.net/app — 웹앱(apps/web, basePath /app; 사이트 브리지 워커가 WEB_APP_ORIGIN(터널 호스트)으로 전달하며, 호스트가 비어 있으면 /app은 503을 반환한다 — 터널 호스트 설정 대기), https://admin.chanpaca.net (admin CRM). Edge: server/cloudflare-worker proxying to the NAS origin, plus a Cron Trigger (* * * * *) that drains the Supabase push outbox via send-push?mode=drain (src/push-drain.ts; needs SUPABASE_URL var + SUPABASE_SERVICE_ROLE_KEY secret). Tunnel: Cloudflare Tunnel kd-nas (NAS 포털/API는 현재 이 호스트네임에 바인딩되어 있지 않다).
7. server/supabase (backend)
config.toml— projectd3ro-voice, ports 55321-55324, DB major 17, auth redirects (localhost,d3ro.chanpaca.net,d3ro-voice://auth-callback), providers Google/GitHub/Apple.migrations/— 63 SQL migrations (schema, RLS, auth triggers, storage, team invites, knowledge/pgvector, push outbox, Payple billing (legacy Stripe columns/provider values kept for history), admin roles, mobile platform/monetization, atomic command reorder, device revocation, content reporting, audit log, meeting documents, STT quota reservations, ad reward replay protection, team activity feed).functions/— ~24 Deno Edge Functions (stt-proxy,llm-proxy,content-report,generate-meeting-document,embed-chunks,search-knowledge,realtime-token,team-invite,team-accept,send-push,account-delete,admin-users,admin-subscriptions,admin-payments,admin-audit-log, billingbilling-catalog/payple-checkout/payple-manage/payple-renew/payple-webhook,iap-verify,admob-ssv,google-play-rtdn; Stripe functions removed 2026-09-26). Shared contracts infunctions/_shared/— push transports now includewebpush.ts(VAPID + RFC 8291) andapns.ts(.p8 token) alongside FCM. CI (edge-functions-quality) runsdeno check+deno testand also the Cloudflare worker drain test.tests/— integration/E2E for content report, mobile platform/recording/reward-race, payments, mobile release preflight, push, team push security, STT quota.
Full detail: 09-supabase-backend.md.
8. scripts/ groups
- CI (
scripts/ci/, 33 files): build/version/release (build-all,sync-version,generate-checksums,verify-release-metadata,create-release-tag,extract-release-notes), security (check-no-hardcoded-secrets), mobile release gates (verify-mobile-release-boundary/-config/-build-config,verify-android-artifact/-app-links,verify-play-store-assets,prepare-whisper-model,create-mobile-release-evidence,prepare-mobile-release-publication, emulator/CSPRNG gates), keys (create-desktop-license-keypair,create-release-evidence-key, etc.), publish (publish-forgejo-releasecanonical,publish-gitlab-releasemirror; the legacysync-and-publish-forgejo-release/upload-asset-to-forgejo-releasescripts were deleted 2026-09-26, Wave 3,cd9d199), env/tooling (bootstrap-linux-toolchain.sh,audit-nas-stt-config.ps1, mobile local E2E scripts). - Deploy/release:
deploy-nas.ps1/.sh,nas-control.sh,publish-gh.ps1,gen-keystore.js(deploy-site-to-nas.jsdeleted 2026-09-26, Wave 3,cd9d199— site deploy is Cloudflare Pages only). - GCP/Google OAuth automation + inspection (~70
*.mjs):auto-configure-oauth,automate-google-oauth,setup-consent,create-*-client,check-*,inspect-*— mostly one-off/browser-driven console automation. - AdMob console automation:
admob-probe.mjs(read-only login/app/ad-unit probe),admob-login.mjs+run-admob-login.bat(one interactive headful Chrome login into a persistent profile),admob-automate.mjs(dry-run by default;--applycreates/verifies banner+rewarded units and reports Play-store link). Usesplaywrightwithchannel: 'chrome'and the gitignored.chrome-playwright-profile. - E2E / verification:
e2e-desktop-*.js,real-app-multi-tab-e2e.js,test-and-capture-all-10-ad-services.js,verify-live-production-d3ro.js. - Screenshots/captures:
capture-*.js. - Forgejo ops:
check-forgejo-actions-runs.js,capture-forgejo-*.js.
scripts/is large and partially scratch. Preferscripts/ci/*for anything release-gated, andserver/supabase/testsfor backend integration.
9. Release & versioning SSOT
| File | Purpose |
|---|---|
release/product-version.json |
version 1.6.0, androidVersionCode/iosBuildNumber 1060000, releaseDate 2026-09-24, desktop license keyId |
release/android-release-identity.json |
package com.d3ro.voice, Play app ID, app-signing SHA-256, upload cert SHA-256, evidence keyId, AdMob unit IDs |
release/desktop-license-public.pem |
Ed25519 public key for desktop offline licenses |
release/mobile-release-evidence-public.pem |
Ed25519 public key for mobile release evidence |
apps/desktop/electron-builder.yml |
appId com.d3ro.voice, NSIS x64 (forced code signing), macOS DMG/ZIP arm64, generic Forgejo publish feed, asarUnpack native modules + @ffmpeg-installer, extraResources (icons, sounds, sox, sidecar, ffmpeg, ollama) |
apps/desktop/src/main/update-feed.ts |
Auto-update feed SSOT (canonical Forgejo + legacy GitLab mirror, channels) |
release/update-policy.json |
Update policy SSOT (channels, minimum supported version, forced update, delta/full, staged rollout, kill switch) |
site/src/release.ts |
Download-center desktop release contract (installer filename + release date); version and date are kept on the SSOT by npm run version:sync (drifted to 1.2.0 once — GAP-REL-08). apps/web/src/lib/desktop-release.ts and the apps/web download/releases pages were deleted 2026-09-26 (Wave 3, cd9d199) — site/ is the only download surface, and /download//releases on apps/web now redirect to it |
apps/desktop/src/main/update-policy.ts |
Policy parsing/decision logic |
scripts/ci/publish-forgejo-release.mjs |
Canonical Forgejo registry + Release + feed publisher |
Version sync is enforced by scripts/ci/sync-version.mjs and verify-release-metadata.mjs; npm run version:check should be clean.
10. Resources & tests
resources/sox/— bundled Windows SoX (sox.exe+ DLLs) for audio capture.resources/ffmpeg/— optional bundled ffmpeg (CI or manual);getFfmpegPath()also resolves the@ffmpeg-installer/ffmpegbinary fromapp.asar.unpacked.apps/desktop/sidecar-dist/— PyInstaller sidecar bundle consumed byextraResources(gitignored; built bynpm --prefix apps/desktop run sidecar:build).resources/icons/— empty; electron-builder falls back tobuild/icon.ico|png.- Desktop tests:
apps/desktop/tests/(vitest unit + playwright e2e),apps/desktop/test-results/. - Mobile tests:
apps/mobile-rn/__tests__/(57 suites / 353 tests per mobile SSOT),.maestro/+.maestro-output/E2E evidence. Note: a full parallel Jest run can hit the 5s render timeout on slow machines; re-run the failing spec in isolation before treating it as a regression. - Web tests:
apps/web/e2e/(playwright). - API tests:
apps/api-server.Tests/(xUnit). - Root:
tests/e2e/,test-results/.last-run.json.
11. Known infrastructure gaps
See 11-gap-backlog.md for the maintained list (INFRA-*). Headlines:
apps/mobile-rnis not an npm workspace member; usetypecheck:mobile/lint:mobile/test:mobileorverify:all.- Admin NAS deploy job disabled in GitLab CI; the
package-admin-docker→GHCR publish step lived only in the now-deleted.github/workflows/release.yml(2026-09-26,dc43884) and was not ported to Forgejo, so admin image publish + NAS compose update is manual (GAP-INFRA-02). - Two identity systems (.NET JWT/SQLite vs Supabase); a canonical resolver now exists in
@d3ro/core/entitlementbut web/mobile/.NET adoption is incremental (11GAP-ID-02).
운영 자격 증명 위치 (2026-09-26, 값은 적지 않는다)
재로그인 없이 배포·운영하려고 모은 목록이다. 로컬 값은 루트 .env(gitignore), 이름 목록은 .env.example이 정본이다.
| 용도 | 루트 .env 키 |
다른 곳의 같은 값 |
|---|---|---|
| Cloudflare 터널 ingress·DNS·Pages·브리지 워커 | CLOUDFLARE_API_TOKEN(토큰 이름 "D3RO Voice — tunnel + DNS"), CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_ZONE_ID, CLOUDFLARE_TUNNEL_ID(kd-nas) |
Forgejo 시크릿 CF_API_TOKEN·CF_ACCOUNT_ID |
| Supabase CLI·Management API | SUPABASE_ACCESS_TOKEN(만료 2027-09-25), SUPABASE_PROJECT_REF |
— |
| Payple 정기 갱신 cron | CRON_SECRET |
Supabase 함수 시크릿 CRON_SECRET, Forgejo 시크릿 CRON_SECRET |
NAS 배포(ssh 별칭 yunchan-nas) |
NAS_HOST, NAS_USER, NAS_SSH_PORT, NAS_DEPLOY_PATH |
— |
| NAS compose 필수 값 | SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, ADMIN_BOOTSTRAP_TOKEN, API_SERVER_URL 외 |
NAS /volume1/docker/d3ro/.env |
| 웹앱 이미지 build arg | NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY (NEXT_PUBLIC_PAYPLE_CLIENT_KEY는 보류, GAP-BILL-02) |
— |
| Forgejo API | FORGEJO_TOKEN |
Forgejo 시크릿 FORGEJO_TOKEN |
- 공개 라우팅:
d3ro.chanpaca.net→ 브리지 워커 →/app·/api·/health는 터널 kd-nas(NAS 3002·5050), 그 밖은 Pagesd3ro. 터널 ingressd3ro.chanpaca.netpath^/app→http://192.168.0.39:3002. - Supabase Auth:
site_url=https://d3ro.chanpaca.net/app, 허용 목록에/app/**.