# 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) │ ├── 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) ├── site/ Vite landing site (deployed to Cloudflare Pages + GitHub Pages) ├── 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 ├── .github/workflows/ GitHub Actions (6) ├── .gitlab-ci.yml GitLab CI (primary desktop/mobile release pipeline) ├── .forgejo/workflows/ Forgejo Actions (site deploy to Cloudflare Pages) ├── docker-compose.yml, docker-compose.nas.yml ├── Dockerfile.admin, apps/api-server/Dockerfile, apps/admin/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` | | 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`) ```bash 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 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`](./03-shared-packages.md). Summary: | Package | Provides | |---|---| | `@d3ro/core` | Domain types, `D3ROError`/`ErrorCode`, IPC channel SSOT, constants, `crypto-license`, `pii-redactor`, `secure-memory`, `supabase-config`, `meeting-markdown`, `markdown-to-docx` | | `@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 Actions (`.github/workflows/`) | Workflow | Purpose | |---|---| | `ci.yml` | Main CI: `code-quality` (secret scan, mobile release/config/build-config self-tests, Play asset contract, lint, typecheck), `api-server-tests`, `edge-functions-quality` (Deno), `test-matrix` (win/mac/ubuntu vitest), `build-validation` (desktop win, admin ubuntu), `mobile-android` (debug/CSPRNG/E2E APKs + verifiers), `mobile-emulator-e2e` (API 35 + Maestro 2.7.0) | | `release.yml` | On tag `v*.*.*`: preflight → `package-windows` (NSIS) → `package-macos` (DMG/ZIP arm64) → `package-android` (signed APK/AAB + evidence) → `package-admin-docker` (GHCR) → `publish-release` (checksums + GitHub Release + Forgejo canonical publish) | | `deploy-site.yml` | On `site/**`: build Vite site, boundary self-test, deploy GitHub Pages | | `build-mac.yml` | Manual macOS build (arm64/x64), sox + PyInstaller sidecar + electron-rebuild | | `payple-renew.yml` | Daily cron → `payple-renew` edge function | | `release-signing-ca.yml` | Manual Windows (Azure Trusted Signing) / macOS notarize build+sign | ### 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. ### Forgejo Actions (`.forgejo/workflows/`) `deploy-site.yml` / `deploy-site-windows.yml` — build `site`, write release identity, deploy to Cloudflare Pages `d3ro` (`d3ro.chanpaca.net`), verify live commit/version, app-links, legal URLs. `release.yml` — tag-triggered Windows build (signed) + `publish-forgejo-release.mjs` to the canonical Forgejo feed/release hub. --- ## 6. Docker & deployment | File | Purpose | |---|---| | `Dockerfile.admin` | 3-stage Next.js admin build (node 24.19.0-alpine, port 3001) | | `apps/admin/Dockerfile` | Next.js standalone runner for `.next/standalone` | | `apps/api-server/Dockerfile` | Multi-stage .NET 10 (sdk → aspnet runtime), port 5000, `VOLUME /app/data` | | `docker-compose.yml` | Dev/self-host: `d3ro-api-server` (5050→5000, `./data` volume), `d3ro-admin` (3001), optional `ollama` (profile `ai`, 11434) | | `docker-compose.nas.yml` | NAS: prebuilt `d3ro-voice-api:latest` + `d3ro-voice-admin:latest`; API mounts `/volume1/docker/d3ro/wwwroot/{privacy,terms,delete-account,legal.css}` read-only | Deploy scripts: `scripts/deploy-nas.ps1`, `scripts/deploy-nas.sh`, `scripts/deploy-site-to-nas.js`, `scripts/nas-control.sh` (start/stop/restart/status/logs/backup/update). Public endpoints (production): `https://d3ro.chanpaca.net` (portal/API), `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`. --- ## 7. `server/supabase` (backend) - `config.toml` — project `d3ro-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/Stripe billing, 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/` — **~27 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`, billing `billing-catalog`/`stripe-checkout`/`stripe-portal`/`stripe-webhook`/`payple-checkout`/`payple-manage`/`payple-renew`/`payple-webhook`, `iap-verify`, `admob-ssv`, `google-play-rtdn`). Shared contracts in `functions/_shared/` — push transports now include `webpush.ts` (VAPID + RFC 8291) and `apns.ts` (.p8 token) alongside FCM. CI (`edge-functions-quality`) runs `deno check` + `deno test` and 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`](./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-release` canonical, `publish-gitlab-release` mirror; legacy `sync-and-publish-forgejo-release`, `upload-asset-to-forgejo-release`), env/tooling (`bootstrap-linux-toolchain.sh`, `audit-nas-stt-config.ps1`, mobile local E2E scripts). - **Deploy/release:** `deploy-nas.ps1/.sh`, `deploy-site-to-nas.js`, `nas-control.sh`, `publish-gh.ps1`, `gen-keystore.js`. - **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; `--apply` creates/verifies banner+rewarded units and reports Play-store link). Uses `playwright` with `channel: '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. Prefer `scripts/ci/*` for anything release-gated, and `server/supabase/tests` for backend integration. --- ## 9. Release & versioning SSOT | File | Purpose | |---|---| | `release/product-version.json` | version `1.3.0`, `androidVersionCode`/`iosBuildNumber` `1030001`, releaseDate, 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) | | `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/ffmpeg` binary from `app.asar.unpacked`. - `apps/desktop/sidecar-dist/` — PyInstaller sidecar bundle consumed by `extraResources` (gitignored; built by `npm --prefix apps/desktop run sidecar:build`). - `resources/icons/` — empty; electron-builder falls back to `build/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`](./11-gap-backlog.md) for the maintained list (`INFRA-*`). Headlines: - `apps/mobile-rn` is not an npm workspace member; use `typecheck:mobile`/`lint:mobile`/`test:mobile` or `verify:all`. - Admin NAS deploy job disabled in GitLab CI; admin ships via GitHub/GHCR + manual NAS compose. - Two identity systems (.NET JWT/SQLite vs Supabase); a canonical resolver now exists in `@d3ro/core/entitlement` but web/mobile/.NET adoption is incremental (`11` GAP-ID-02).