# Repository and microservice reorganization — specification | Field | Value | |---|---| | Author | Anton Afanasyeu | | Revision | R1 | | Creation date | 2026-06-18 | | Last modification date | 2026-06-22 | | Co-authored | Cursor Agent (project assistant) | | Severity | high | | State | accepted | | Document type | spec | | Supersedes | — | | Design review | [docs/DRs/20260618_repos_reorganizing.md](../DRs/20260618_repos_reorganizing.md) (frozen R1.1) | --- **Document type:** SPEC **Source draft:** [docs/drafts/20260618_repos_reorganizing.txt](../drafts/20260618_repos_reorganizing.txt) **Design review:** [docs/DRs/20260618_repos_reorganizing.md](../DRs/20260618_repos_reorganizing.md) (frozen) **PDF:** [20260618_repos_reorganizing.pdf](20260618_repos_reorganizing.pdf) · Regenerate: `bash scripts/build-all-docs-pdf.sh` **Status:** **Accepted** — implementation phased per §9; monolith remains deployable until each migration step completes **Scope:** Split `git://f0xx.org/android_cast` monolith into git org **`ac`** on the **f0xx.org git server** — full microservice end-state, VM-first cloud path **Related:** [INFRA.md](../INFRA.md) · [BUILD_DEPLOY.md](../BUILD_DEPLOY.md) · [specs/20100612_1_scaling.md](20100612_1_scaling.md) · [orchestration/sim/cluster0/ARCHITECTURE.md](../../orchestration/sim/cluster0/ARCHITECTURE.md) **Documentation index:** [README.md](../README.md) **VCS (normative):** Canonical remotes are **`git://f0xx.org/ac/`** — repos are **created on the PO git server** (bare repos, hooks, ACLs). **Gitea** at `…/git/` is a **browse UI** over mirrors only. Optional HTTPS read-only: [Appendix B](#appendix-b-optional-https-read-only-git-access). --- ## Table of contents - [1. Purpose](#1-purpose) - [2. Scope](#2-scope) - [3. Requirements](#3-requirements) - [3.1 Must](#31-must) - [3.2 Should](#32-should) - [3.3 Non-goals](#33-non-goals) - [4. Baseline (current monolith)](#4-baseline-current-monolith) - [5. Target architecture](#5-target-architecture) - [6. Git repository catalog](#6-git-repository-catalog) - [7. Public URL specification](#7-public-url-specification) - [8. Dependency graphs](#8-dependency-graphs) - [9. Migration plan](#9-migration-plan) - [10. Infrastructure and cloud](#10-infrastructure-and-cloud) - [11. ac-workspace and OTA versioning](#11-ac-workspace-and-ota-versioning) - [12. Verification and acceptance](#12-verification-and-acceptance) - [13. Risks and mitigations](#13-risks-and-mitigations) - [14. Related documents](#14-related-documents) - [15. Changelog](#15-changelog) - [Appendix A — Git disk paths and remotes](#appendix-a-git-disk-paths-and-remotes) - [Appendix B — Optional HTTPS read-only git access](#appendix-b-optional-https-read-only-git-access) --- ## 1. Purpose Define the **normative** end-state and **ordered migration** for splitting the AndroidCast monolith (`git://f0xx.org/android_cast`) into: - Independent **git repositories** under org **`ac`** on the f0xx.org **git server** - **Microservice APIs** (OpenAPI) and **thin backend UIs** - **One public URI root per microproject** under `https://apps.f0xx.org/app/androidcast_project/` - **Phased delivery** without a forced big-bang during alpha **Identity gate:** `ac-ms-identity` (migration Step 6) MUST complete before any other domain microservice split. --- ## 2. Scope | In scope | Out of scope (this SPEC) | |----------|---------------------------| | Repo catalog, disk paths, git remotes (Appendix A) | K8s / service mesh (optional later) | | Path-per-microproject URL map (§7) | SFU/media transcode runtime (F1/F2 repos listed only) | | 13-step migration plan (§9) | Rewriting mobile codec stack | | Platform libs (Composer) before MS split | Gitea as repo provisioning UI | | Optional HTTPS read-only git (Appendix B) | Merging AndroidCast with unrelated git hosting products | | ac-workspace manifest + OTA multi-repo SHAs | Unified monorepo build in ac-workspace | | VM lift-and-shift mapping to [scaling SPEC](20100612_1_scaling.md) | | | Unrelated nested git trees under `examples/` (not product submodules) | | **Git submodules (monolith → split):** **only** `third-party/*` (mobile deps) and `backend/url-shortener`. **Alpha:** Production MAY continue on the monolith until migration Step 8+ per service; each step MUST leave prior steps deployable. --- ## 3. Requirements ### 3.1 Must | ID | Requirement | |----|-------------| | R-VCS-1 | Canonical remote: `git://f0xx.org/ac/`; PO creates bare repos on git server | | R-VCS-2 | Gitea at `…/git/` is browse-only mirror; agents MUST NOT provision repos in Gitea | | R-URL-1 | Public base prefix: `/app/androidcast_project/` | | R-URL-2 | Target URLs MUST NOT use `/crashes/` or `?view=` (legacy 301 only, ≥ one release) | | R-URL-3 | One path root per microproject (§7.2) | | R-URL-4 | Auth at project root: `/login`, `/logout`, `/register`, `/two-factor`, `/verify-email` | | R-ARCH-1 | Full microservice end-state per §5–§6 | | R-ARCH-2 | `ac-ms-identity` gate before domain MS extraction (Step 6) | | R-ARCH-3 | `ac-scripts` separate; `ac-deploy` consumes it | | R-MIG-1 | Migration steps §9 executed in order; gates MUST NOT be skipped | | R-SUB-1 | Monolith submodules: **only** `third-party/*` and `backend/url-shortener` | | R-SUB-2 | No other nested git submodules in product repos or `ac-workspace` manifests | | R-TEST-1 | Each step verification in §9 and §12 MUST pass before the next step starts | ### 3.2 Should | ID | Requirement | |----|-------------| | R-VCS-3 | Optional HTTPS read-only clones via `git-r.f0xx.org` (Appendix B) | | R-VCS-4 | Disk layout `ac/` under configurable `GIT_ROOT` (default `/var/git/`) | | R-URL-5 | Cookie path `/app/androidcast_project` until SSO replaces shared PHP session | | R-DOC-1 | OpenAPI stubs per MS in `ac-docs/openapi/` after Step 4 | ### 3.3 Non-goals - Big-bang cutover before identity MS and edge shell exist - `ac-ms-vcs` application repo (VCS is infrastructure) - `https://f0xx.org/...` as git smart HTTP endpoint - AndroidCast code reading `/etc/git-ro/` token ACL files (Appendix B is ops-only) --- ## 4. Baseline (current monolith) ### 4.1 Monolith map | Monolith path | Future repo(s) | Notes | |---------------|----------------|-------| | `app/`, `ndk/`, `gradle/` | ac-mobile-android | third-party submodules | | `desktop/session-studio/` | ac-session-studio | Low coupling | | `docs/` | ac-docs | | | `scripts/` | ac-scripts | | | `orchestration/` | ac-deploy | cluster0, docker | | `examples/crash_reporter/backend/` | ac-ms-* + ac-be-* | **32 PHP classes**, 24 APIs | | `examples/build_console/` | ac-ms-build, ac-be-builder | Same DB `users` | | `examples/app_hub/` | ac-be-hub | Loads shared platform-web assets (not `/crashes/assets/`) | | `backend/url-shortener/` | ac-ms-url-shortener | Existing submodule | ### 4.2 What is not a git repo - **Gitea** — read-only web UI over git mirrors on BE (`:3000`); **not** where repos are created - **MariaDB**, **Janus** (:8089) ### 4.3 Git submodules (monolith — only these) | Path | Role after split | |------|------------------| | `third-party/*` | Native/codec deps (submodules of **ac-mobile-android**) | | `backend/url-shortener/` | URL shortener → **ac-ms-url-shortener** | No other directories in the monolith carry git submodules. Post-split, **ac-deploy** may submodule **ac-scripts** (repo-to-repo pin). --- ## 5. Target architecture ```text Platform (Composer / static — not runtime MS) ac-platform-php, ac-platform-db, ac-platform-web, ac-scripts, ac-docs Infrastructure ac-deploy submodules ac-scripts; docker, cluster0, nginx ac-platform-edge nginx BFF: routes + session cookie domain ac-workspace optional clone manifest (no unified build) Clients ac-mobile-android, ac-mobile-ios (future), ac-session-studio Microservices (OpenAPI) ac-ms-identity → ac-ms-rbac → ac-ms-devices ac-ms-issues, ac-ms-tickets, ac-ms-graphs, ac-ms-remote-access ac-ms-url-shortener, ac-ms-build, ac-ms-ota, ac-ms-notifications (later) Backend UI (thin → MS APIs) ac-be-hub, ac-be-issues, ac-be-tickets, ac-be-graphs, ac-be-remote-access, ac-be-access, ac-be-builder, ac-be-auth ``` **Consolidations vs initial PO sketch:** | Initial name | R0 decision | |--------------|-------------| | ac-be-console (monolith) | Dissolved into ac-be-issues … ac-be-access | | ac-ms-tracker | **ac-ms-issues** + **ac-ms-tickets** | | ac-ms-vpn-rssh + ac-ms-vpn-wireguard | **ac-ms-remote-access** (single control plane) | | ac-ms-analytics | **ac-ms-graphs** (+ BI later) | | ac-ms-orchestration | **ac-ms-build** + **ac-deploy** | | ac-ms-vcs | **Not a repo** — Gitea is infra/UI only; git server is source of truth | --- ## 6. Git repository catalog **Canonical remote:** **`git://f0xx.org/ac/`** — PO provisions **bare repos on the git server** (org namespace `ac`). **Browse UI (optional):** **`https://apps.f0xx.org/app/androidcast_project/git/ac/`** — Gitea mirror for humans; push/fetch use git server. **Legacy:** **`git://f0xx.org/android_cast`** → read-only mirror until retired. ### 6.1 Structure rationale (R1) The catalog below is the **recommended end-state** for this project: - **Platform libs (P\*)** before microservices — avoids auth/DB duplication across 10+ PHP trees. - **Thin BE UI (B\*)** separate from **MS APIs (S\*)** — matches PO’s **one public URI per microproject** (§7). - **ac-deploy + ac-scripts** as the only deploy orchestration entry — fits VM roles in scaling SPEC. - **No `ac-ms-vcs` repo** — VCS is infrastructure, not application code. PO’s initial sketch used deeper folder names (`ac-backend/*`, `ac-microservice/*`). **R1 keeps the flatter `ac-` list** — easier permissions, clone URLs, and OTA manifest entries. Minor renames are acceptable; **dependency order and URI split are locked**. | ID | Repo | Git URL | Role | |----|------|---------|------| | W0 | ac-workspace | `git://f0xx.org/ac/ac-workspace` | Optional submodule manifest | | P0 | ac-platform-php | `git://f0xx.org/ac/ac-platform-php` | Composer: auth client, PDO, HTTP | | P1 | ac-platform-db | `git://f0xx.org/ac/ac-platform-db` | SQL migrations | | P2 | ac-platform-web | `git://f0xx.org/ac/ac-platform-web` | Shared CSS/JS/theme | | P3 | ac-platform-edge | `git://f0xx.org/ac/ac-platform-edge` | nginx routes + proxy | | P4 | ac-scripts | `git://f0xx.org/ac/ac-scripts` | CI, OTA, native codecs, PDF | | P5 | ac-docs | `git://f0xx.org/ac/ac-docs` | Documentation | | D0 | ac-deploy | `git://f0xx.org/ac/ac-deploy` | docker, cluster0; **uses P4** | | C0 | ac-mobile-android | `git://f0xx.org/ac/ac-mobile-android` | Android + ndk + third-party | | C1 | ac-mobile-ios | `git://f0xx.org/ac/ac-mobile-ios` | Future iOS | | C2 | ac-session-studio | `git://f0xx.org/ac/ac-session-studio` | Desktop analyzer | | S0 | ac-ms-template | `git://f0xx.org/ac/ac-ms-template` | New MS cookiecutter | | S1 | ac-ms-identity | `git://f0xx.org/ac/ac-ms-identity` | Users, login, 2FA, sessions | | S2 | ac-ms-rbac | `git://f0xx.org/ac/ac-ms-rbac` | Companies, privileges | | S3 | ac-ms-devices | `git://f0xx.org/ac/ac-ms-devices` | Device registry | | S4 | ac-ms-issues | `git://f0xx.org/ac/ac-ms-issues` | Crash/report ingest | | S5 | ac-ms-tickets | `git://f0xx.org/ac/ac-ms-tickets` | Ticket workflow | | S6 | ac-ms-graphs | `git://f0xx.org/ac/ac-ms-graphs` | Graph ingest/query | | S7 | ac-ms-remote-access | `git://f0xx.org/ac/ac-ms-remote-access` | WG + RSSH API | | S8 | ac-ms-url-shortener | `git://f0xx.org/ac/ac-ms-url-shortener` | Short links API | | S9 | ac-ms-build | `git://f0xx.org/ac/ac-ms-build` | Build worker API | | S10 | ac-ms-ota | `git://f0xx.org/ac/ac-ms-ota` | Channel manifests | | S11 | ac-ms-notifications | `git://f0xx.org/ac/ac-ms-notifications` | SMTP (post-alpha) | | B0 | ac-be-hub | `git://f0xx.org/ac/ac-be-hub` | Landing | | B1 | ac-be-issues | `git://f0xx.org/ac/ac-be-issues` | Issues UI | | B2 | ac-be-tickets | `git://f0xx.org/ac/ac-be-tickets` | Tickets UI | | B3 | ac-be-graphs | `git://f0xx.org/ac/ac-be-graphs` | Graphs UI | | B4 | ac-be-remote-access | `git://f0xx.org/ac/ac-be-remote-access` | RA admin UI | | B5 | ac-be-access | `git://f0xx.org/ac/ac-be-access` | RBAC admin UI | | B6 | ac-be-builder | `git://f0xx.org/ac/ac-be-builder` | Builder UI | | B7 | ac-be-auth | `git://f0xx.org/ac/ac-be-auth` | Login/register shell | | F1 | ac-ms-sfu-signaling | `git://f0xx.org/ac/ac-ms-sfu-signaling` | Future F1 | | F2 | ac-ms-media-transcode | `git://f0xx.org/ac/ac-ms-media-transcode` | Future VOD | Third-party codecs remain **submodules of C0** (upstream URLs); optional **git-server mirrors** under `ac/` for backup (sync scripts, not Gitea-as-SoT). ### 6.2 Repo creation waves (git server) PO creates bare repos on the **git server** in waves (Gitea mirror sync follows). Agent/docs do **not** “create repos in Gitea.” ```mermaid flowchart LR W1[Wave 1: docs scripts deploy session-studio url-shortener] --> W2[Wave 2: platform-php platform-db platform-web] W2 --> W3[Wave 3: ms-identity] W3 --> W4[Wave 4: ms-rbac ms-devices] W4 --> W5[Wave 5: ms-issues ms-tickets + be UI] W5 --> W6[Wave 6: ms-graphs ms-remote-access ms-build + UI] W6 --> W7[Wave 7: platform-edge mobile-android ms-ota] ``` --- ## 7. Public URL specification **Base prefix (locked):** `https://apps.f0xx.org/app/androidcast_project` ### 7.1 Rules (R1 — PO locked) 1. **No `/crashes/` in target URLs** — legacy path **301 →** correct microproject root for ≥ one release, then drop from nginx/docs/mobile. 2. **No `?view=` routing** — each product surface has its **own path root** and (eventually) its **own BE UI repo + nginx `location`**. 3. **Auth at project root** — `/login`, `/logout`, `/register`, `/two-factor`, `/verify-email` (not under `/issues/`). 4. **Shared cookie path** — `/app/androidcast_project` at **ac-platform-edge** until SSO tokens replace shared PHP session. ### 7.2 Target path catalog (one URI per microproject) | Microproject | Target URL root | Repo (UI / API) | Notes | |--------------|-----------------|-----------------|-------| | Hub / landing | `…/` | ac-be-hub | Entry cards link to path roots only | | Auth | `…/login`, `…/logout`, … | ac-be-auth → ac-ms-identity | Project-root paths | | Issues (reports) | `…/issues/` | ac-be-issues / ac-ms-issues | Was `?view=reports` | | Issue detail | `…/issues/{id}` | ac-be-issues | Was `?view=report&id=` | | Tickets | `…/tickets/` | ac-be-tickets / ac-ms-tickets | Was `?view=tickets` | | Ticket detail | `…/tickets/{id}` | ac-be-tickets | Was `?view=ticket&id=` | | Analytics / graphs | `…/graphs/` | ac-be-graphs / ac-ms-graphs | Already separate | | RBAC / access | `…/access/` | ac-be-access / ac-ms-rbac | Was `?view=rbac` | | Remote access | `…/remote-access/` | ac-be-remote-access / ac-ms-remote-access | Was `?view=remote_access` | | Short links | `…/short-links/` | ac-be-hub or thin UI / ac-ms-url-shortener | Was `?view=short_links` | | Live sessions | `…/live-sessions/` | ac-be-issues (or ac-be-live) / live_cast API | Was `?view=live_sessions` | | Live join / education | `…/live/join`, `…/live/education` | shared live pages | Under issues until split | | Builder | `…/build/` | ac-be-builder / ac-ms-build | Unchanged | | Git browse | `…/git/` | Gitea UI (mirror) | Not a microservice repo | | OTA | `/v0/ota/` | ac-ms-ota | Top-level on apps host | **API examples:** `…/issues/api/upload` (not `…/crashes/api/upload.php`). ### 7.3 Migration table (legacy → target) | Legacy (forbidden in target) | Target URL | |----------------------------|------------| | `…/crashes/?view=reports` | `…/issues/` | | `…/crashes/?view=report&id=N` | `…/issues/N` | | `…/crashes/?view=tickets` | `…/tickets/` | | `…/crashes/?view=ticket&id=N` | `…/tickets/N` | | `…/crashes/?view=home` | `…/issues/` or `…/` (hub) | | `…/crashes/?view=rbac` | `…/access/` | | `…/crashes/?view=remote_access` | `…/remote-access/` | | `…/crashes/?view=short_links` | `…/short-links/` | | `…/crashes/?view=live_sessions` | `…/live-sessions/` | | `…/crashes/?view=graphs` | `…/graphs/` | | `…/crashes/login` | `…/login` | | `…/crashes/api/upload.php` | `…/issues/api/upload` | | `…/crashes/` (any) | **301** → matching row above | **Monolith shim (Step 5 only):** nginx may **internally** rewrite `/issues/` → legacy PHP `?view=reports` until ac-ms-issues exists — **not** exposed in links, docs, or mobile defaults. **Code updates:** `BackendEndpoints.java`, hub `index.php`, all console nav, OpenAPI base paths, deploy nginx fragments. --- ## 8. Dependency graphs ### 8.1 End-to-end — clients, edge, services ```mermaid flowchart TB subgraph clients [Clients] AND[ac-mobile-android] IOS[ac-mobile-ios] HUB[ac-be-hub] SS[ac-session-studio] end subgraph edge [Edge] EDGE[ac-platform-edge] end subgraph core [Core MS — order matters] ID[ac-ms-identity] RB[ac-ms-rbac] DV[ac-ms-devices] end subgraph domain [Domain MS] IS[ac-ms-issues] TK[ac-ms-tickets] GR[ac-ms-graphs] RA[ac-ms-remote-access] UL[ac-ms-url-shortener] BL[ac-ms-build] OT[ac-ms-ota] end AND --> EDGE IOS --> EDGE HUB --> EDGE SS -.->|offline files| AND EDGE --> ID EDGE --> IS & TK & GR & RA & UL & BL & OT & RB ID --> RB RB --> DV IS --> DV & RB TK --> RB TK -.-> IS GR --> ID RA --> RB & DV RA -.-> IS UL --> RB BL --> ID OT --> BL OT --> AND & IOS ``` ### 8.2 Backend UI → microservice (requires) ```mermaid flowchart LR B7[ac-be-auth] --> S1[ac-ms-identity] B5[ac-be-access] --> S2[ac-ms-rbac] B1[ac-be-issues] --> S4[ac-ms-issues] B2[ac-be-tickets] --> S5[ac-ms-tickets] B3[ac-be-graphs] --> S6[ac-ms-graphs] B4[ac-be-remote-access] --> S7[ac-ms-remote-access] B6[ac-be-builder] --> S9[ac-ms-build] B0[ac-be-hub] --> S1 B0 --> P2[ac-platform-web] S4 --> S3[ac-ms-devices] S4 --> S2 S5 --> S2 S7 --> S2 & S3 ``` Every **B*** and **S*** also **requires** **P0** (ac-platform-php) and **P1** (ac-platform-db) via Composer — omitted from diagram for clarity. ### 8.3 Platform and deploy layer ```mermaid flowchart TB D0[ac-deploy] --> P4[ac-scripts] D0 --> P3[ac-platform-edge] D0 --> P1[ac-platform-db] P3 -->|routes| S1 & S4 & S5 & S6 & S7 & S8 & S9 & S10 P2[ac-platform-web] -->|static| B0 & B1 & B2 S1 & S2 & S3 & S4 & S5 --> P0[ac-platform-php] S6 & S7 & S8 & S9 & S10 --> P0 B0 & B1 & B2 & B3 & B4 & B5 & B6 & B7 --> P0 P1 --> DB[(MariaDB / managed DB)] S11[ac-ms-notifications] -.->|mail| S1 ``` ### 8.4 Service dependency matrix | Service | Requires (hard) | Requires (soft) | Required by | |---------|-----------------|-----------------|-------------| | ac-platform-db | — | — | All MS, migration jobs | | ac-platform-php | ac-platform-db | — | All MS, all BE UI | | ac-platform-web | — | — | ac-be-hub, all BE UI | | ac-platform-edge | ac-deploy nginx templates | all MS upstream addrs | All public HTTP | | ac-ms-identity | platform-db, platform-php | ac-ms-notifications | All MS, all BE UI, ac-ms-build | | ac-ms-rbac | ac-ms-identity | — | devices, issues, tickets, RA, url-shortener | | ac-ms-devices | ac-ms-rbac | ac-ms-identity | ac-ms-issues, ac-ms-remote-access | | ac-ms-issues | ac-ms-rbac, ac-ms-devices | — | ac-be-issues, mobile upload | | ac-ms-tickets | ac-ms-rbac | ac-ms-issues | ac-be-tickets | | ac-ms-graphs | ac-ms-identity | — | ac-be-graphs, mobile | | ac-ms-remote-access | ac-ms-rbac, ac-ms-devices | ac-ms-issues | ac-be-remote-access, mobile | | ac-ms-url-shortener | ac-ms-rbac | — | hub/admin UI | | ac-ms-build | ac-ms-identity | git server mirrors | ac-be-builder, ac-ms-ota | | ac-ms-ota | ac-ms-build | C0, C1 SHAs | mobile OTA clients | | ac-mobile-android | ac-ms-issues, ac-ms-graphs, ac-ms-remote-access APIs | ac-ms-ota | — | ### 8.5 Repo creation waves See **[§6.2](#62-repo-creation-waves-git-server)** for the ordered wave diagram. --- ## 9. Migration plan Ordered steps for converting the monolith. **Do not skip gates.** Each step lists repos created/moved, monolith paths affected, and verification (see also §12). ### Step 1 — Git org `ac` on f0xx.org server (no code move) **Goal:** Namespace **`ac`** exists on the **git server**; legacy monolith mirror preserved; Gitea browse UI synced. | Action | Detail | |--------|--------| | Create org / paths | PO: bare repos under `git://f0xx.org/ac/` on git server | | Mirror | `android_cast` → read-only; plan `ac-workspace` or retire later | | Gitea (optional) | Sync mirrors for `…/git/` browse UI only — **not** repo creation | | Update docs | Pointer in INFRA.md, AGENTS.md | **Verify:** `git ls-remote git://f0xx.org/ac/ac-docs` (after Step 2) ; legacy push still works. **Requires:** nothing **Enables:** all following steps --- ### Step 2 — Extract zero-coupling repos **Goal:** Repos with no runtime dependency on PHP monolith. | Repo | From monolith | CI | |------|---------------|-----| | ac-docs | `docs/` | PDF build | | ac-scripts | `scripts/` | lint/smoke | | ac-session-studio | `desktop/session-studio/` | unit tests | | ac-ms-url-shortener | `backend/url-shortener/` | PHPUnit (change remote URL only if already split) | | ac-deploy | `orchestration/` + nginx seeds | docker compose smoke | **ac-deploy** submodules **ac-scripts** at pinned SHA. **Verify:** Each repo builds independently; monolith submodule paths updated OR mirror dual-push during transition. **Requires:** Step 1 **Enables:** Steps 3–4 --- ### Step 3 — Platform libraries (foundation) **Goal:** Shared code leaves monolith as Composer packages — **no MS split yet**. | Repo | Extract from | |------|--------------| | ac-platform-db | `sql/`, migrations from crash_reporter + url-shortener | | ac-platform-php | Auth*, Database, Rbac helpers, bootstrap patterns, `shared_session.php` | | ac-platform-web | `crashes/assets/`, shared theme referenced by hub | Monolith **requires** packages via Composer; behavior unchanged. **Verify:** Monolith tests green; single login still works across hub/crashes/build. **Requires:** Step 2 **Enables:** Step 4 (identity) --- ### Step 4 — Microservice template and OpenAPI baseline **Goal:** Cookiecutter + contract layout for all MS. | Repo | Action | |------|--------| | ac-ms-template | Copy url-shortener + platform-php wiring | | ac-docs | Add `openapi/` stubs per service | **Verify:** Scaffold new empty MS from template; deploy locally via ac-deploy. **Requires:** Step 3 **Enables:** Step 5 --- ### Step 5 — Edge proxy (routing shell) **Goal:** **ac-platform-edge** nginx config routes paths; still proxies to **monolith** PHP until MS exist. | Action | Detail | |--------|--------| | Deploy edge config | Map `/issues/` → monolith `?view=reports` internally (temporary) | | Cookie | Single path `/app/androidcast_project` | **Verify:** `curl -I` new paths return 200 via rewrite; old URLs still work. **Requires:** Step 2 (ac-deploy) **Enables:** Step 10 URL cutover --- ### Step 6 — Identity microservice (GATE) **Goal:** **ac-ms-identity** owns users, login, register, 2FA, session issuance. | Repo | From monolith | |------|---------------| | ac-ms-identity | Auth*, UserRepository, AuthTotp*, AuthRegistration*, … | | ac-be-auth | login/register views (thin) | Monolith/builder **call identity** for auth (HTTP or shared session bridge during transition). **Verify:** Login at `/app/androidcast_project/` + builder; sessions valid; `./scripts/test_rbac_api.sh` still passes against bridged auth. **Requires:** Steps 3, 4 **Enables:** Steps 7, 9, 11 — **nothing else splits before this** --- ### Step 7 — RBAC and devices microservices **Goal:** Company scope and device registry are independent APIs. | Repo | From monolith | |------|---------------| | ac-ms-rbac | Rbac*, RbacAdmin*, companies API | | ac-ms-devices | DeviceRepository | **Verify:** Upload still registers device; RBAC panel works via APIs. **Requires:** Step 6 **Enables:** Steps 8, 9 --- ### Step 8 — Issues and tickets (domain core) **Goal:** Split PO’s primary URL surfaces. | Repo | From monolith | |------|---------------| | ac-ms-issues | upload, Report*, Tag*, reports API | | ac-ms-tickets | Ticket*, attachments | | ac-be-issues | reports views + JS | | ac-be-tickets | tickets views + JS | **Edge** routes `/issues/` → S4/B1, `/tickets/` → S5/B2. **Verify:** Mobile upload to new URL; web issue/ticket lists; E2E ticket workflow. **Requires:** Step 7 **Enables:** Step 9 (RA links to issues) --- ### Step 9 — Graphs and remote access | Repo | From monolith | |------|---------------| | ac-ms-graphs | GraphRepository, graph_* API | | ac-ms-remote-access | RemoteAccess*, WireGuard*, Rssh* | | ac-be-graphs | graphs UI | | ac-be-remote-access | remote_access view | **Verify:** `BackendEndpoints` graph URL; RA E2E (`ra_e2e_cli.sh`); mobile RA API. **Requires:** Steps 7, 8 (RA → devices, issue links) **Enables:** Step 11 --- ### Step 10 — Hub, access UI, URL migration | Repo | Action | |------|--------| | ac-be-hub | Extract app_hub; use **ac-platform-web** assets | | ac-be-access | RBAC UI → calls ac-ms-rbac | | ac-platform-edge | Enable **301** from `/crashes/` and `?view=*` to §7 path roots | **Verify:** Hub cards link to `/issues/`, `/tickets/`; no broken CSS. **Requires:** Steps 5, 8, 9 **Enables:** Step 11 --- ### Step 11 — Build and OTA pipeline | Repo | From monolith | |------|---------------| | ac-ms-build | BuildRunner, build APIs | | ac-be-builder | build_console UI | | ac-ms-ota | OTA manifest assembly | | ac-mobile-android | `app/`, `ndk/`, gradle (extract from monolith) | **Verify:** Builder triggers APK; OTA channel publishes; manifest lists android (+ ios when C1 exists). **Requires:** Step 6 (identity for builder users) **Enables:** Step 12 --- ### Step 12 — Optional workspace and monolith retirement | Action | Detail | |--------|--------| | ac-workspace | Publish optional `.gitmodules` manifest | | Retire | Stop commits to `android_cast` monolith; archive mirror | | Gitea mirror sync | Update mirror scripts for org `ac` (server → Gitea UI; [gitea README](../../examples/crash_reporter/backend/scripts/gitea/README.md)) | **Verify:** Fresh clone via ac-workspace or individual repos; prod deploy from ac-deploy only. **Requires:** Steps 1–11 complete for services in scope **Enables:** cloud VM scaling, future F1/F2 MS --- ### Step 13 — Notifications and cloud hardening (post-alpha) | Repo | Action | |------|--------| | ac-ms-notifications | Extract AuthMailer; wire 2.x mail when unblocked | | ac-deploy | Managed DB DSN templates; cluster0 → cloud VM roles | **Requires:** Step 6; mail infra (developer) **Enables:** full 2FA mail E2E --- ### Conversion step summary (quick reference) | Step | Name | Gate | |------|------|------| | 1 | Git org `ac` on server | — | | 2 | docs, scripts, deploy, session-studio, url-shortener | — | | 3 | platform-php, platform-db, platform-web | — | | 4 | ms-template, OpenAPI | — | | 5 | platform-edge (shell) | — | | 6 | **ms-identity**, be-auth | **GATE** | | 7 | ms-rbac, ms-devices | after 6 | | 8 | ms-issues, ms-tickets, be-issues, be-tickets | after 7 | | 9 | ms-graphs, ms-remote-access, be UI | after 7–8 | | 10 | be-hub, be-access, URL 301 | after 8–9 | | 11 | ms-build, ms-ota, mobile-android | after 6 | | 12 | ac-workspace, retire monolith | after 1–11 | | 13 | notifications, cloud | post-alpha | --- ## 10. Infrastructure and cloud | Stage | Pattern | |-------|---------| | **Prod today** | FE TLS → BE :80 nginx + PHP-FPM + MariaDB | | **cluster0 lab** | cast01–03; NFS config only; GTID DB ([ARCHITECTURE.md](../../orchestration/sim/cluster0/ARCHITECTURE.md)) | | **Cloud phase A** | 4–6 VMs: be-web, be-data, be-ops, be-build ([scaling §6.3](../specs/20100612_1_scaling.md)) | | **Cloud phase B** | Managed DB; DSN in secrets (not in git) | | **Cloud phase C** | K8s optional — not required at R0 | **Inter-service calls:** HTTP on private VLAN / localhost initially; OpenAPI in ac-docs. No service mesh at R0. **ac-deploy** ships Ansible/docker-compose per VM role; submodules **ac-scripts**. --- ## 11. ac-workspace and OTA versioning **ac-workspace** — optional; lists repo SHAs for a release train. **Not** a unified Gradle/PHP build. **ac-ms-ota** manifest (same product version, per-platform SHAs): ```json { "channel": "staging", "version": "00.02.00.00", "artifacts": { "android": { "repo": "ac-mobile-android", "git_sha": "abc123" }, "ios": { "repo": "ac-mobile-ios", "git_sha": "def456" } } } ``` --- ## 12. Verification and acceptance Per-step gates from §9 — all MUST pass before the next step begins. | Step | Acceptance criteria | |------|---------------------| | 1 | `git ls-remote git://f0xx.org/ac/ac-docs` succeeds (after Step 2 repo exists); legacy `android_cast` push still works | | 2 | Each extracted repo builds/CI green independently | | 3 | Monolith tests green; single login across hub/issues/build | | 4 | Empty MS scaffold deploys locally via ac-deploy | | 5 | `curl -I` on `/issues/`, `/tickets/`, … return 200 via edge rewrite | | 6 | Login + `./scripts/test_rbac_api.sh` pass (identity gate) | | 7 | Device upload + RBAC API via new services | | 8 | Mobile upload to `…/issues/api/upload`; ticket E2E | | 9 | Graph + RA E2E (`ra_e2e_cli.sh`) | | 10 | Hub links use path roots only; 301 from `/crashes/` | | 11 | Builder APK + OTA channel publish | | 12 | Fresh clone via ac-workspace or per-repo remotes | | 13 | Mail E2E when infra unblocked | **URL regression (ongoing):** hub, mobile `BackendEndpoints`, nginx fragments, and docs MUST NOT introduce new `/crashes/` or `?view=` links after Step 5. **Repo index:** Appendix A disk paths MUST match live git server layout before Step 2 mass extract. --- ## 13. Risks and mitigations | Risk | Mitigation | |------|------------| | Auth split breaks all consoles | Step 6 gate; session bridge during transition | | Hub broken CSS | ac-platform-web before ac-be-hub (Step 3 before 10) | | Mobile upload URL change | Parallel endpoints + `BackendEndpoints` normalization | | DB schema drift | ac-platform-db single migration stream | | Too many repos to bump | Composer semver for P0; not @common submodule diamond | | Alpha blocked | Monolith stays prod until Step 8+ complete per service | | PO BE learning curve | ac-ms-template, ac-deploy, OpenAPI, localhost HTTP | --- ## Appendix A — Git disk paths and remotes **Convention:** disk paths are **relative to the git server root** (example: `/var/git/`). PO may choose another root; keep the `ac/` layout. **Canonical clone/fetch (write + read):** `git://f0xx.org/ac/` **HTTPS read-only (optional):** see [Appendix B](#appendix-b-optional-https-read-only-git-access) — separate vhost; **not** `https://f0xx.org/...` app URLs. | # | Disk path (relative) | Git repo URL | |---|----------------------|--------------| | 1 | `ac/ac-workspace` | `git://f0xx.org/ac/ac-workspace` | | 2 | `ac/ac-platform-php` | `git://f0xx.org/ac/ac-platform-php` | | 3 | `ac/ac-platform-db` | `git://f0xx.org/ac/ac-platform-db` | | 4 | `ac/ac-platform-web` | `git://f0xx.org/ac/ac-platform-web` | | 5 | `ac/ac-platform-edge` | `git://f0xx.org/ac/ac-platform-edge` | | 6 | `ac/ac-scripts` | `git://f0xx.org/ac/ac-scripts` | | 7 | `ac/ac-docs` | `git://f0xx.org/ac/ac-docs` | | 8 | `ac/ac-deploy` | `git://f0xx.org/ac/ac-deploy` | | 9 | `ac/ac-mobile-android` | `git://f0xx.org/ac/ac-mobile-android` | | 10 | `ac/ac-mobile-ios` | `git://f0xx.org/ac/ac-mobile-ios` | | 11 | `ac/ac-session-studio` | `git://f0xx.org/ac/ac-session-studio` | | 12 | `ac/ac-ms-template` | `git://f0xx.org/ac/ac-ms-template` | | 13 | `ac/ac-ms-identity` | `git://f0xx.org/ac/ac-ms-identity` | | 14 | `ac/ac-ms-rbac` | `git://f0xx.org/ac/ac-ms-rbac` | | 15 | `ac/ac-ms-devices` | `git://f0xx.org/ac/ac-ms-devices` | | 16 | `ac/ac-ms-issues` | `git://f0xx.org/ac/ac-ms-issues` | | 17 | `ac/ac-ms-tickets` | `git://f0xx.org/ac/ac-ms-tickets` | | 18 | `ac/ac-ms-graphs` | `git://f0xx.org/ac/ac-ms-graphs` | | 19 | `ac/ac-ms-remote-access` | `git://f0xx.org/ac/ac-ms-remote-access` | | 20 | `ac/ac-ms-url-shortener` | `git://f0xx.org/ac/ac-ms-url-shortener` | | 21 | `ac/ac-ms-build` | `git://f0xx.org/ac/ac-ms-build` | | 22 | `ac/ac-ms-ota` | `git://f0xx.org/ac/ac-ms-ota` | | 23 | `ac/ac-ms-notifications` | `git://f0xx.org/ac/ac-ms-notifications` | | 24 | `ac/ac-be-hub` | `git://f0xx.org/ac/ac-be-hub` | | 25 | `ac/ac-be-issues` | `git://f0xx.org/ac/ac-be-issues` | | 26 | `ac/ac-be-tickets` | `git://f0xx.org/ac/ac-be-tickets` | | 27 | `ac/ac-be-graphs` | `git://f0xx.org/ac/ac-be-graphs` | | 28 | `ac/ac-be-remote-access` | `git://f0xx.org/ac/ac-be-remote-access` | | 29 | `ac/ac-be-access` | `git://f0xx.org/ac/ac-be-access` | | 30 | `ac/ac-be-builder` | `git://f0xx.org/ac/ac-be-builder` | | 31 | `ac/ac-be-auth` | `git://f0xx.org/ac/ac-be-auth` | | 32 | `ac/ac-ms-sfu-signaling` | `git://f0xx.org/ac/ac-ms-sfu-signaling` | | 33 | `ac/ac-ms-media-transcode` | `git://f0xx.org/ac/ac-ms-media-transcode` | | 34 | `android_cast` | `git://f0xx.org/android_cast` | **Bare repo on server (PO):** `git init --bare /var/git/ac/ac-workspace` (repeat per row; path = `{GIT_ROOT}/` + disk path). --- ## Appendix B — Optional HTTPS read-only git access **Scope:** Standalone **git infrastructure** on the PO git host. **Not** part of the AndroidCast monorepo, BE PHP tree, Gitea product UI, or `apps.f0xx.org` consoles. **Goal:** Keep **`git://f0xx.org/...`** for normal push/fetch; add **`https://`** for **read-only** clones (anonymous and/or token), with TLS certs and ACL files **outside** the AndroidCast project. **Suggested hostname:** `git-r.f0xx.org` (read-only smart HTTP). Do **not** reuse `https://f0xx.org/` landing or `https://apps.f0xx.org/` app paths for git smart HTTP. **Suggested paths on git server:** | Path | Purpose | |------|---------| | `/var/git/` | Bare repos (Appendix A disk layout) | | `/etc/git-ro/` | nginx snippets, token maps, install scripts — **not** in android_cast repo | | `/etc/git-ro/tokens/` | Per-token permission files (see step 8) | | `/etc/letsencrypt/live/git-r.f0xx.org/` | Certbot certificates | --- ### 1) DNS Add an **A** (or **AAAA**) record for the read-only git vhost pointing at the **git server** public or edge IP (same host that serves `git://` today, or FE DNAT to it): ```text git-r.f0xx.org. IN A ``` Verify: ```bash dig +short git-r.f0xx.org ``` --- ### 2) Install packages (git server — example: Gentoo/Alpine) On the machine that holds `/var/git/`: ```bash # Gentoo (example) emerge -av nginx git fcgiwrap certbot # Alpine (example) apk add nginx git git-daemon fcgiwrap certbot certbot-nginx openssl ``` Enable `fcgiwrap` (smart HTTP via `git http-backend`): ```bash # systemd example systemctl enable --now fcgiwrap ``` --- ### 3) Issue free TLS certificates (Certbot) Run on the git server (or on FE if it terminates TLS and proxies to git — adjust plugin): ```bash certbot certonly --standalone -d git-r.f0xx.org \ --agree-tos -m admin@f0xx.org --non-interactive ``` If nginx is already bound to :80/:443, use the nginx plugin instead: ```bash certbot certonly --nginx -d git-r.f0xx.org \ --agree-tos -m admin@f0xx.org --non-interactive ``` Certificates land under `/etc/letsencrypt/live/git-r.f0xx.org/fullchain.pem` and `privkey.pem`. --- ### 4) nginx — HTTPS smart HTTP, read-only Create `/etc/git-ro/nginx-git-ro.conf` (outside AndroidCast): ```nginx server { listen 443 ssl; server_name git-r.f0xx.org; ssl_certificate /etc/letsencrypt/live/git-r.f0xx.org/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/git-r.f0xx.org/privkey.pem; root /var/git; location ~ ^/ac/(.+?)(\.git)?/(HEAD|info/refs|objects/info/.*|objects/([0-9a-f]{2}/[0-9a-f]{38}|pack/pack-.*))$ { include fastcgi_params; fastcgi_param SCRIPT_FILENAME /usr/libexec/git-core/git-http-backend; fastcgi_param GIT_HTTP_EXPORT_ALL ""; fastcgi_param GIT_PROJECT_ROOT /var/git; fastcgi_param PATH_INFO /$1.git/$2; fastcgi_pass unix:/run/fcgiwrap.socket; } location ~ ^/ac/(.+?)\.git$ { return 301 /ac/$1.git/info/refs?service=git-upload-pack; } } ``` Include it from the main nginx config: ```bash ln -s /etc/git-ro/nginx-git-ro.conf /etc/nginx/conf.d/git-ro.conf nginx -t && rc-service nginx reload # Gentoo # nginx -t && systemctl reload nginx # other ``` **Read-only guarantee:** do **not** enable `receive-pack`; only `git-upload-pack` (clone/fetch) is exposed. Push stays on **`git://f0xx.org`** (SSH or authenticated path), not this vhost. Test anonymous clone: ```bash git clone https://git-r.f0xx.org/ac/ac-docs.git /tmp/ac-docs-ro-test ``` --- ### 5) Mark public repos for anonymous HTTPS read For each bare repo that may be cloned without a token: ```bash GIT_ROOT=/var/git repo=ac/ac-docs touch "${GIT_ROOT}/${repo}.git/git-daemon-export-ok" git -C "${GIT_ROOT}/${repo}.git" config http.receivepack false git -C "${GIT_ROOT}/${repo}.git" config http.uploadpack true ``` Repeat for every Appendix A row you want world-readable over HTTPS. --- ### 6) Optional — require token for private HTTPS read Add HTTP basic auth for selected locations. Store credentials **outside** AndroidCast: ```bash install -d -m 0750 /etc/git-ro/htpasswd htpasswd -c /etc/git-ro/htpasswd/ro-ci-user ``` In `/etc/git-ro/nginx-git-ro.conf`, inside the `location` block for smart HTTP: ```nginx auth_basic "git read-only"; auth_basic_user_file /etc/git-ro/htpasswd/ro-users; ``` Clone with token (password = token): ```bash git clone https://ro-ci-user:@git-r.f0xx.org/ac/ac-ms-identity.git ``` --- ### 7) Token-wise permissions (outside AndroidCast) Keep a simple ACL file per token under `/etc/git-ro/tokens/` — **not** referenced by android_cast code: ```bash install -d -m 0700 /etc/git-ro/tokens cat > /etc/git-ro/tokens/ci-read-issues.env <<'EOF' # token name: ci-read-issues # htpasswd user: ro-ci-issues # allowed repo prefixes (one per line, relative to /var/git) ac/ac-ms-issues ac/ac-be-issues ac/ac-platform-php EOF chmod 0600 /etc/git-ro/tokens/*.env ``` Enforcement options (pick one on the git server): - **Simple:** separate htpasswd users per token + nginx `location` blocks per repo prefix. - **Scalable:** small `git-ro-auth` script (not in AndroidCast repo) consulted by nginx `auth_request`. AndroidCast **never** reads `/etc/git-ro/`; CI machines hold tokens in their own secret stores. --- ### 8) Certbot renewal (crontab) Edit root crontab on the git server: ```bash crontab -e ``` Add: ```cron 17 3 * * * certbot renew --quiet --deploy-hook "nginx -t && rc-service nginx reload" ``` Or install a one-shot renew script `/etc/git-ro/certbot-renew.sh`: ```bash #!/bin/sh set -eu certbot renew --quiet nginx -t rc-service nginx reload ``` ```bash chmod 0755 /etc/git-ro/certbot-renew.sh crontab -e ``` ```cron 17 3 * * * /etc/git-ro/certbot-renew.sh >> /var/log/git-ro-certbot.log 2>&1 ``` --- ### 9) Client remote examples | Use case | Remote URL | |----------|------------| | Developer push/fetch (canonical) | `git://f0xx.org/ac/ac-mobile-android` | | Anonymous read (HTTPS) | `https://git-r.f0xx.org/ac/ac-docs.git` | | Token read (HTTPS) | `https://:@git-r.f0xx.org/ac/ac-ms-issues.git` | | Browse HTML (optional) | `https://apps.f0xx.org/app/androidcast_project/git/` (Gitea mirror only) | Do **not** document `https://f0xx.org/...` as a git smart HTTP endpoint; landing stays human/PDF only. --- ### 10) Verification checklist ```bash # TLS curl -sSI https://git-r.f0xx.org/ac/ac-docs.git/info/refs?service=git-upload-pack | head # git:// still works (unchanged) git ls-remote git://f0xx.org/ac/ac-docs # HTTPS read-only (no push) git clone https://git-r.f0xx.org/ac/ac-docs.git /tmp/ro-clone-test cd /tmp/ro-clone-test && git push origin HEAD # must fail on this vhost ``` --- ## 14. Related documents | Document | Role | |----------|------| | [DRs/20260618_repos_reorganizing.md](../DRs/20260618_repos_reorganizing.md) | Frozen design review R1.1 (source of this SPEC) | | [INFRA.md](../INFRA.md) | FE/BE topology, ports | | [BUILD_DEPLOY.md](../BUILD_DEPLOY.md) | Session cookie, deploy paths | | [specs/20100612_1_scaling.md](20100612_1_scaling.md) | VM roles, cluster layout | | [orchestration/sim/cluster0/ARCHITECTURE.md](../../orchestration/sim/cluster0/ARCHITECTURE.md) | Lab cluster | | [OPEN_TASKS_GRAPH.md](../OPEN_TASKS_GRAPH.md) | Task dependencies | --- ## 15. Changelog | Rev | Date | Change | |-----|------|--------| | R1 | 2026-06-22 | SPEC converted from frozen DR R1.1; normative requirements §3; migration §9; verification §12 | | R1.1 | 2026-06-22 | Submodule policy §4.3: only `third-party/*` + `backend/url-shortener` |