# STATUS — AINS Microservices

Cold-start brief. Read this first; full detail in `docs/PLAN.md`.

Last updated: 2026-10-16  |  **Phase: 0 essentially complete**

---

## Where we are

- Phase 0 foundations done. Repo is **not** git-initialized; owner commits
  manually (no git operations by the agent).
- Go **1.27.2** installed at `/usr/local/go` (symlinked to `/usr/local/bin`,
  `PATH` via `/etc/profile.d/golang.sh`).
- Module path: `gitlab.com/dynaweb/ains-microservices`.
- `docker compose up` brings up MySQL + Redis + NATS + fake-gcs + all 7 services;
  **every service `/healthz` returns 200**.
- **Caveat:** a compose stack is **currently running** from an older build (it
  still has a `reporting-1` container). Rebuild+recreate before trusting it; see
  "Resume here".

## Resume here (next session)

1. Rebuild/recreate compose (memory-safe): `COMPOSE_PARALLEL_LIMIT=1 docker compose
   build <svc>` per service, then `docker compose up -d`. The `edge`/`auth` images
   must be rebuilt to pick up the new Go code and embedded `web/` assets.
   (The stale stack occupies `:8080-:8087`; stop it first.)
2. Re-verify `/healthz` on all 7 services and the mock OAuth flow through the
   `edge` container (`GOOGLE_MOCK=true`), then flip mocks off for real Google +
   IAM Delima.
3. Start Phase 1: pick the first vertical slice — recommended **auth** (complete
   the profile/`*-student-list` pages) or **record** (`reading_record` write path
   + totals + `report_student_record`). See `docs/BUSINESS-PROCESSES.md`.
- Local verification used throwaway binaries on ports `19080/19081` (edge/auth)
  with `GOOGLE_MOCK=true`, `IAM_DELIMA_MOCK=true`; those test servers are stopped.

### Done

- `go.mod` / `go.sum` (Gin).
- `docker-compose.yml`: mysql 8.0 (`:3307`), redis (`:6379`), nats (`:4222`/`:8222`),
  fake-gcs (`:4443`), and services edge(`:8080`) auth(`:8081`) record(`:8082`)
  library(`:8083`) chatbot(`:8084`) content(`:8086`) worker(`:8087`).
  (reporting-svc removed — reporting folded into record/content.)
- `Dockerfile` (multi-stage, `ARG SERVICE`, distroless) + `.dockerignore`.
- `pkg/*` primitives (all building blocks):
  - `config` (env loader + `.env` support)
  - `middleware` (`NewRouter` with `/healthz`, self `healthcheck` subcommand)
  - `db` (MySQL `database/sql` pool + ping)
  - `jwt` (HS256 access/refresh issue + parse, type checks)
  - `events` (NATS `Bus`: publish/subscribe/queue)
  - `i18n` (MY/EN translator, default `ms`, key fallback)
  - `scope` (role x state/PPD/school visibility)
  - `gcs` (`Store` interface + fake-gcs-compatible HTTP client)
  - `media` (upload + `/ebook/proxy` stream via `gcs.Store`)
  - tests: `go test ./pkg/...` green (`scope`, `i18n`, `jwt`, `gcs`, `media`).
- `cmd/{edge,auth,record,library,chatbot,content,worker}/main.go`.
- `internal/auth` + `internal/edge`: service logic (kept out of `cmd/` for
  testability). `web/` is a Go package holding embedded assets (`embed.go`,
  `view.go`, `static.go`, `i18n.go`).
- `scripts/snapshot.sh`: curated dev dump from the live DB (service-owned tables
  only, `ROW_LIMIT=1000` rows/table) into `db/snapshot/{01-schema,02-data}.sql`.
- Scaffold dirs: `web/templates/{layouts,pages,partials}`, `web/static/vendor`,
  `db/migrations/<svc>/`.
- `AGENTS.md` moved to `docs/LEGACY-REFERENCE.md` (reference only).
- `docs/BUSINESS-PROCESSES.md`: legacy business processes + requirement ->
  service mapping + boundary rule.
- **Auth-svc Google OAuth login flow** (`internal/auth/*`, `pkg/oauth2`,
  `pkg/delima`): `/auth/login` -> Google (or `GOOGLE_MOCK`) ->
  `/auth/google/callback` -> IAM Delima enrichment -> role/scope resolve ->
  `frontend_user` upsert -> JWT access+refresh in HttpOnly cookies -> redirect
  `/web/{role}-dashboard`. Plus `/auth/me`, `/auth/logout` (jti denylist),
  `/auth/refresh`. Tests green (`internal/auth`, `pkg/oauth2`, `pkg/delima`).
- **CI wired**: `.gitlab-ci.yml` (lint/test/build), `.golangci.yml` (v2),
  `Makefile`; `gofmt -l` clean, `golangci-lint run` 0 issues, `go test` green.
- **Web assets + layouts** (embedded via `web/embed.go`, no bundler):
  vendored `htmx 2.0.4`, `vue 3.5.13` (`vue.global.prod.js`), `bootstrap 5.3.3`
  (+icons 1.11.3) under `web/static/vendor`; `web/templates/{layouts,partials,pages}`,
  `web/static/{css,js}`, `web/i18n/{ms,en}.json`. `web` package view loader +
  `internal/edge` handler: `/`, `/reading-center`, `/web/:page`, `/lang`,
  `/api/ping`, `/static/*`, and a reverse proxy `/auth/*` -> `AUTH_UPSTREAM`.
- **Verified end-to-end** (mock): edge home/static/htmx/vue 200; OAuth
  login -> callback -> JWT cookie -> `/auth/me` (`role=murid`) -> logout revokes
  (`401`).

### Not done (Phase 0 remainder)

- `pkg/gcs` currently targets fake-gcs via the GCS JSON API over plain HTTP;
  swap to the real Google client when media/PROD work starts.
- `web/` is no longer excluded from `.dockerignore` (needed for `go:embed`);
  rebuild images so embedded assets ship.

---

## Environment facts (new)

- DB snapshot: **45 tables, ~1k rows/table** (dev only; NOT full legacy data).
  `frontend_*` page/block builder tables are **excluded** (dropped in the
  redesign).
- Live legacy DB for re-snapshot: MySQL `127.0.0.1:3306`, `dbname=ains`,
  user `dynaweb`, pass `Dynaweb@huawei`. Regenerate with `scripts/snapshot.sh`.
- Host constraints: **7.6 GiB RAM / 2 vCPU, no swap**. `docker compose build`
  OOM-crashed the box when building all services in parallel. Build with
  `COMPOSE_PARALLEL_LIMIT=1` one service at a time; `Dockerfile` sets `GOFLAGS=-p=1`.
- New env vars (in `.env.example` + compose `x-app-env`): `GOOGLE_MOCK`,
  `IAM_DELIMA_MOCK` (dev-only, default `false`); `AUTH_COOKIE_NAME` (`ains_token`),
  `AUTH_COOKIE_DOMAIN`, `AUTH_COOKIE_SECURE`, `AUTH_POST_LOGIN_REDIRECT`
  (`/web/home`); `AUTH_UPSTREAM` (edge -> auth; compose sets `http://auth:8081`).
- Lint binary `golangci-lint` is **not installed** on the host by default; CI uses
  the `golangci/golangci-lint:v2.14.0` image. (`/tmp/gobin/golangci-lint` was a
  throwaway download for this session.)

---

## Locked decisions (summary)

1. **MySQL kept**, same `ains` DB; per-service table ownership.
2. **JWT stateless** — HttpOnly cookie for SSR/HTMX, `Bearer` for Vue/API; Redis
   denylist for revocation; no session store.
3. **Admin rebuilt in Go/HTMX, Dynaweb page/block builder dropped.** Pages are
   code-defined routes + Go templates; only publishing data stays DB-driven.
4. **No git operations** by the agent.
5. **Single site: AINS NILAM** (legacy `site_id = 20`, slug `web`, default);
   `site_id = 1` ignored.
6. **Auth = Google OAuth + IAM Delima enrichment.** Google OAuth (creds in
   `frontend/config/params-local.php`) then an IAM Delima API call
   (`GoogleAuthController::getUserDetailDelima`) resolves role/school/class.
   IAM Delima is required for role, not just optional SSO.
7. **Reporting in scope**, designed for millions of rows.
8. **No `eval()`** — redesign `report_code`, `api`, chatbot `php_status` paths.
9. **Reporting dissolved** into owning services: reading-record reporting ->
   `record-svc`, content reporting -> `content-svc`. No `reporting` service.
10. **`record` table deferred** (legacy import from the older system; not part of
    the rewrite yet).
11. **Boundary rule** — tightly-coupled process in one service; same-service
    direct SQL, cross-service REST/NATS only.
12. **Student reading totals + scoring config live in `record-svc`** (moved out
    of auth). `report_student_record` and all reading summaries are record-owned
    so dashboards/scoreboards serve fast with no cross-service call.
13. **Frontend flows mapped** in `docs/BUSINESS-PROCESSES.md` (page -> service,
    incl. student/teacher record save + dashboards).

Stack: Go + Gin; Go `html/template` + HTMX + Vue 3 global (no bundler).

---

## Services (target)

`edge`, `auth`, `record` (+ reading-record reporting), `library`, `chatbot`,
`content` (+ content reporting), `worker`, plus shared `pkg/media`.
**No standalone reporting service** — reporting lives with the data owner.

Boundary rule: tightly-coupled domain/process in one service. **Within a
service:** direct SQL. **Between services:** REST/JSON or NATS only — no
cross-service SQL; denormalize cross-domain data at write time.

## Open items to resolve

1. `record` (legacy millions, older-system import) is **deferred** — revisit only
   if that historical data must be carried over.
2. Google OAuth: `GOOGLE_REDIRECT_URL` is wired (default
   `http://localhost:8080/auth/google/callback`); set the real host + add it to
   the Google Console redirect URIs for deployment.
3. Notification/email service in Phase 1 or later?
4. Denylist is **in-memory** per instance; switch to Redis (shared, survives
   restart) before running auth at more than one replica.

## Conventions

- No comments in generated code unless requested.
- No git commits — write files only.
- Lint/test: `make lint` / `make test`; CI in `.gitlab-ci.yml`.
