# AGENTS.md — Utilities & Access Control > **Parent**: `../../AGENTS.md` — RBAC overview, permission groups, event system. ## Overview 10 files across 3 subdirectories. Core cross-cutting concerns: three-layer RBAC, fire-and-forget event logging, XP resolution. ## Structure ``` utils/ access-control/ 8 files Permission checking, role gates, qualification queries event-log/ 3 files Event emitter, type constants, formatting xp/ 1 file Level resolver ``` ## Access control layers Three independent access mechanisms, each used in different contexts: ### 1. `hasPermission(payload, user, "domain:action")` Full RBAC check against `src/permissions/index.ts` (100+ permissions). Superuser bypass. Cached 30s via `loadUserPermissions`. Used in server actions and collection access functions. ### 2. `isRole(role)` / `hasRoles(roles[])` Lightweight role checks. Used for quick conditional rendering (e.g., `isRole("admin")` for admin-only UI). No Payload call — reads from the user object directly. ### 3. `hasLogisticsQualification()` / `hasIntelligenceQualification()` Queries `Profiles.progression.qualifications` for specific qualification strings (case-insensitive). Admin/developer always pass. Used to gate logistics-only and intelligence-only UI sections. ## Event log system ### Emitter: `emitGameEvent(payload, { type, message, ... })` Fire-and-forget create on `game-event-logs`. Always sets `system: true`. Silent error catch (no throw). Always called AFTER successful mutation in server actions. ### Event types: `EventTypes` constants (~105 types across 15+ categories) Defined in `eventTypes.ts`. Use these constants for TypeScript narrowing on the `type` field. Categories include: `mission:*`, `finance:*`, `market:*`, `structure:*`, `staff:*`, `logistics:*`, `bank:*`, `notification:*`, `locker:*`, `xp:*`, `ticket:*`, `wiki:*`, `minigame:*`, `attendance:*`, `system:*`. ### Display: `formatType(type)` — human-readable label for event types. ## Where to look | Task | Path | |------|------| | Add new RBAC permission | `src/permissions/index.ts` + use in `hasPermission` calls | | Add qualification gate | `access-control/hasLogisticsQualification.ts` (or create similar) | | Add event type constant | `event-log/eventTypes.ts` (add to `EventTypes` object) | | Emit event from action | `import { emitGameEvent } from "@/utils/event-log/emit"` | | Check qualification in layout | Use `hasLogisticsQualification(payload, user)` | | Modify XP calculation | `xp/resolveLevel.ts` | ## Anti-patterns - **NEVER** throw in `emitGameEvent` — it's fire-and-forget by design - **NEVER** use `isRole` for permission-sensitive operations — use `hasPermission` instead - **NEVER** hardcode qualification strings — use the constants in the qualification collection - **NEVER** add event types without adding to `EventTypes` constants (breaks TypeScript narrowing)