---
url: https://docs.nemo12.com/quality/audit-standard/as-07-10.md
description: >-
  Chỉ báo AS-07 bảo mật và riêng tư, AS-08 tin cậy và vận hành, AS-09 trải
  nghiệm và design system, AS-10 AI governance và chi phí.
---

# Audit Standard · AS-07 tới AS-10

Một phần của [Audit Standard](./index.md). Chỉ báo AS-07 bảo mật và riêng tư, AS-08 tin cậy và vận hành, AS-09 trải nghiệm và design system, AS-10 AI governance và chi phí.

## AS-07 — Bảo mật, Riêng tư & Tuân thủ

| ID | Chỉ báo | Cách kiểm |
| --- | --- | --- |
| **AS-07.1** | **Xác thực & phiên** | |
| AS-07.1.1 | 🔴🚸 Mọi endpoint không public đều yêu cầu xác thực | `grep -c "requireSession" workers/api/src/modules` đối chiếu tổng số route; mỗi route thiếu phải được liệt kê + xác nhận "public có chủ đích" |
| AS-07.1.2 | Token/session có hạn và có cơ chế thu hồi | đọc `resolveSession` + `rotated_token` |
| AS-07.1.3 | `audience` / `issuer` của id_token được verify, không bỏ qua | đọc code xác thực OAuth |
| AS-07.1.4 | 🚸 Khu vực nội bộ (admin, dolphin) có lớp bảo vệ hạ tầng (Cloudflare Access), không chỉ check ở app | thử truy cập ẩn danh vào `admin.nemo12.com`/`mentors.nemo12.com` — kỳ vọng chặn ở Access trước khi tới app (đã xác nhận PASS theo đính chính Audit #001 §5 — giữ trong Pilot Gate để không âm thầm tắt lại) |
| AS-07.1.5 | Không có đường đăng nhập tắt / backdoor trên production | `grep -rn "bypass\|skip.*auth\|debug.*login"` + rà middleware |
| **AS-07.2** | **Phân quyền backend** | |
| AS-07.2.1 | 🔴🚸 Mọi truy cập learner data được authorize ở **backend**, không chỉ ẩn ở UI | rà từng endpoint chạm bảng có `learner_id`; `canAccessLearner()` (hoặc tương đương) phải xuất hiện trong mọi module đó |
| AS-07.2.2 | 🔴 RBAC dựa trên role/id, **không so sánh email** | `grep -rn "role.*===.*email\|\.email ===" workers/api/src` trong logic authz phải rỗng |
| AS-07.2.3 | 🔴🚸 Parent chỉ truy cập learner trong family của mình — có test chứng minh | `find workers/api/src -iname "*authz*test*" -o -iname "*parent*family*test*"` phải tồn tại và pass |
| AS-07.2.4 | Mentor truy cập rộng hơn nhưng **bắt buộc** ghi audit log | đọc code mentor routes + `SELECT COUNT(*) FROM audit_log WHERE actor_role='mentor'` > 0 |
| AS-07.2.5 | Ma trận quyền `reference/permissions.md` khớp kiểm tra thật trong code | đối chiếu |
| **AS-07.3** | **Dữ liệu trẻ em & đồng thuận** | |
| AS-07.3.1 | Mỗi trường dữ liệu trẻ em có mục đích ghi rõ (thu thập tối thiểu) | `data-dictionary.md` |
| AS-07.3.2 | 🚸 Ghi dấu vết đồng thuận (KHÔNG chặn) — chủ dự án 2026-08-15 quyết bỏ cổng chặn, xem SRC-137 | đọc luồng onboarding — có bảng `consents` ghi ai bật/tắt cái gì, lúc nào |
| AS-07.3.3 | Nội dung do trẻ viết (portrait learner sections) chỉ role learner được ghi | đọc code + `portraits/*.test.ts` |
| AS-07.3.4 | Không chia sẻ dữ liệu trẻ em sang bên thứ ba ngoài nhà cung cấp hạ tầng đã khai | rà lời gọi ra ngoài — `grep -rn "fetch(\"https://" workers/api/src` đối chiếu domain đã khai |
| AS-07.3.5 | Parent xem được dữ liệu nào đang dùng để ra quyết định về con mình | UI marlins |
| **AS-07.4** | **Audit log** | |
| AS-07.4.1 | Mọi truy cập learner data bởi role không phải chủ sở hữu được ghi log | đọc code — điểm ghi `audit_log` ở mọi route mentor/staff chạm learner data |
| AS-07.4.2 | Log ghi đủ: ai, cái gì, khi nào, từ đâu | schema `audit_log` — có `actor_id`, `target_id`, `created_at`, `ip`/tương đương |
| AS-07.4.3 | Log không chứa nội dung nhạy cảm nguyên văn (chỉ tham chiếu) | đọc mẫu log — chỉ `target_type`+`target_id`, không có nội dung |
| AS-07.4.4 | Log không thể bị ứng dụng sửa/xóa | rà endpoint/quyền — không route nào `UPDATE`/`DELETE audit_log` |
| AS-07.4.5 | Có đường truy vấn log phục vụ điều tra sự cố | UI admin/query endpoint |
| **AS-07.5** | **Bề mặt tấn công** | |
| AS-07.5.1 | 🔴 Không secret trong git history | `git log -p --all \| grep -E "(api[_-]?key\|secret\|token)\s*[:=]"` |
| AS-07.5.2 | 🔴 Không endpoint debug/seed mở trên production | `grep -rn "debug\|/seed" workers/api/src/index.ts` đối chiếu route thật |
| AS-07.5.3 | Input người dùng validate bằng schema trước khi chạm DB | rà router — mọi route có `.input()`/zod schema |
| AS-07.5.4 | Nội dung người dùng nhập được escape khi render (chống XSS) | `grep -rn "dangerouslySetInnerHTML" apps/*/src` — mỗi hit xác nhận nguồn không phải input người dùng trực tiếp |
| AS-07.5.5 | 🚸 Rate limit / chống lạm dụng cho endpoint tốn kém (AI, crawl, đăng nhập) | đọc code — middleware rate-limit trên route AI/login |

***

## AS-08 — Tin cậy & Vận hành

| ID | Chỉ báo | Cách kiểm |
| --- | --- | --- |
| **AS-08.1** | **Production-ready** | |
| AS-08.1.1 | Mỗi worker khai availability tier | docs/config |
| AS-08.1.2 | Trả lời được bằng văn bản 5 câu hỏi production-ready SDD-006 §16 | đọc |
| AS-08.1.3 | Mỗi tính năng mới định nghĩa "hỏng thì người dùng thấy gì" | đọc SDD |
| AS-08.1.4 | 🚸 Health endpoint phản ánh phụ thuộc thật, không trả 200 cứng | đọc code `index.ts` + `curl` — health chạy `SELECT 1` trên D1 thật |
| AS-08.1.5 | Không TODO/FIXME chặn đường còn sót trong mã đang chạy production | `grep -rn "TODO\|FIXME" workers/api/src apps/*/src` |
| **AS-08.2** | **Idempotency & DLQ** | |
| AS-08.2.1 | Mọi queue consumer idempotent theo message id | đọc code consumer |
| AS-08.2.2 | 🔴🚸 Mọi queue có DLQ cấu hình | `grep "dead_letter_queue" workers/api/wrangler.jsonc` |
| AS-08.2.3 | Có đường xử lý/replay message trong DLQ | quy trình + code |
| AS-08.2.4 | Retry có giới hạn và backoff, không vòng lặp vô hạn | đọc `max_retries` trong wrangler config |
| AS-08.2.5 | Cron job an toàn khi chạy chồng (overlap), hoặc có khóa | đọc code cron handler |
| **AS-08.3** | **Observability** | |
| AS-08.3.1 | Observability bật trên **mọi** worker | `grep -c "observability" workers/*/wrangler.jsonc` khớp số worker |
| AS-08.3.2 | Log có level và request id xuyên suốt một request | đọc code middleware log |
| AS-08.3.3 | Lỗi 5xx ghi kèm ngữ cảnh đủ để tái hiện | đọc code error handler |
| AS-08.3.4 | Có chỉ số theo dõi engine/queue (số run, số lỗi) | dashboard/query |
| AS-08.3.5 | Biết được một workflow đã chạy hay chưa mà không cần đọc log thô | `SELECT COUNT(*) FROM workflow_runs` > 0 |
| **AS-08.4** | **CI/CD & rollback** | |
| AS-08.4.1 | 🚸 CI chạy typecheck + lint + test **trước** khi deploy | `ci.yml` bước 1-3 — đã enforce trên mọi push; `deploy-app.yml` đọc lại kết quả ci qua `wait-for-ci.sh` |
| AS-08.4.2 | Deploy chỉ chạy cho app có thay đổi (path filter đúng) | đọc workflow `paths:` |
| AS-08.4.3 | 🔴🚸 Migration được áp trước code trong pipeline API | đọc `ci.yml` và `deploy-app.yml` — dòng apply trước dòng `wrangler deploy` ở cả hai |
| AS-08.4.4 | Có cách rollback nhanh về version trước cho từng worker | `rollback.yml` + quy trình ghi lại |
| AS-08.4.5 | Không bước deploy thủ công bắt buộc nằm trong đường chính | đọc workflow — mọi bước trong `on: push` tự động, không chờ tay trừ migration-apply (đã ghi rõ lý do ở AS-03.5.4) |
| **AS-08.5** | **Suy giảm duyên dáng & phụ thuộc ngoài** | |
| AS-08.5.1 | Mỗi phụ thuộc ngoài (AI, OAuth, crawl target) có hành vi fallback khi lỗi | đọc code |
| AS-08.5.2 | Lỗi phụ thuộc ngoài không làm sập trang chính | thử ngắt — mock lỗi AI, xem UI |
| AS-08.5.3 | Timeout đặt tường minh cho mọi lời gọi ra ngoài | `grep -rn "AbortSignal.timeout\|signal:" workers/api/src` |
| AS-08.5.4 | Có cache / kết quả gần nhất cho dữ liệu đọc nhiều | đọc code — KV cache hoặc tương đương |
| AS-08.5.5 | Sự cố hiển thị thông điệp người dùng hiểu được, không phải stack trace | thử lỗi — đọc response `errorResponse` |

***

## AS-09 — Trải nghiệm & Design System

| ID | Chỉ báo | Cách kiểm |
| --- | --- | --- |
| **AS-09.1** | **Luật tối giản (SRC-048)** — *từ v0.2: đòi artifact cụ thể, xem [§1.10](./history.md#_1-10-as-09-1-luat-toi-gian-—-tu-ra-thu-cong-sang-co-artifact-kiem-đuoc)* | |
| AS-09.1.1 | Mỗi màn hình có đúng **một** hành động chính rõ ràng | đọc `docs/reference/ui-minimalism-audit.md` cột "hành động chính"; artifact chưa tồn tại = KHÔNG PASS (mã CX) |
| AS-09.1.2 | Bố cục full-width, không cột phụ thừa | cột "bố cục" trong cùng artifact |
| AS-09.1.3 | Số element ở mức tối thiểu cần thiết — không badge/nhãn/đếm thừa | cột "số element" trong cùng artifact |
| AS-09.1.4 | Hình ảnh/biểu tượng ưu tiên hơn chữ giải thích dài | cột "hình/chữ" trong cùng artifact |
| AS-09.1.5 | Trang chủ mỗi cổng đóng vai trò **hub**, không phải trang nội dung dài | cột "là hub?" trong cùng artifact, riêng cho route `/` mỗi app |
| **AS-09.2** | **Token & component** | |
| AS-09.2.1 | Chỉ dùng token màu/khoảng cách của DS; không hardcode hex trong app — xem thêm AS-03.1.4 (góc khác: không *định nghĩa lại* token, đây là không *hardcode* khi dùng) | `grep -rEc "#[0-9a-f]{6}" apps/*/src` loại trừ file SVG/art |
| AS-09.2.2 | Không fork component: mỗi component dùng chung có đúng một định nghĩa | `grep -rn "function <TênComponent>" packages apps` phải chỉ 1 định nghĩa |
| AS-09.2.3 | Không CSS riêng theo trang ngoài phạm vi cho phép | rà file `.css` theo route |
| AS-09.2.4 | Portal theme chỉ đổi lớp accent, không đổi cấu trúc token | đọc `packages/design-system/tokens/base.css` |
| AS-09.2.5 | Font size sàn 14px, không có chữ nhỏ hơn | `grep -rn "text-xs\|font-size:\s*1[0-3]px" apps/*/src` |
| **AS-09.3** | **Accessibility** | |
| AS-09.3.1 | 🔴🚸 Tương phản màu đạt WCAG AA cho cả text thường và text lớn | `npm run contrast --workspace @nemo12/design-system`, **chạy trong CI** từ SRC-593. Trước đó chỉ tiêu này có script nhưng script không được nối vào CI, nên một cặp trượt ngưỡng nằm trên `main` mà không gì báo — chỉ tiêu đỏ mà không có cổng chạy thì chỉ là một lời hứa. **Từ SRC-827** cổng chấm mỗi cặp trong đúng ngữ cảnh nó chạy (`:root`, `.n12-v1`, `.n12-v1 .n12-deep`, `.theme-marlins.od-root`, `.n12-band-dark`, `.n12-nav-deep`) thay vì chỉ ở gốc, hoà màu mờ lên `--card` trước khi đo, và soi gương các remap utility của từng vùng; nợ cũ (nếu có) phải ghi TÊN trong `contrast-baseline.json`, nợ mới là đỏ. Ca mẫu: `bg-accent-ink text-white` được chấm 17:1 ở gốc nhưng là 1,11:1 trong `.n12-deep` (b09b40ea). **Từ SRC-842** cổng chấm thêm ở **ĐỈNH** của mặt gradient (od-root/marlins/deep) với lớp kính đặt lên đó — đỉnh suy từ token thật, không phải stop tuyến tính — đọc `.od-dim`/`.od-card` từ chính CSS và **từ chối chạy** nếu `.od-root` rời `--surface-deep`. Giới hạn đã biết: mực badge của marlins trong od-root (`.n12-badge-solid`/`-review`, `hover:bg-card`) là gương viết tay trong PAIRS — xoá rule ở index.css thì cổng vẫn xanh; kiểm bằng đo pixel trên build (DS-001 §2·1·26). |
| AS-09.3.2 | Mọi phần tử tương tác có focus state nhìn thấy được | thử tab qua bàn phím trên ≥3 màn hình chính |
| AS-09.3.3 | Touch target ≥ 44px trên mobile | đo trên DevTools mobile emulation |
| AS-09.3.4 | Mọi ảnh có `alt`, hoặc được đánh dấu là trang trí | `grep -rn "<img" apps/*/src \| grep -v "alt="` |
| AS-09.3.5 | Điều hướng bàn phím đi hết được luồng chính | thử tay — Tab qua luồng "trả lời câu hỏi" đầu-cuối |
| **AS-09.4** | **Ngôn ngữ & nội dung UI** | |
| AS-09.4.1 | Cổng learner/marlins dùng tiếng Việt; khu admin dùng **tiếng Anh toàn bộ** (SRC-098) | rà UI string trong `apps/admin/src`, `apps/learn/src` |
| AS-09.4.2 | Không lẫn hai ngôn ngữ trong cùng màn hình (trừ thuật ngữ giữ nguyên có chủ đích) | rà UI |
| AS-09.4.3 | Thuật ngữ dùng thống nhất theo `reference/glossary.md` | đối chiếu |
| AS-09.4.4 | Thông điệp lỗi viết cho người dùng, không phơi mã kỹ thuật trần | rà UI — `errorResponse` |
| AS-09.4.5 | Chữ trên UI không lặp lại nội dung đã hiển thị bằng hình/nhãn khác | rà UI |
| **AS-09.5** | **Hiệu năng cảm nhận** | |
| AS-09.5.1 | Trang chính hiện nội dung chính dưới ngưỡng đã khai báo | đo — Lighthouse LCP đối chiếu ngưỡng khai trong SDD |
| AS-09.5.2 | Không có layout shift lớn sau khi tải xong | Lighthouse CLS |
| AS-09.5.3 | Trạng thái loading / empty / error đều có thiết kế, không để trắng | rà từng màn — spot-check ≥5 màn |
| AS-09.5.4 | Ảnh/mascot phục vụ đúng kích thước, không tải ảnh quá khổ | network panel — so kích thước file với kích thước hiển thị |
| AS-09.5.5 | Thao tác chính (trả lời câu hỏi) phản hồi ngay, không chờ round-trip mới đổi UI | thử tay — chấm client-side trước, evidence gửi async |

***

## AS-10 — AI Governance & Chi phí

| ID | Chỉ báo | Cách kiểm |
| --- | --- | --- |
| **AS-10.1** | **Đường AI duy nhất** | |
| AS-10.1.1 | 🔴 100% lời gọi AI đi qua AI Gateway `nemo12` | `grep -rn "env.AI.run(" workers/api/src` mọi hit có `gateway: { id:` |
| AS-10.1.2 | 🔴 Không có probe-fallback tự dò model | đọc code — không vòng lặp thử nhiều model id |
| AS-10.1.3 | Model id khai tường minh, không dùng alias mơ hồ | `grep -rn "@cf/" workers/api/src` |
| AS-10.1.4 | Không có lời gọi AI trực tiếp từ frontend | `grep -rn "env.AI\|ai.run" apps/*/src` phải rỗng |
| AS-10.1.5 | Mọi điểm gọi AI có mặt trong `reference/ai-registry.md` | đối chiếu |
| **AS-10.2** | **Prompt registry** | |
| AS-10.2.1 | Mọi prompt production nằm trong registry, không viết inline rải rác | `grep -rn "const.*[Pp]rompt.*=.*\`" workers/api/src/modules`đối chiếu registry |
| AS-10.2.2 | Mỗi prompt có version | đọc registry — trường`PROMPT_VERSION`|
| AS-10.2.3 | Đổi prompt = tạo version mới, không sửa tại chỗ |`git log -p`spot-check |
| AS-10.2.4 | Mỗi prompt ghi mục đích, đầu vào kỳ vọng, ràng buộc đầu ra | đọc registry |
| AS-10.2.5 | Output AI có schema và được validate trước khi dùng | đọc code — không`z.object({ data: z.any() })`|
| **AS-10.3** | **Context Builder & ranh giới dữ liệu** | |
| AS-10.3.1 | 🔴🚸 Context Builder là đường **duy nhất** AI chạm learner data | rà code — mọi route gọi AI đi qua một hàm builder chung |
| AS-10.3.2 | Context gửi đi được tối thiểu hóa — chỉ trường cần cho tác vụ | đọc builder |
| AS-10.3.3 | Không gửi định danh trực tiếp của trẻ (tên thật, email) vào prompt khi không cần | đọc builder |
| AS-10.3.4 | Mỗi output AI lưu`input_ref`để truy ngược | schema |
| AS-10.3.5 | 🔴🚸 Có kiểm chứng dữ liệu learner A không lọt vào context của learner B |`find . -iname "*context*leak*test*" -o -iname "*context*isolation*test*"`phải tồn tại và pass |
| **AS-10.4** | **Đánh giá & chống ảo giác** | |
| AS-10.4.1 | 🔴🚸 Nội dung AI sinh ra không publish thẳng; phải qua ngưỡng đánh giá | đọc pipeline — route sinh nội dung AI không`INSERT`thẳng vào bảng published |
| AS-10.4.2 | Có multi-evaluator cho nội dung quan trọng (đề, lời giải) | đọc pipeline |
| AS-10.4.3 | Có tỷ lệ spot-check bởi người, được ghi lại thành số | log/ghi chú |
| AS-10.4.4 | Mọi khẳng định kiến thức do AI sinh có nguồn, hoặc được đánh dấu là sinh tự động | rà nội dung |
| AS-10.4.5 | Learner báo sai → có đường quay lại chỉnh nội dung, không rơi vào hư vô | đọc pipeline —`content_reports`nối vào hàng đợi thật |
| **AS-10.5** | **Chi phí & hạn mức** | |
| AS-10.5.1 | Mỗi loại lời gọi AI có ước tính chi phí ghi lại |`ai-registry.md\` |
| AS-10.5.2 | Có cách xem chi phí theo thời gian (dashboard/gateway) | dashboard Cloudflare AI Gateway — cần scope token đọc gateway |
| AS-10.5.3 | Có hạn mức hoặc cảnh báo khi vượt ngưỡng | config gateway |
| AS-10.5.4 | Tác vụ nền tốn kém có lịch chạy và giới hạn số lượng mỗi lần | cron config |
| AS-10.5.5 | Không có vòng lặp gọi AI không giới hạn kích hoạt được bởi hành vi người dùng | đọc code — route AI chỉ ở staff/admin hoặc có rate limit |

***
