diff --git a/AGENTS.md b/AGENTS.md index 776cb16a..af0c445f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -417,7 +417,81 @@ AZURE_BLOB_ACCOUNT_KEY= DEFAULT_PRODUCT_ID=lysnrai ``` -## 9. Dependency Graph +## 9. Corporate Network / Proxy Setup + +Development happens on a corporate network with a TLS-intercepting proxy. The `NETWORK` env var controls all proxy behavior — **no manual toggling needed**. + +### Quick Reference + +| Tool | Corporate proxy mechanism | Config location | +| -------------------------- | ------------------------------------------------------------------ | --------------------------------------------- | +| **npm / pnpm** | `NPM_CONFIG_REGISTRY` → JFrog proxy, `NPM_CONFIG_STRICT_SSL=false` | `switch-network.sh` | +| **Node.js** | `NODE_TLS_REJECT_UNAUTHORIZED=0` | `switch-network.sh` | +| **Python (pip)** | `PIP_TRUSTED_HOST`, `http_proxy`/`https_proxy` | `switch-network.sh` | +| **Gradle / Android / KMP** | Custom JVM truststore with corporate CA cert | `~/.gradle/gradle.properties` + `GRADLE_OPTS` | +| **curl / general HTTP** | `http_proxy`/`https_proxy` → `cso.proxy.att.com:8080` | `switch-network.sh` | + +### How it works + +1. `~/.zshrc` sets `export NETWORK=corp` (or `home`) +2. `~/.zshrc` sources `scripts/switch-network.sh` from this repo +3. The script conditionally sets/unsets all proxy env vars based on `NETWORK` +4. On new shell: `🏢 NETWORK=corp — proxy active` or `🏠 NETWORK=home — direct internet` + +### Key files + +| File | Purpose | +| -------------------------------------------------------- | ---------------------------------------------------------- | +| [`scripts/switch-network.sh`](scripts/switch-network.sh) | Central network switch — sets all proxy vars | +| `~/.gradle/gradle.properties` | Gradle daemon JVM args (truststore) + HTTP proxy host/port | +| `~/.gradle/ssl/gradle-cacerts.jks` | Java truststore = default cacerts + ATT CSO proxy CA cert | + +### Gradle SSL (TLS interception) + +The corporate proxy (`cso.proxy.att.com`) does TLS interception — it replaces upstream TLS certificates with its own, signed by the `ATTINTERNALROOTv2` CA. Gradle's JVM doesn't trust this CA by default. + +**Solution:** A custom Java truststore (`~/.gradle/ssl/gradle-cacerts.jks`) that includes: + +- All default Java CA certificates (from `$JAVA_HOME/lib/security/cacerts`) +- The AT&T CSO proxy CA certificate + +The truststore is passed to Gradle via: + +- `GRADLE_OPTS` env var (set by `switch-network.sh` when `NETWORK=corp`) — for the wrapper bootstrap JVM +- `org.gradle.jvmargs` in `~/.gradle/gradle.properties` — for the daemon JVM + +**Recreate truststore** (needed after Java updates): + +```bash +mkdir -p ~/.gradle/ssl +JAVA_HOME=$(/usr/libexec/java_home) +cp "$JAVA_HOME/lib/security/cacerts" ~/.gradle/ssl/gradle-cacerts.jks +echo | openssl s_client -connect services.gradle.org:443 \ + -proxy cso.proxy.att.com:8080 -showcerts 2>/dev/null \ + | awk 'BEGIN{c=0} /BEGIN CERT/{c++} c==2{print} /END CERT/&&c==2{exit}' \ + > /tmp/corp-ca.pem +keytool -importcert -noprompt -trustcacerts -alias att-cso-proxy \ + -file /tmp/corp-ca.pem \ + -keystore ~/.gradle/ssl/gradle-cacerts.jks -storepass changeit +``` + +### MUST follow (network-related) + +- Always use `NETWORK` env var — never hardcode proxy URLs in app code +- Gradle builds require `GRADLE_OPTS` with truststore when on corp network (handled automatically by `switch-network.sh`) +- If a Gradle build fails with SSL errors, verify `echo $GRADLE_OPTS` shows the truststore path +- If adding a new tool that fetches from the internet, add its proxy config to `switch-network.sh` +- `~/.gradle/gradle.properties` is a local-only file — never commit it to any repo + +### Kotlin Platform SDK (`packages/kotlin-platform-sdk/`) + +The shared Kotlin SDK is consumed by Android apps as a composite build via `includeBuild()` in each product's `settings.gradle.kts`. Key details: + +- `settings.gradle.kts` declares `pluginManagement` and `dependencyResolutionManagement` repos (required for composite builds to resolve their own deps) +- `build.gradle.kts` uses the Compose compiler plugin for UI components (`SurveyUI`, `InAppMessageUI`) +- Uses `kotlinOptions { jvmTarget = "17" }` (not `compilerOptions {}`) for compatibility + +## 10. Dependency Graph ``` @bytelyst/errors ← no deps (foundation) @@ -441,7 +515,7 @@ DEFAULT_PRODUCT_ID=lysnrai Build order: packages first (they have no inter-deps), then services. `pnpm build` handles this automatically via workspace topology. -## 10. Key Documents +## 11. Key Documents | When you need to... | Read this | | -------------------------------- | -------------------------------------------------------------------------------------------------- | @@ -452,7 +526,7 @@ Build order: packages first (they have no inter-deps), then services. `pnpm buil | LysnrAI product repo conventions | [`../learning_voice_ai_agent/AGENTS.md`](../learning_voice_ai_agent/AGENTS.md) | | MindLyst native repo conventions | [`../learning_multimodal_memory_agents/AGENTS.md`](../learning_multimodal_memory_agents/AGENTS.md) | -## 11. Service Test Counts +## 12. Service Test Counts | Service / Package | Tests | Runner | | ------------------ | -------- | ------ | @@ -478,7 +552,7 @@ Product-specific API modules have been migrated from platform-service into each Each product backend uses `@bytelyst/*` packages via `file:` refs and follows the same Fastify module pattern. -## 12. Common Pitfalls +## 14. Common Pitfalls 1. **Don't import `zod` from `@bytelyst/config` in services** — services bundle their own zod version. Use self-contained Zod schemas in `src/lib/config.ts`. 2. **Don't forget `productId`** — every Cosmos document must include it. diff --git a/scripts/switch-network.sh b/scripts/switch-network.sh index eabe2b56..e2f55c18 100755 --- a/scripts/switch-network.sh +++ b/scripts/switch-network.sh @@ -6,15 +6,40 @@ # Controls: one env var — NETWORK=corp or NETWORK=home # # Usage (add to ~/.zshrc): -# export NETWORK=corp # at work +# export NETWORK=corp # at work / on VPN # export NETWORK=home # at home (or just unset it) # # Then source this file from ~/.zshrc: # source "$HOME/code/mygh/learning_ai_common_plat/scripts/switch-network.sh" # -# What it sets: -# NETWORK=corp → http_proxy, https_proxy, npm/pnpm registry, pip trusted-host -# NETWORK=home → all proxy vars unset, default registries +# What it sets when NETWORK=corp: +# - http_proxy / https_proxy → cso.proxy.att.com:8080 +# - NPM_CONFIG_REGISTRY → AT&T JFrog npm proxy +# - NPM_CONFIG_PROXY → corporate proxy +# - NPM_CONFIG_STRICT_SSL → false (proxy TLS interception) +# - NODE_TLS_REJECT_UNAUTHORIZED → 0 (Node.js trusts proxy certs) +# - PIP_TRUSTED_HOST → pypi.org, files.pythonhosted.org +# - GRADLE_OPTS → JVM truststore with corporate CA cert +# (truststore at ~/.gradle/ssl/gradle-cacerts.jks) +# +# What it sets when NETWORK=home: +# - All proxy vars unset, default registries, direct internet +# +# Gradle SSL setup (one-time): +# The corporate proxy (cso.proxy.att.com) does TLS interception. +# Gradle's JVM needs a custom truststore with the proxy CA cert. +# To create/recreate: +# mkdir -p ~/.gradle/ssl +# JAVA_HOME=$(/usr/libexec/java_home) +# cp "$JAVA_HOME/lib/security/cacerts" ~/.gradle/ssl/gradle-cacerts.jks +# echo | openssl s_client -connect services.gradle.org:443 \ +# -proxy cso.proxy.att.com:8080 -showcerts 2>/dev/null \ +# | awk 'BEGIN{c=0} /BEGIN CERT/{c++} c==2{print} /END CERT/&&c==2{exit}' \ +# > /tmp/corp-ca.pem +# keytool -importcert -noprompt -trustcacerts -alias att-cso-proxy \ +# -file /tmp/corp-ca.pem \ +# -keystore ~/.gradle/ssl/gradle-cacerts.jks -storepass changeit +# # ───────────────────────────────────────────────────────────── _CORP_PROXY="http://cso.proxy.att.com:8080/"