Anti-Cheat Patterns
The v3 endpoint system is built around server authority. The client reports events, the server decides consequences. Here are the key patterns for preventing ch...
# Anti-Cheat Patterns
The v3 endpoint system is built around server authority. The client reports events, the server decides consequences. Here are the key patterns for preventing cheating.
## Secure Transactions: buy a car without trusting the client
The most common exploit is simple: a player tries to spoof money, price, or inventory changes from the client. Network Storage prevents this by combining:
- **endpoint-controlled collections** -- money and inventory cannot be written directly by published game clients
- **Automatic s&box/Steam auth verification** -- the library sends the auth context and sboxcool.com verifies the player server-side
- **Server-side Game Values** -- prices and stats come from the cloud value table, not from the client
- **Atomic endpoint writes** -- the purchase only writes after every condition passes
Game code stays small:
```csharp
var result = await NetworkStorage.CallEndpoint( "purchase-item", new
{
item_id = "sports_car"
} );
```
The endpoint does the authority work:
```yml
sourceVersion: "1"
kind: endpoint
name: Purchase Item
slug: purchase-item
method: POST
enabled: true
input:
type: object
properties:
item_id:
type: string
required:
- item_id
steps:
- id: player
type: read
collection: players
key: "{{steamId}}_default"
- id: item
type: lookup
source: values
table: shop_items
where:
field: id
op: "=="
value: "{{input.item_id}}"
- id: can_afford
type: condition
check:
field: player.gold
op: ">="
value: "{{item.price}}"
onFail:
status: 400
error: INSUFFICIENT_FUNDS
- id: purchase
type: write
collection: players
key: "{{steamId}}_default"
ops:
- op: inc
path: gold
value: "{{-item.price}}"
- op: push
path: inventory
value: "{{input.item_id}}"
response:
status: 200
body:
success: true
gold_remaining: "{{purchase.gold}}"
```
No separate Node.js, Python, or generic BaaS glue service is needed for this transaction. The Source-native library call, auth handshake, endpoint validation, and protected collection write are all part of the Network Storage flow.
## 1. Server Decides Rewards
The most important pattern. Never let the client decide how much XP, currency, or items to award.
**Client-side reward writes are vulnerable:**
```csharp
// Never let client code choose reward amounts
var ops = new { ops = new object[] {
new { op = "inc", path = "xp", value = 999999 }
}};
```
**Server-side endpoint rewards are secure:**
```csharp
// Client just reports the event
var result = await NetworkStorage.CallEndpoint( "report-kill", new
{
target = "training_dummy"
} );
// Server looked up xp_per_kill from Game Values, awarded 50 XP
```
The endpoint reads `xp_per_kill` from Game Values and awards that amount. The client has no say in the reward.
## 2. Validate with Conditions
Check requirements before writing. All conditions run before any writes, so either everything succeeds or nothing changes.
**XP requirement for mining:**
```yml
- id: compute_req
type: transform
expression: "{{node.xp_required}}"
- id: check_xp
type: condition
check:
field: player.mining_xp
op: ">="
value: "{{compute_req}}"
onFail:
status: 400
error: XP_TOO_LOW
message: Not enough mining XP for this node
```
A player with 5000 mining XP trying to mine a node requiring 25000 gets rejected.
**Check currency before purchase:**
```yml
id: check_funds
type: condition
check:
field: player.currency
op: '>='
value: "{{item.cost}}"
onFail:
status: 400
error: INSUFFICIENT_CURRENCY
```
## 3. Rate Limits as Safety Nets
Even with server-authoritative endpoints, add rate limit rules as a second layer:
| Rule | Target | Window | Action | Why |
|------|--------|--------|--------|-----|
| XP Cap | `xp` | 10,000/day | Clamp | Prevents botting from earning unlimited XP |
| Currency Cap | `currency` | 50,000/hour | Clamp | Caps earning rate even through legitimate play |
| Ore Limit | `ore.*` | 5/minute | Reject | Prevents speed-hack mining |
| Save Throttle | `*` | 360/hour | Reject | Prevents API spam |
**Clamp vs Reject:**
- **Clamp** for earned values (XP, currency). The player still gets a reward, just capped. Avoids frustrating legitimate players.
- **Reject** for action limits (mining speed, save rate). Hard-block abuse.
## 4. Flag Suspicious Activity
Workflows can flag players without rejecting their request:
```yml
onFail:
reject: false
flag: true
webhook: true
severity: warning
errorCode: SUSPICIOUS_VALUE
```
The action goes through, but:
- A flag is logged to `logs/flags/{steamId}/{date}.json`
- A Discord webhook is sent (if configured)
- You can review flagged players in Project Logs
Use `severity: critical` for high-confidence cheating indicators.
## 5. Endpoint-Only Access Mode
Set collections to **Endpoint controlled** in collection settings. This blocks the direct save API:
Endpoint-only collections reject direct storage writes with `403 ENDPOINT_ONLY`. Game code should call `NetworkStorage.CallEndpoint(...)`; the endpoint then performs validated writes server-side.
All writes must go through your validated endpoints. There is no way to bypass the validation.
## 6. Compound Validation
Check multiple things in a single condition using `all` (AND):
```yml
check:
all:
- field: input.amount
op: '>'
value: '0'
- field: input.amount
op: <=
value: "{{values.limits.max_per_action}}"
- field: player.xp
op: '>='
value: "{{required_xp}}"
- field: player
op: exists
value: ""
```
Or `any` (OR) for alternative conditions:
```yml
check:
any:
- field: player.rank
op: ==
value: admin
- field: player.rank
op: ==
value: moderator
```
## 7. Ledger for Audit Trail
Enable `_ledger: true` on currency and XP fields in your collection schema. Every change is permanently logged with:
- **Who** changed it (Steam ID)
- **When** (timestamp)
- **What** changed (field path, old value, new value)
- **Why** (source + reason from the operation)
If a player claims they were unfairly banned, you can review the full history of every currency and XP change they ever received.
## What the System Prevents
| Attack | How v3 Stops It |
|--------|----------------|
| Client sends fake XP/currency values | Server looks up amounts from Game Values, ignores client |
| Speed hacking (mining too fast) | Rate limit rules reject over-speed actions |
| Currency duplication | Ledger tracks every change, conditions check balances before deducting |
| Accessing locked content | Condition checks XP requirements before allowing action |
| API spam / DDoS | Endpoint rate limits (per-player and global) throttle requests |
| Client-side storage manipulation | Endpoint/query access routes gameplay writes through endpoints |
| Negative currency exploits | Schema `min: 0` validation prevents negative values |
| Duplicate perk unlocks | Condition checks if perk already in array before allowing |
## C# Example: Handling Rejections
```csharp
var response = await NetworkStorage.CallEndpoint( "mine-node", new
{
node_id = "gold_node"
} );
if ( !response.HasValue )
{
Log.Warning( "Mining action was rejected." );
return;
}
// Mining succeeded -- show the reward
var ore = response["ore_type"];
var amount = response["ore_amount"];
Log.Info( $"Mined {amount}x {ore}!" );
```