# Track A Handoff — 2026-03-29

This handoff captures the current state of Track A on the Azure VM at `/opt/bytelyst/`.

Registrar-specific DNS runbook:

- [`GODADDY_DNS_SETUP_BYTELYST.md`](/opt/bytelyst/learning_ai_common_plat/docs/devops/vercel/GODADDY_DNS_SETUP_BYTELYST.md)

Architecture decision after this handoff:

- The VM should host self-hosted infrastructure, backend APIs, and internal web tools in Docker
- `api.bytelyst.com`, `gitea.bytelyst.com`, `admin.bytelyst.com`, and `tracker.bytelyst.com` should point to the VM
- Monitoring and observability stacks such as Grafana and Loki should stay on the VM as internal-only tools
- Future shared infrastructure such as Valkey and PostgreSQL + pgvector should also be treated as VM-hosted internal services
- Only clearly public-facing sites should be considered for Vercel later

## What Was Completed

- Fixed a blocking parse error in [`docker-compose.ecosystem.yml`](/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml) by removing a duplicate `efforise-web` service definition.
- Added [`/opt/bytelyst/Caddyfile`](/opt/bytelyst/Caddyfile) with path-based routing for the 13 backend services.
- Added a tracked template copy at [`docs/devops/single_azure_vm/docker/Caddyfile.bytelyst.example`](/opt/bytelyst/learning_ai_common_plat/docs/devops/single_azure_vm/docker/Caddyfile.bytelyst.example).
- Updated [`docker-compose.ecosystem.yml`](/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml) to:
  - add a `caddy` service
  - place Traefik `gateway` behind a `legacy-gateway` profile
  - remove published backend ports for `4003`, `4005`, `4007`, `4010`-`4019`
- Updated dashboard Dockerfiles to use the Next.js standalone runtime:
  - [`dashboards/admin-web/Dockerfile`](/opt/bytelyst/learning_ai_common_plat/dashboards/admin-web/Dockerfile)
  - [`dashboards/tracker-web/Dockerfile`](/opt/bytelyst/learning_ai_common_plat/dashboards/tracker-web/Dockerfile)
- Updated deployment docs:
  - [`docs/devops/single_azure_vm/docker/README.md`](/opt/bytelyst/learning_ai_common_plat/docs/devops/single_azure_vm/docker/README.md)
  - [`docs/devops/single_azure_vm/docker/DEPLOYMENT_STATUS_2026-03-29.md`](/opt/bytelyst/learning_ai_common_plat/docs/devops/single_azure_vm/docker/DEPLOYMENT_STATUS_2026-03-29.md)
- Updated the Track A progress table to mark A1 in progress:
  - [`docs/devops/vercel/CODEX_PROMPTS_TRACK_A_AZURE_VM.md`](/opt/bytelyst/learning_ai_common_plat/docs/devops/vercel/CODEX_PROMPTS_TRACK_A_AZURE_VM.md)

## Blockers Found

### 1. DNS is not ready

At the time of the original session, these records did not resolve from the VM:

- `api.bytelyst.com`
- `gitea.bytelyst.com`
- `admin.bytelyst.com`
- `tracker.bytelyst.com`

For the current architecture, all four records must resolve to the VM.

### 2. Azure CLI is installed, but the active subscription context still needs validation

Azure CLI is now installed and `az account show` succeeds.

Current observed context:

- subscription: `Azure subscription 1`
- tenant: `eScube`
- state: `Disabled`

This means A4 is no longer blocked by a missing CLI, but it may still be blocked until the correct active subscription is selected and has permission to manage the VM NSG.

### 3. Linux build host is still using the wrong Gitea registry hostname

Broad compose rebuilds hit repeated package fetch failures such as:

```text
GET http://host.docker.internal:3300/api/packages/bytelyst/npm/... error (ENOTFOUND)
```

The VM is Linux, and many builds still default to `GITEA_NPM_HOST=host.docker.internal`. On this VM the Gitea registry is reachable at `localhost:3300` on the host and via the detected Docker host IP used by `setup.sh`.

This blocks reliable rebuilds for A3 and potentially other services.

## Required Preconditions Before Resuming

1. Create or confirm VM DNS records for:
   - `api.bytelyst.com`
   - `gitea.bytelyst.com`
2. Confirm they resolve to the VM public IP from the VM:

```bash
dig +short api.bytelyst.com
dig +short gitea.bytelyst.com
curl -sf https://api.ipify.org && echo
```

3. Point `admin.bytelyst.com` and `tracker.bytelyst.com` at the VM, not Vercel.
4. Verify Azure account context and switch to the correct active subscription if needed:

```bash
az account show -o table
az account list -o table
az account set --subscription "<active-subscription-name-or-id>"
```

5. Export a Linux-safe Gitea host before rebuilds, for example:

```bash
export GITEA_NPM_HOST=172.17.0.1
```

Use the actual Docker-reachable host IP if it differs on this VM.

## Resume Steps

Run from:

```bash
cd /opt/bytelyst/learning_ai_common_plat
```

### A1

1. Confirm backend health:

```bash
docker compose -f docker-compose.ecosystem.yml --env-file .env.ecosystem ps
docker compose -f docker-compose.ecosystem.yml --env-file .env.ecosystem ps | grep -c healthy
```

2. Start or refresh only the gateway-related services first:

```bash
docker compose -f docker-compose.ecosystem.yml --env-file .env.ecosystem up -d caddy platform-service extraction-service mcp-server peakpulse-backend chronomind-backend jarvisjr-backend nomgap-backend mindlyst-backend lysnrai-backend notelett-backend flowmonk-backend actiontrail-backend localmemgpt-backend
docker logs caddy --tail 100
```

3. Verify A1:

```bash
curl -sI https://api.bytelyst.com/platform/health | head -5
for svc in platform extraction mcp peakpulse chronomind jarvisjr nomgap mindlyst lysnrai notelett flowmonk actiontrail localmemgpt; do
  echo -n "$svc: "
  curl -sf https://api.bytelyst.com/$svc/health | jq -r '.status // "FAIL"'
done
curl -sf --max-time 3 http://<VM_PUBLIC_IP>:4003/health && echo "FAIL: port still open" || echo "PASS: port closed"
```

### A2

1. Reconfigure the standalone `gitea-npm-registry` container for HTTPS exposure. It is not managed by the ecosystem compose file.
2. Update its `ROOT_URL` to `https://gitea.bytelyst.com`.
3. Add a `gitea.bytelyst.com` block to `/opt/bytelyst/Caddyfile`.
4. Reload Caddy.
5. Verify:

```bash
curl -sI https://gitea.bytelyst.com | head -3
curl -sf https://gitea.bytelyst.com/api/packages/ByteLyst/npm/@bytelyst%2ferrors | jq '.name'
docker exec platform-service curl -sf http://gitea:3300/api/v1/version | jq '.version'
```

Note:
The current running container is:

```bash
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' | grep -i gitea
```

Observed name during this session:

```text
gitea-npm-registry
```

### A3

1. Keep `admin-web` and `tracker-web` as VM-hosted internal dashboards.
2. Configure them to use `https://api.bytelyst.com` for browser/backend access.
3. Add Caddy host routing for:
   - `admin.bytelyst.com` -> `admin-web:3001`
   - `tracker.bytelyst.com` -> `tracker-web:3003`
4. Point `admin.bytelyst.com` and `tracker.bytelyst.com` DNS to the VM.
5. Document access controls for these internal dashboards:
   - app auth at minimum
   - optional IP allowlist, VPN, or SSO
   - no public raw container port exposure

### A4

1. Install Azure CLI if still missing.
2. Identify the resource group and NSG name for this VM.
3. Snapshot rules, then reduce inbound access to only `22`, `80`, and `443`.
4. Run the full A4 verification from the Track A prompt file.

## Suggested Commit Sequence

After each prompt passes, update the progress table in:

- [`docs/devops/vercel/CODEX_PROMPTS_TRACK_A_AZURE_VM.md`](/opt/bytelyst/learning_ai_common_plat/docs/devops/vercel/CODEX_PROMPTS_TRACK_A_AZURE_VM.md)

Suggested commits:

- `feat(gateway): replace Traefik with Caddy for HTTPS path routing`
- `feat(gateway): expose Gitea npm registry via HTTPS at gitea.bytelyst.com`
- `docs(architecture): keep internal dashboards on VM Docker`
- `chore(security): lock down Azure NSG to 22/80/443 only`
