# Access control

Clearance is the legacy **0–9 ladder**, but *which* level each capability needs
is configurable at runtime. A capability is called a **section**.

## How it works

1. **`aclRegistry()`** in `api/components/Base.cfc` is the single source of
   truth. Every section has a `label`, a built-in `def` (default level), and a
   `group` (used to lay out the Access Control screen).
2. **`gcc_admin.admin_acl`** stores overrides (`section`, `min_level`). An empty
   table means every section uses its default.
3. **`sectionLevel(section)`** resolves the effective level: override if present,
   otherwise the registry default. Overrides are cached in application scope for
   **60 seconds**, so a change takes effect within ~a minute (or immediately via
   `aclCacheBust()`, which the Access Control screen calls on save).
4. A section that is *not* in the registry resolves to **9**, so a typo fails
   closed rather than open.
5. **`acl` is pinned to 9** in code and can never be lowered — nobody can lock
   everyone out of the screen that fixes access.

## Enforcing a section

**Server-side — this is the real gate.**

```cfc
requireSection("players.fleet.edit");   // 403s when below the level
var canEdit = canSection("players.fleet.edit");   // boolean, for shaping output
```

Use `requireSection` at the top of any endpoint the section protects. Use
`canSection` to decide what to include in a response — typically a `perms`
struct the view reads, and to omit sensitive fields entirely rather than sending
them and hiding them client-side.

**Client-side — cosmetic only.**

```js
secLevel("players.fleet.edit")   // effective minimum level
canSec("players.fleet.edit")     // boolean for the signed-in admin
```

`state.me.sectionLevels` is the whole registry resolved for the current admin,
sent at login by `sectionLevelsFor()`. Use `canSec` to hide nav items, buttons
and tabs. **Never** rely on it for protection — the server re-checks every call.

Routes may also declare `sec:` (see `ARCHITECTURE.md` §5); that covers views
with no backing endpoint.

## Three gating styles

| Style | When to use | Example |
|---|---|---|
| Whole screen | The section maps to a nav area | `requireSection("economy")` at the top of every Economy endpoint |
| View / edit split | Reading is safe, writing is not | `players.fleet.view` (2) vs `players.fleet.edit` (5) |
| Basic / full tier | Everyone sees a redacted version, seniors see all | `events.basic` (1) vs `events` (3); `players.research.full` |

For a basic/full tier, the endpoint checks the *basic* section to allow entry,
then uses `canSection("<full>")` to decide how much to include, and records a
different audit action for each tier (see `AUDIT-LOGGING.md`).

## Adding a section

1. Add it to `aclRegistry()` with a label, default, and group.
2. Gate the server path with `requireSection` / `canSection`.
3. Gate the UI with `canSec`, and add `sec:` to the route if the view has no
   endpoint of its own.
4. If the view needs it, surface it in the endpoint's `perms` payload.

Nothing else is needed — the Access Control screen builds itself from the
registry, and `sectionLevelsFor()` sends every registry key to the client.

---

## Registry reference

Defaults as shipped. Any of these can be overridden per install from the Access
Control screen (level 9).

### Chat

| Section | Def | Capability |
|---|---|---|
| `chat.view` | 0 | View live chat feed |
| `chat.report` | 0 | Report a chat post |
| `chat.action` | 3 | Warn / silence from chat |
| `chat.remove` | 5 | Remove (scrub) a chat post |
| `chat.removed.view` | 3 | See original text of removed posts |
| `chat.removed.report` | 3 | Report an already-removed post |
| `chat.post` | 5 | Post to game chat as staff |

### Game

| Section | Def | Capability |
|---|---|---|
| `dashboard` | 1 | Overview / dashboard |
| `anticheat` | 3 | Anti-cheat tools |
| `economy` | 7 | Economy reports |
| `events.basic` | 1 | Global event watch (basic, no battles) |
| `events` | 3 | Global event watch (full, incl. battles) |
| `federations` | 3 | Federation view |
| `federations.edit` | 4 | Federation edit + change log |
| `federations.atwar.view` | 4 | Federation: view At War With |
| `federations.faction.view` | 9 | Federation: view faction |
| `federations.faction.edit` | 9 | Federation: edit faction |

> **Faction** is a reserved-for-future field. Both halves sit at 9 so the
> capability exists and is wired, but no staff member sees it today.

### Moderation

| Section | Def | Capability |
|---|---|---|
| `moderation.chat` | 2 | Chat complaints |
| `moderation.chatlog` | 3 | Recent moderation actions — chat + PM (PM rows also need `moderation.pm`) |
| `moderation.dismissed` | 3 | Dismissed complaints (the PM half also needs `moderation.pm`) |
| `moderation.email` | 3 | Email blacklist |
| `moderation.pm` | 4 | PM complaints |
| `moderation.bans` | 5 | IP blacklist |

### Overview widgets

| Section | Def | Capability |
|---|---|---|
| `dashboard.servers.basic` | 1 | Basic server stats (24h/7d/signups) |
| `dashboard.accounts` | 3 | Active / suspended account counts |
| `dashboard.servers.full` | 3 | Full server table |
| `dashboard.audit` | 5 | Recent admin activity |

### Players

| Section | Def | Capability |
|---|---|---|
| `players.view` | 1 | Open a player's page |
| `players.notes` | 1 | Player case notes |
| `players.realname.view` | 1 | See real name |
| `players.market.view` | 1 | View market posts |
| `players.events.basic.view` | 1 | Player's basic events (no battles) |
| `players.helplevel.view` | 2 | See in-game help level |
| `players.details.view` | 2 | See donation / country / turn timer |
| `players.resources.view` | 2 | View resources / minerals |
| `players.records.view` | 2 | View record + change logs |
| `players.fleet.view` | 2 | View fleet |
| `players.colony.view` | 2 | View colonies |
| `players.projects.view` | 2 | View projects |
| `players.events.view` | 2 | Player's full events (incl. battles) |
| `players.email.view` | 3 | See email |
| `players.ip.view` | 3 | See IP address |
| `players.turns.view` | 3 | See current turn balance |
| `players.turnstats.view` | 3 | Turn-usage stats (24h/7d/30d/lifetime) |
| `players.chathistory.view` | 3 | View chat history |
| `players.artifacts.view` | 3 | View artifacts |
| `players.research.full` | 3 | Research: full view (incl. off-race) |
| `players.realname.edit` | 3 | Edit real name |
| `players.empirename.edit` | 3 | Edit empire name |
| `players.email.edit` | 4 | Edit email / validation |
| `players.race.edit` | 4 | Edit race |
| `players.projects.edit` | 4 | Edit projects |
| `players.helplevel.edit` | 5 | Edit in-game help level |
| `players.fleet.edit` | 5 | Edit fleet |
| `players.colony.edit` | 5 | Edit colonies |
| `players.artifacts.edit` | 5 | Edit artifacts |
| `players.research.edit` | 5 | Edit research / researched / ships |
| `players.infra.edit` | 5 | Edit infrastructure levels |
| `players.resources.edit` | 5 | Edit resources / minerals |
| `players.market.edit` | 9 | Edit market posts |
| `players.password.edit` | 9 | Reset player password |

### Player actions

| Section | Def | Capability |
|---|---|---|
| `players.action.comment` | 2 | Add comment |
| `players.action.freeze` | 2 | Freeze |
| `players.action.refreshtech` | 3 | Refresh tech / ship |
| `players.action.suspend` | 4 | Suspend / unsuspend |
| `players.action.blacklist` | 4 | Blacklist |
| `players.action.disableturns` | 4 | Disable turns |
| `players.action.restart` | 4 | Restart empire |
| `players.action.silence` | 5 | Silence chat |

### Reference (Game Data)

| Section | Def | Capability |
|---|---|---|
| `gamedata.view` | 1 | View game data |
| `content.races.full` | 5 | Hidden races (7+) |
| `content.ships.full` | 5 | Hidden ships (race 7+, `UW.*`) |
| `content.planets.full` | 5 | Planet dig rate / special flag |
| `content.artifacts.full` | 5 | Raw-good artifacts (id 1–6) |
| `gamedata.edit` | 9 | Edit game data definitions |

### Panel

| Section | Def | Capability |
|---|---|---|
| `changelog` | 0 | What's New (panel changelog) |
| `admins.view` | 3 | View admin accounts |
| `admins.manage` | 5 | Manage admin accounts |
| `audit` | 5 | Audit log |
| `admins.logins` | 6 | View admin login history |
| `admins.guidelist` | 6 | In-game staff list (Guide / Mod / Admin) |
| `server.settings` | 6 | Server Settings — announcement banner |
| `server` | 9 | Server ops / settings |
| `acl` | 9 | Access control (pinned — not configurable) |

---

## Clearance level names

Shown in the sidebar and the admin editor. Defined once in `assets/app.js`
(`LEVEL_NAMES` / `levelName(n)`) so the two never drift.

| Level | Name |
|---|---|
| 0 | Guide |
| 1 | Senior Guide |
| 2 | Moderator |
| 3 | Senior Moderator |
| 4 | Lead Moderator |
| 5 | Junior Administrator |
| 6 | Administrator |
| 7 | Lead Administrator |
| 8 | Head Administrator |
| 9 | Super Administrator |

## Account management rules

Enforced in `Admins.cfc`, independent of the ACL registry:

- You may only manage accounts **strictly below** your own level.
- You may assign at most **your own level minus one**.
- You may never edit your **own** account (self password reset is still allowed).
- **Exception:** a level-9 superadmin may manage any account up to 9, including
  their own.
