# ForgeGuard exploit and abuse coverage

ForgeGuard 0.6.0-alpha.1 has 26 server-side combat, building and movement checks (below), per-player violation levels, an opt-in response ladder, the configurable event-abuse budgets and bounded book-text inspection from 0.3. These are specific safeguards, not an assurance that a player is cheating or that every Minecraft exploit is blocked. The movement observation model remains restricted to its documented native 1.21.11 environments.

## Implemented protections

| Family | What ForgeGuard observes | Optional `CANCEL` behavior | Default burst / refill per second |
| --- | --- | --- | --- |
| `inventory-click` | Inventory click and drag events, sharing one budget | Cancels an excess click or drag | 240 / 80 |
| `item-drop` | Player item-drop events | Cancels an excess drop | 160 / 40 |
| `interact` | Uncancelled block/air interactions and entity right-click interactions, including interaction-at-position events | Cancels an excess interaction | 400 / 120 |
| `book-edit` | Book editing and signing events | Cancels an excess edit | 20 / 4 |
| `command` | Player command preprocess events | Cancels an excess command | 60 / 12 |
| `book-content` | Page count, per-page Unicode codepoints, title codepoints, total UTF-8 text bytes and malformed surrogate pairs | Cancels the proposed book edit | See limits below |

Each player has separate budgets for each event family. A command flood therefore does not consume another player's budget or that player's inventory budget. Buckets start full and refill using elapsed time; normal tick and network bursts have headroom, while accumulated credit never exceeds the burst limit. Excesses do not add unbounded token debt.

The default mode is `OBSERVE`: incidents are recorded, and no event is cancelled by these guards. Setting `guards.mode: CANCEL` opts in to cancellation of the specific over-budget or oversized-book event. The guards never kick or ban automatically. Other plugins can subsequently change event cancellation, so the `cancelled` metric means cancellation requested by ForgeGuard.

Book defaults allow 100 pages, 4096 Unicode codepoints per page, 128 title codepoints and 1,048,576 UTF-8 text bytes across the title and pages. Astral characters count as one codepoint and four UTF-8 bytes. These defaults admit 100 pages of 1024 astral characters. The scanner short-circuits on limits and does not allocate encoded copies of each page. It reads already-decoded Bukkit book text, not the original packet payload, component JSON or full item NBT. Once a book-edit rate budget is exhausted, extra metadata copying/scanning is skipped in either mode and `skippedBookScans` increases; an `OBSERVE` event still proceeds.

## Behaviour checks (0.6)

Judged only from the server's own state: entity positions and hitboxes, the player's reach attributes, block states and collision shapes, the break speed the server computes for that player, tool and block, and the knockback velocity the server itself sent. Each needs repeated violations before it becomes a case, and cases per check are rate limited (`behaviour.case-cooldown-seconds`, default 20). Movement checks pause for players above `behaviour.max-ping-ms` (300) and while the server is below `behaviour.minimum-tps` (17); buffers reset so a stall never completes a sequence.

| Check (`key`) | Signal | Default | `CANCEL` cancels | Setback |
| --- | --- | --- | --- | --- |
| Reach (`combat.reach`) | Eye-to-hitbox distance beyond the reach attribute + leniency + the target's measured movement during the attacker's latency | 0.35 blocks leniency, 3 hits in a row | The hit | |
| Hit angle (`combat.hit-angle`) | Target far outside the look direction, judged from 1.5+ blocks | 100°, 3 hits | The hit | |
| Attack rate (`combat.attack-rate`) | Melee attacks per second | 18 | Nothing (observe only) | |
| Multi-target (`combat.multi-target`) | Distinct entities hit in a window, sweeps excluded | more than 3 in 500 ms | The hit | |
| Autoclicker (`combat.autoclicker`) | Standard deviation of the tick gaps between left clicks not aimed at a block, over a window of clicks at a minimum rate | 30 clicks, 8/s, deviation ≤ 0.35 ticks | Nothing (observe only) | |
| Criticals (`combat.criticals`) | Critical damage while the last move had a server-verified floor under the feet and no vertical motion | 4 hits | The hit | |
| Aim snap (`combat.aim-snap`) | The rotation before a hit moved from far off the target to within 12° of it, from 2+ blocks | 45°, 4 hits | The hit | |
| Velocity (`combat.velocity`) | Four ticks after server knockback with no block in the path, the horizontal distance moved as a fraction of the velocity magnitude | below 25%, 3 times | Nothing (observe only) | |
| Fast break (`world.fast-break`) | Break time against the server's break speed, or finishing a break never started | 30% tolerance, 2 breaks | The break | |
| Fast place (`world.fast-place`) | Placements per second | 12 | The placement | |
| Scaffold (`world.scaffold`) | Placing against a face that points away from or sits behind the player, or against a liquid | 3 placements | The placement | |
| Block reach (`world.block-reach`) | Broken block beyond the block reach attribute + leniency, or more than 95° outside the view | 0.5 blocks leniency, 3 breaks | The break | |
| Nuker (`world.nuker`) | Non-instant blocks finished per second | 10 | The break | |
| Tower (`world.tower`) | Blocks placed directly under the player's own feet per second while rising | 4 | The placement | |
| Ground spoof (`movement.ground-spoof`) | Client claims ground with nothing collidable below (fall-damage avoidance) | 5 moves | Nothing (observe only) | |
| Hover (`movement.hover`) | Constant height in open air without support | 16 moves | Nothing (observe only) | |
| Flight (`movement.flight`) | Consecutive ascending moves in open air, not bouncy, no pistons nearby, beyond a jump plus jump boost | 12 moves + 4 per boost level | The move | yes |
| Glide (`movement.glide`) | Descent slower than the ceiling in open air, not touching a wall | 0.1 blocks/move, 10 moves | The move | yes |
| Step (`movement.step`) | Floor-to-floor rise above 0.6 blocks in one move, no pistons, bounce or liquid | 2 moves | The move | yes |
| Phase (`movement.phase`) | The hitbox (shrunk 0.1) overlaps a full opaque block's collision shape at the destination but not at the origin | 3 moves | The move | yes |
| Water walk (`movement.water-walk`) | Ground claimed with liquid directly below, feet not in liquid, nothing else supporting | 8 moves | The move | yes |
| Spider (`movement.spider`) | Steady 0.1–0.35 ascent next to a wall without support or a climbable | 5 moves | The move | yes |
| Fast climb (`movement.fast-climb`) | Vertical speed on a climbable above the ceiling | 0.2 blocks/move, 6 moves | The move | yes |
| No-slow (`movement.no-slow`) | Horizontal speed while using an item above the ceiling × speed attribute scale, on a verified floor, not slippery | 0.15 blocks/move, 6 moves | The move | yes |
| Inventory move (`movement.inventory-move`) | Horizontal speed above 0.12 × speed scale within 3 ticks of an inventory click | 6 moves | Nothing (observe only) | |
| Bad rotation (`movement.bad-rotation`) | Pitch outside -90..90 | 1 move | The move | |

Exempt from the movement checks: creative and spectator players; flight permission or flying, gliding, swimming, riptide, vehicles, lava, sleeping, death, levitation and slow falling; recent damage; nearby boats, minecarts, shulkers, armour stands and players; unloaded destination chunks; join, teleport, velocity and integration grace; translated or unidentified clients; and `forgeguard.bypass`. Flight additionally skips bouncy floors (slime, beds) and nearby pistons; glide skips wall contact (honey); no-slow and inventory move skip ice, slime, honey, bubble columns and soul blocks; water walk needs the feet out of liquid. Bad rotation is judged even for exempt players because a vanilla client cannot send it.

Limits: these are server-side geometry and timing, not a client prediction. Vanilla already refuses melee well beyond reach, so reach cases cover the gap between the reach attribute and the server's own limit. Attack-rate, autoclicker, hover, glide and inventory-move are statistical signals with legitimate edge cases, criticals and aim snap are experimental heuristics, and ground checks cannot see every modded or plugin-provided surface. Review the case, the player's context, `/fg vl` and `/fg inspect` before acting. The status's `behaviour.lastAttack` shows the most recent reach measurement for tuning.

## Violation levels and the response ladder

Every case adds `response.finding-weight` (1.0) to the player's violation level for that check; levels fade by `response.decay-per-minute` (1.0) without new findings. `/fg vl`, `/fg top`, the player card and the website status show them. VL is a triage number: a protocol-invalid packet and a borderline experimental signal both add one point.

`response.enabled` (default `false`) turns on the ladder. Steps are `{vl, action}` entries: `ALERT_STAFF`, `SETBACK` (to the last verified floor), `KICK` (`message`), `TEMPBAN` (`seconds` 60–31,536,000 and `reason`, through the same audited UUID ban store as staff bans) or `COMMAND` (`command`, a console command without a leading slash). Placeholders `{player}`, `{uuid}`, `{check}`, `{vl}` and `{case}` are filled. Steps are sorted by VL, fire once each as the total climbs and only fire again after the VL has fully decayed. `excluded-checks` (default: packet rate, timer window, attack rate) never count toward VL. Protected (`forgeguard.protected`) and bypassing (`forgeguard.bypass`) players only ever trigger `ALERT_STAFF`. Each applied step is audited (`response.<action>`) before it runs; a failed audit means nothing happens.

## Configuration

All listed defaults are available in `config.yml`. Reload after changing values.

```yaml
guards:
  enabled: true
  mode: OBSERVE
  alert-cooldown-seconds: 15
  maximum-tracked-players: 10000
  rates:
    inventory-click: {enabled: true, burst: 240, per-second: 80}
    item-drop: {enabled: true, burst: 160, per-second: 40}
    interact: {enabled: true, burst: 400, per-second: 120}
    book-edit: {enabled: true, burst: 20, per-second: 4}
    command: {enabled: true, burst: 60, per-second: 12}
  book-limits:
    enabled: true
    maximum-pages: 100
    maximum-page-codepoints: 4096
    maximum-total-utf8-bytes: 1048576
    maximum-title-codepoints: 128
```

`guards.enabled` disables all six families; each rate family and the book limits can also be disabled individually. Supported modes are exactly `OBSERVE` and `CANCEL` (case insensitive). Booleans must be YAML booleans, numeric values must be finite, and integer fields reject fractions. Incorrect section shapes are rejected instead of silently using defaults.

Validated ranges: burst and refill 1–100,000; incident cooldown 1–3600 seconds; tracked players 1–100,000; book pages 1–1000; page codepoints 1–65,536; title codepoints 1–4096; total UTF-8 bytes 1–16,777,216. A configurable limit does not change Minecraft's own book, packet or protocol limits. Very low limits can disrupt legitimate play; choose limits from real server observations, including menu plugins and rapid inventory management, before enabling cancellation.

An explicit `forgeguard.guard.bypass` permission exempts a player; the permission defaults to false and is not granted simply by being an operator. Bypassed checks are counted. This permission is separate from the movement-observation bypass.

## Visibility, memory and evidence

The health snapshot exposes the mode, enabled families, per-family thresholds and counters, checked events, violations, requested cancellations, bypasses, tracked players, capacity misses, aggregated reports, notification failures, skipped book scans and a maximum of 100 recent incidents. This data powers the in-game and web health views.

The first incident in a player/family cooldown is reported immediately. Further incidents are aggregated and delivered after the cooldown by the heartbeat, or flushed when the player leaves, settings reload or the plugin closes. Reports contain a UUID, bounded player name, family, reason code and counts. They never contain command arguments, passwords, book contents, inventory contents or raw packet data. The plugin's bounded audit writer handles persistence; guard reports are not deterministic movement replay cases.

Player state is capped by `maximum-tracked-players`. On capacity exhaustion, previously tracked players retain their budgets and new untracked events proceed without guard enforcement; `capacityMisses` and `capacity-limited` coverage make that gap visible. Player quit releases state. Reload resets budgets and pending player state, preserving lifetime counters and recent incidents. Counters reset when the service/plugin restarts. There is no packet-thread inventory access, recursive NBT traversal or blanket packet cancellation.

## Coverage still absent

| Area | Actual status / needed work |
| --- | --- |
| General duplication exploits | No transaction-level duplication detection. Rate cancellation can limit some flood attempts, but cannot establish item conservation or fix a vanilla/plugin dupe. |
| Packet parser crashes, oversized compressed payloads, arbitrary NBT/component depth | Not covered by Bukkit events that occur after parsing. Requires maintained server/protocol patches and separately audited transport limits. |
| X-ray and seed cracking | No ore obfuscation or seed-cracking prevention. Server chunk obfuscation is a separate system. |
| Kill aura, reach, autoclicker, aim assistance, scaffold, combat backtracking | Partial: the behaviour checks above cover server-side reach, hit angle, attack rate, multi-target, click regularity, criticals, aim snaps, knockback, fast break/place, block reach, nuker, tower and hidden-face/liquid scaffolding. There is no full rotation classifier, no backtrack model and no client-visible hitbox history. |
| All movement, vehicles, Elytra, liquids, translated or Bedrock clients | The packet movement model covers limited native 1.21.11 contexts. The server-side movement checks (flight, glide, step, phase, water walk, spider, fast climb, no-slow) apply to any Java client but skip vehicles, Elytra, swimming, lava and translated clients, and are not a physics simulation. |
| Login bots, distributed attacks and DDoS | These guards begin with authenticated in-game events. They are not a login firewall, proxy-level connection gate or network DDoS service. |
| Chat, sign text, plugin messaging, creative item payloads | No general content/protocol validation supplied by this addition. |
| WorldEdit, custom plugin exploits, permission mistakes and compromised staff | No universal repair of other plugins or server administration. Staff tools and permissions need their own access controls and audit. |
| Minecraft 1.7 through current | This implementation uses the modern Paper API. It is not a tested legacy server binary. Protocol translation alone does not provide native-version physics equivalence. |

## Verification and upstream context

Automated tests cover burst tolerance, sustained abuse, per-player and per-family independence, observe/cancel behavior, backwards clock handling, capacity exhaustion, cooldown aggregation, reload/quit cleanup, explicit bypass and revocation, Unicode and byte limits, rejection before page access, metadata-scan suppression during floods, invalid settings and incident-delivery failure. These tests establish the implemented mechanics; they are not a competitive cheat-client benchmark or proof of production compatibility with every menu/plugin combination.

API references: [Paper 1.21.11 BookMeta](https://jd.papermc.io/paper/1.21.11/org/bukkit/inventory/meta/BookMeta.html), [WritableBookMeta](https://jd.papermc.io/paper/1.21.11/org/bukkit/inventory/meta/WritableBookMeta.html), [PlayerEditBookEvent](https://jd.papermc.io/paper/1.21.11/org/bukkit/event/player/PlayerEditBookEvent.html). These APIs expose already-decoded metadata; rejecting an edit at this layer cannot undo work already performed by the server's packet decoder.
