# Gitea npm Package Registry Migration > **Goal:** Replace the `docker-prep.sh` + tarball workflow with Gitea's built-in npm package registry for `@bytelyst/*` shared packages. > > **Phase policy:** use **this Mac as the future single VM** and validate the entire flow locally first. No Azure rollout and no publishing to any external or corporate registry in this phase. --- ## 1. Decision We should adopt the local Gitea npm registry as the long-term package-distribution path for `@bytelyst/*` packages. We should **not** switch directly in Azure first. Instead, we should validate the exact end-to-end flow locally on this Mac: - local Gitea git hosting - local Gitea Actions - local Gitea npm package registry - local Docker / Docker Compose builds - local product repos consuming `@bytelyst/*` from local Gitea Once that is stable, we replicate the same validated shape on the Azure single VM. --- ## 2. Why We Want This ### Current pain Today many repos still depend on one of these Docker-time workarounds: - `file:` references to sibling `learning_ai_common_plat/packages/*` - `docker-prep.sh` - `.docker-deps` or older `.tarballs` copy steps - temporary `package.json` rewriting before builds This works, but it creates repeated operational drag: - every build may need prep - Dockerfiles become repo-specific and fragile - CI, local development, and Docker are not using one clean dependency source - path and build-context mismatches keep surfacing during ecosystem validation ### Target outcome We want one dependency source for shared packages: - local development - local CI via Gitea Actions - local Docker builds - later Azure single-VM builds That dependency source should be the **local Gitea npm registry**. --- ## 3. Local-First Target Architecture ### 3.1 This Mac as the rehearsal VM ```text This Mac ├── local Gitea (git + actions + npm registry) ├── local Docker / Docker Compose ├── learning_ai_common_plat └── product repos ``` Flow: ```text learning_ai_common_plat/packages/* ↓ build locally ↓ publish to local Gitea npm registry local Gitea npm registry (localhost:3300) ↓ install via semver refs product repos / Docker builds / local Gitea CI ``` ### 3.2 Future Azure single VM After the local rehearsal is proven, Azure should mirror the same shape: ```text Azure VM ├── Gitea ├── Gitea Actions runner ├── Gitea npm registry ├── Docker / Compose └── same repo + build flow ``` The Azure plan should reuse the validated local approach, not invent a second design. --- ## 4. What Is In Scope Right Now ### Included - validate local Gitea package-registry access - validate local Gitea Actions availability - define local-only package publish flow to local Gitea - define local-only consumer install flow from local Gitea - define local-only Docker build flow from local Gitea - define the later Azure single-VM replication steps ### Excluded - Azure changes right now - external npm registry publishing - corporate registry publishing - non-local deployment actions - K3s / Helm work --- ## 5. Current Local Readiness Snapshot Validated locally on this Mac: - local Gitea health endpoint responds - local Gitea version endpoint responds - local `bytelyst` user auth works - local Gitea Actions workflow exists for `learning_ai_common_plat` - local Gitea package registry endpoint is reachable - local pilot packages have been published to local Gitea successfully - a clean scratch consumer install from local Gitea works on the host That is enough to treat this Mac as the single-VM rehearsal environment. --- ## 5.1 Validation Results So Far ### Proven locally on this Mac - shared packages build cleanly in `learning_ai_common_plat` - a local-only Gitea package token was created and used successfully for local package publishing - pilot packages were published to local Gitea npm registry successfully - a clean scratch `pnpm install` from the local Gitea registry worked on the host when `GITEA_NPM_TOKEN` was set to a non-empty placeholder value for `.npmrc` env resolution - `learning_ai_flowmonk` was migrated on the host from tarball refs to semver `@bytelyst/*` refs - `learning_ai_flowmonk` host-side install, typecheck, and tests passed against the local Gitea registry ### Important implementation finding Publishing `@bytelyst/*` workspace packages with raw `npm publish` is not sufficient for this migration. Why: - packages with internal `workspace:*` dependencies can be published with unresolved workspace specifiers - those published artifacts break downstream consumers What worked: - `pnpm pack` produces normalized package metadata for workspace dependencies - publishing the normalized tarball to Gitea works This is now codified in the local-only helper script: `scripts/publish-local-gitea-packages.sh` That script is currently the authoritative local rehearsal path for publishing `@bytelyst/*` packages to local Gitea. ### Current blocker The remaining blocker is not host-side package publishing or host-side package consumption. ### Network topology determines the Docker recipe | Topology | `NETWORK=` | Gitea host | Docker reaches Gitea via | `--add-host` needed? | | ------------------- | ---------- | ------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------- | | **Local dev (Mac)** | `corp` | `localhost:3300` | `host.docker.internal` + `--add-host localhost:host-gateway` | **Yes** — expected | | **VM deployment** | (TBD) | `:3300` or compose service `gitea:3300` | Native Docker networking | **No** — `ROOT_URL` resolves natively | The `--add-host localhost:host-gateway` workaround is the **correct local-dev pattern** when `NETWORK=corp`, not a generic blocker. On a VM, Gitea's `ROOT_URL` will be set to the real hostname or compose service name, so tarball URLs resolve without any host-mapping workaround. At the time of writing: - host-side registry usage is validated - FlowMonk host-side pilot migration is validated - FlowMonk backend/web Docker builds are validated locally with the recipe below - local Gitea CI is green for both FlowMonk and common-platform on current commits Verified local FlowMonk Docker recipe (`NETWORK=corp`): ```bash export TOKEN='' docker build \ --add-host localhost:host-gateway \ --build-arg GITEA_NPM_HOST=host.docker.internal \ --secret id=gitea_npm_token,env=TOKEN \ -f backend/Dockerfile /Users/sd9235/code/mygh/learning_ai_flowmonk docker build \ --add-host localhost:host-gateway \ --build-arg GITEA_NPM_HOST=host.docker.internal \ --secret id=gitea_npm_token,env=TOKEN \ -f web/Dockerfile /Users/sd9235/code/mygh/learning_ai_flowmonk ``` On the target VM, the recipe simplifies to just `--build-arg GITEA_NPM_HOST=` plus the BuildKit secret — no `--add-host` needed. --- ## 5.2 Remaining Gaps From Local Mac Validation The local rehearsal on this Mac has validated the **host-side** registry model, **Docker builds** (backend + web), and **local Gitea CI**. The `--add-host localhost:host-gateway` pattern is the correct local-dev recipe when `NETWORK=corp`; it is not needed on the target VM. ### Validated ✅ - Host-side `pnpm install` against local Gitea registry - FlowMonk backend/web Docker builds with BuildKit secret + `--add-host` (local-dev topology) - Local Gitea CI green for FlowMonk and common-platform on current commits - Runner re-registered against `127.0.0.1` (avoids IPv6 `[::1]` declaration failure) ### Gaps still open - Docker-side package installs need a real Gitea package token (host-side only needs non-empty `GITEA_NPM_TOKEN` for `.npmrc` resolution) - Local Gitea Actions has not yet been validated end-to-end for the package build → publish → consumer flow - Mobile remains outside the registry-backed pilot slice - Not all `@bytelyst/*` packages have been revalidated under the local Gitea registry with a fresh consumer install after the `pnpm pack` publish flow changes ### Why this matters for Azure Azure should be a replication step, not a redesign step. On the VM, Gitea's `ROOT_URL` will point to the real hostname, so Docker containers will resolve tarball URLs natively — the `--add-host` workaround is local-dev only. The Azure VM rollout should wait until the remaining local gaps are cleared for: - package metadata correctness - Docker consumer correctness across all product repos (not just FlowMonk pilot) - local CI correctness for the full build → publish → consume loop If we skip those validations locally, the first Azure VM trial becomes a debugging environment instead of a deployment environment. ### Concrete local-exit criteria before Azure Before starting Azure VM rollout, we should be able to demonstrate all of the following on this Mac: - one Gitea deployment shape whose package URLs work for both host installs and Docker builds - one publish path for `@bytelyst/*` packages that works repeatably with `pnpm pack` - one pilot repo that installs from the registry on the host and inside Docker without fallback tarballs - one local Gitea Actions path that can build/publish/install with the same registry assumptions - one documented rollback path that cleanly returns a pilot repo to tarball-based Docker consumption if needed --- ## 6. Migration Strategy ### Stage A — Local registry rehearsal Validate the package-registry path locally before changing every repo. Required outcomes: 1. local package token exists 2. a small pilot set of `@bytelyst/*` packages can be published to local Gitea 3. a local consumer can install them via semver refs 4. a Docker build can install them without `docker-prep.sh` 5. local Gitea Actions can run the same package build/publish path ### Stage B — Pilot repo migration Migrate one repo first. Recommended pilot: - `learning_ai_flowmonk` Reason: - recent Docker/build work already exposed its edge cases - both backend and web surfaces exist - it is a strong canary for dependency-distribution changes ### Stage C — Sequential ecosystem rollout After the pilot is stable, migrate remaining repos sequentially. --- ## 7. Local Implementation Plan ### 7.1 Verify package metadata in `learning_ai_common_plat` Every `packages/*/package.json` should have: - a valid `name` - a valid `version` - a valid `files` / `exports` setup - a successful local `build` Local validation command: ```bash cd /Users/sd9235/code/mygh/learning_ai_common_plat pnpm -r --filter './packages/*' build ``` ### 7.2 Create a local-only Gitea package token Use a token intended only for this Mac rehearsal. Required scopes: - package read - package write Do not use Azure or external registry credentials here. ### 7.3 Publish only to local Gitea In this phase, all `npm publish` activity must point only to: `http://localhost:3300/api/packages/bytelyst/npm/` No Azure Artifacts. No npmjs.org. No external corporate npm registry. Use the local-only helper script for this rehearsal: ```bash cd /Users/sd9235/code/mygh/learning_ai_common_plat GITEA_NPM_TOKEN='' bash ./scripts/publish-local-gitea-packages.sh ``` For a single package: ```bash cd /Users/sd9235/code/mygh/learning_ai_common_plat GITEA_NPM_TOKEN='' bash ./scripts/publish-local-gitea-packages.sh '@bytelyst/errors' ``` ### 7.4 Start with a minimal package pilot set Publish a small set first: - `@bytelyst/errors` - `@bytelyst/config` - `@bytelyst/api-client` Then expand once the local install path is proven. ### 7.5 Consumer dependency model after migration Consumers move from local file refs: ```json "@bytelyst/auth": "file:../../learning_ai_common_plat/packages/auth" ``` to semver refs: ```json "@bytelyst/auth": "^0.1.0" ``` And resolve via scoped `.npmrc` or `.pnpmrc` config pointing `@bytelyst` to local Gitea. ### 7.6 Docker model after migration After a repo is migrated to the registry model, its Dockerfiles should: - stop copying `.docker-deps` or `.tarballs` - stop depending on `docker-prep.sh` - install from local Gitea through scoped registry config That is the main operational simplification we want. --- ## 8. Local Validation Sequence ### Step 1 — Validate local Gitea surfaces Confirm locally: - Gitea UI is reachable - package registry API is reachable with auth - Actions workflow is visible and active ### Step 2 — Validate package build output Run local builds for the shared packages and confirm pack/publish inputs are clean. ### Step 3 — Publish a minimal package pilot set to local Gitea only Success means: - packages appear in local Gitea package list - metadata looks correct - no external registry interaction occurred ### Step 4 — Validate local install from Gitea Use a scratch consumer or a pilot repo surface and verify: - `pnpm install` resolves `@bytelyst/*` from local Gitea - lockfile updates are clean - no fallback to tarballs is required for the tested packages ### Step 5 — Validate Docker build from local Gitea For the pilot repo: - remove the tested surface's dependency on `docker-prep.sh` - add scoped registry config for Docker - build backend and web without tarball prep Success means: - image build works without `.docker-deps` or `.tarballs` - build does not require local package rewrite logic Current status on this Mac: - **not yet fully green** - the remaining issue is the local Homebrew Gitea instance serving package metadata/tarball URLs across the Docker build boundary - host-side validation is complete, Docker-side validation is still open ### Step 6 — Validate local Gitea Actions path Add or adapt a local-only workflow in `learning_ai_common_plat` that: - builds packages - optionally publishes to local Gitea npm registry - never targets an external registry ### Step 7 — Validate full local E2E At the end of the local rehearsal we should be able to say: - shared packages build locally - shared packages publish to local Gitea registry - a pilot consumer installs from local Gitea - a pilot Docker build succeeds from local Gitea - local Gitea Actions can drive the same path Only then should we expand repo-by-repo. --- ## 9. Recommended Rollout Order ### Pilot 1. `learning_ai_flowmonk` ### Then 2. `learning_ai_local_memory_gpt` 3. `learning_ai_notes` 4. `learning_ai_trails` 5. `learning_ai_fastgap` 6. `learning_ai_clock` 7. `learning_ai_jarvis_jr` 8. `learning_ai_peakpulse` 9. `learning_multimodal_memory_agents` 10. `learning_voice_ai_agent` The ordering prioritizes recently exercised Docker paths before higher blast-radius repos. --- ## 10. CI Model For The Local Rehearsal For the local rehearsal phase, Gitea Actions should do only local work: - build packages - test packages - typecheck packages - optionally publish to **local Gitea only** This is intentionally different from a final enterprise rollout. The objective is to prove the local VM pattern first. --- ## 11. Risks And Guardrails ### Risks - version drift between local source and published package versions - peer dependency mismatches hidden by current tarball workflow - Docker auth/config differences from shell installs - accidental publishing to the wrong registry - switching too many repos before the pilot is stable ### Guardrails - use local-only credentials for this phase - keep `@bytelyst` scoped registry config explicit - do not remove tarball fallback globally until the pilot is green - migrate one consumer repo at a time - keep rollback steps documented per repo --- ## 12. Rollback Plan If the registry-based flow fails during pilot migration: 1. revert the tested consumer back to its current working dependency mode 2. restore its `docker-prep.sh` path if needed 3. keep the Gitea registry work isolated to the local rehearsal branch/state 4. fix the issue locally before retrying No repo should lose its known-good build path until the registry model is proven. --- ## 13. Azure Single-VM Follow-Through After the local rehearsal is green, Azure should follow the same validated recipe: 1. provision one VM 2. install Docker and Gitea 3. enable Gitea Actions runner 4. enable Gitea packages 5. clone the same repos onto the VM 6. apply the same local registry/token/package flow 7. re-run the pilot repo first 8. then expand sequentially across the ecosystem Azure should be a replication step, not a redesign step. ### What Azure still needs beyond current local proof Even though the codebase is already designed to be highly configurable, Azure VM rollout still requires a few validations that have not been fully cleared by the local Mac rehearsal yet: - Gitea running in a deployment shape where package tarball URLs are stable for both host consumers and containerized consumers - registry-backed Docker builds for the pilot repo without `docker-prep.sh` - local Gitea Actions or equivalent host-runner proof for package build/publish/install - a final decision on whether mobile-facing `@bytelyst/*` packages stay on local `file:` links during the pilot phase or join the registry migration in a later wave - one Azure-ready secrets model for Gitea token handling, service envs, and registry auth that maps cleanly from local env vars to VM secrets/config files ### What should require minimal change when moving to Azure The good news is that most of the ecosystem has already been implemented in a way that keeps scale-up mostly configuration-driven once the registry and image flows are proven. Expected low-change areas: - service code already externalizes most environment-specific values via env/config - Compose and K8s models already map cleanly to Deployments, Services, Ingress, ConfigMaps, and Secrets - Traefik, readiness probes, service ports, and namespace separation are already documented in a K8s-friendly way - scaling stateless services should mostly mean changing replica counts, resource requests/limits, and HPA settings - moving from single-node K3s to multi-node K3s or managed Kubernetes should mostly reuse the same manifests with infra-level adjustments What is **not** yet proven enough to call low-change: - the package-distribution layer for Docker/K8s image builds - the exact image build/publish flow for the full ecosystem after registry migration - the complete repo-by-repo removal of tarball-based Docker prep --- ## 14. Definition Of Done This migration plan is locally validated only when all are true: - [x] local Gitea package publish auth verified - [x] local package publish path verified for a pilot package set - [x] local consumer install path verified on the host with non-empty `GITEA_NPM_TOKEN` env resolution - [x] local FlowMonk backend/web Docker build path verified without `docker-prep.sh` using the documented local workaround - [ ] local Gitea Actions path verified - [ ] pilot repo migrated successfully end-to-end including Docker - [ ] rollback path documented and tested conceptually - [ ] Azure single-VM reproduction steps documented from the validated local process --- ## 15. Immediate Next Actions 1. create or verify a local-only Gitea package token 2. publish a minimal pilot `@bytelyst/*` package set to local Gitea only 3. validate the same FlowMonk backend/web Docker workaround path via local Gitea Actions 4. decide whether the current `localhost` tarball workaround is acceptable for the single-VM pattern or if Gitea URL shape must be corrected first 5. document and validate the rollback path for the FlowMonk pilot 6. only then expand to broader ecosystem migration