Desktop transcript edits, auto-polish and speaker labels now reach the phone, which draws meetings from transcript segments, and prompt edits of the four shared preset commands are used by the phone's commands. Bumps the product version to 1.9.0 (Android/iOS build 1090000).
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://d3ro-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.9.0, androidVersionCode/iosBuildNumber 1090000, releaseDate 2026-09-27, 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/**.